diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ForwardedRequestCustomizer.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ForwardedRequestCustomizer.java index 3e20cece188a..f43ddfecfaba 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/ForwardedRequestCustomizer.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ForwardedRequestCustomizer.java @@ -461,13 +461,17 @@ public void customize(Connector connector, HttpConfiguration config, Request req // Do a single pass through the header fields as it is a more efficient single iteration. Forwarded forwarded = new Forwarded(request, config); + boolean match = false; for (HttpField field : httpFields) { try { MethodHandle handle = _handles.get(field.getName()); if (handle != null) + { + match = true; handle.invoke(forwarded, field); + } } catch (Throwable t) { @@ -475,67 +479,64 @@ public void customize(Connector connector, HttpConfiguration config, Request req } } - String proto = "http"; - - // Is secure status configured from headers? - if (forwarded.isSecure()) + if (match) { - // set default to https - proto = config.getSecureScheme(); - } + // Is secure status configured from headers? + if (forwarded.isSecure()) + { + request.setSecure(true); + } - // Set Scheme from configured protocol - if (forwarded._proto != null) - { - proto = forwarded._proto; - request.setScheme(proto); - } + // Set Scheme from configured protocol + if (forwarded._proto != null) + { + request.setScheme(forwarded._proto); + } + // Set scheme if header implies secure scheme is to be used (see #isSslIsSecure()) + else if (forwarded._secureScheme) + { + request.setScheme(config.getSecureScheme()); + } - // Set authority - String host = null; - int port = -1; + // Use authority from headers, if configured. + if (forwarded._authority != null) + { + String host = forwarded._authority._host; + int port = forwarded._authority._port; - // Use authority from headers, if configured. - if (forwarded._authority != null) - { - host = forwarded._authority._host; - port = forwarded._authority._port; - } + HttpURI requestURI = request.getMetaData().getURI(); - // Fall back to request metadata if needed. - HttpURI requestURI = request.getMetaData().getURI(); - if (host == null) - { - host = requestURI.getHost(); - } - if (port == MutableHostPort.UNSET) // is unset by headers - { - port = requestURI.getPort(); - } - // Don't change port if port == IMPLIED. + if (requestURI != null) + { + // Fall back to request metadata if needed. + if (host == null) + { + host = requestURI.getHost(); + } - // Update authority if different from metadata - if (!host.equalsIgnoreCase(requestURI.getHost()) || - port != requestURI.getPort()) - { - httpFields.put(new HostPortHttpField(host, port)); - request.setAuthority(host, port); - } + if (port == MutableHostPort.UNSET) // is unset by headers + { + port = requestURI.getPort(); + } - // Set secure status - if (forwarded.isSecure() || - proto.equalsIgnoreCase(config.getSecureScheme()) || - port == getSecurePort(config)) - { - request.setSecure(true); - request.setScheme(proto); - } + // Don't change port if port == IMPLIED. - // Set Remote Address - if (forwarded.hasFor()) - { - int forPort = forwarded._for._port > 0 ? forwarded._for._port : request.getRemotePort(); - request.setRemoteAddr(InetSocketAddress.createUnresolved(forwarded._for._host, forPort)); + // Update authority if different from metadata + if (!host.equalsIgnoreCase(requestURI.getHost()) || + port != requestURI.getPort()) + { + httpFields.put(new HostPortHttpField(host, port)); + request.setAuthority(host, port); + } + } + } + + // Set Remote Address + if (forwarded.hasFor()) + { + int forPort = forwarded._for._port > 0 ? forwarded._for._port : request.getRemotePort(); + request.setRemoteAddr(InetSocketAddress.createUnresolved(forwarded._for._host, forPort)); + } } } @@ -744,6 +745,7 @@ private class Forwarded extends QuotedCSVParser String _proto; Source _protoSource = Source.UNSET; Boolean _secure; + boolean _secureScheme = false; public Forwarded(Request request, HttpConfiguration config) { @@ -787,40 +789,58 @@ private MutableHostPort getFor() return _for; } - @SuppressWarnings("unused") + /** + * Called if header is Proxy-auth-cert + */ public void handleCipherSuite(HttpField field) { _request.setAttribute("javax.servlet.request.cipher_suite", field.getValue()); + + // Is ForwardingRequestCustomizer configured to trigger isSecure and scheme change on this header? if (isSslIsSecure()) { _secure = true; + // track desire for secure scheme, actual protocol will be resolved later. + _secureScheme = true; } } - @SuppressWarnings("unused") + /** + * Called if header is Proxy-Ssl-Id + */ public void handleSslSessionId(HttpField field) { _request.setAttribute("javax.servlet.request.ssl_session_id", field.getValue()); + + // Is ForwardingRequestCustomizer configured to trigger isSecure and scheme change on this header? if (isSslIsSecure()) { _secure = true; + // track desire for secure scheme, actual protocol will be resolved later. + _secureScheme = true; } } - @SuppressWarnings("unused") + /** + * Called if header is X-Forwarded-Host + */ public void handleForwardedHost(HttpField field) { updateAuthority(getLeftMost(field.getValue()), Source.XFORWARDED_HOST); } - @SuppressWarnings("unused") + /** + * Called if header is X-Forwarded-For + */ public void handleForwardedFor(HttpField field) { HostPort hostField = new HostPort(getLeftMost(field.getValue())); getFor().setHostPort(hostField, Source.XFORWARDED_FOR); } - @SuppressWarnings("unused") + /** + * Called if header is X-Forwarded-Server + */ public void handleForwardedServer(HttpField field) { if (getProxyAsAuthority()) @@ -828,7 +848,9 @@ public void handleForwardedServer(HttpField field) updateAuthority(getLeftMost(field.getValue()), Source.XFORWARDED_SERVER); } - @SuppressWarnings("unused") + /** + * Called if header is X-Forwarded-Port + */ public void handleForwardedPort(HttpField field) { int port = HostPort.parsePort(getLeftMost(field.getValue())); @@ -836,13 +858,17 @@ public void handleForwardedPort(HttpField field) updatePort(port, Source.XFORWARDED_PORT); } - @SuppressWarnings("unused") + /** + * Called if header is X-Forwarded-Proto + */ public void handleProto(HttpField field) { updateProto(getLeftMost(field.getValue()), Source.XFORWARDED_PROTO); } - @SuppressWarnings("unused") + /** + * Called if header is X-Proxied-Https + */ public void handleHttps(HttpField field) { if ("on".equalsIgnoreCase(field.getValue()) || "true".equalsIgnoreCase(field.getValue())) @@ -851,9 +877,21 @@ public void handleHttps(HttpField field) updateProto(HttpScheme.HTTPS.asString(), Source.XPROXIED_HTTPS); updatePort(getSecurePort(_config), Source.XPROXIED_HTTPS); } + else if ("off".equalsIgnoreCase(field.getValue()) || "false".equalsIgnoreCase(field.getValue())) + { + _secure = false; + updateProto(HttpScheme.HTTP.asString(), Source.XPROXIED_HTTPS); + updatePort(MutableHostPort.IMPLIED, Source.XPROXIED_HTTPS); + } + else + { + throw new BadMessageException("Invalid value for " + field.getName()); + } } - @SuppressWarnings("unused") + /** + * Called if header is Forwarded + */ public void handleRFC7239(HttpField field) { addValue(field.getValue()); diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ForwardedRequestCustomizerTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ForwardedRequestCustomizerTest.java index d425e894c16f..b5e90fad16ec 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/ForwardedRequestCustomizerTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ForwardedRequestCustomizerTest.java @@ -32,7 +32,6 @@ import org.eclipse.jetty.server.handler.AbstractHandler; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -135,6 +134,51 @@ public void destroy() throws Exception public static Stream cases() { return Stream.of( + // HTTP 1.0 + Arguments.of( + new Request("HTTP/1.0 - no Host header") + .headers( + "GET /example HTTP/1.0" + ), + new Expectations() + .scheme("http").serverName("0.0.0.0").serverPort(80) + .secure(false) + .requestURL("http://0.0.0.0/example") + ), + Arguments.of( + new Request("HTTP/1.0 - Empty Host header") + .headers( + "GET /example HTTP/1.0", + "Host:" + ), + new Expectations() + .scheme("http").serverName("0.0.0.0").serverPort(80) + .secure(false) + .requestURL("http://0.0.0.0/example") + ), + Arguments.of( + new Request("HTTP/1.0 - No Host header, with X-Forwarded-Host") + .headers( + "GET /example HTTP/1.0", + "X-Forwarded-Host: alt.example.net:7070" + ), + new Expectations() + .scheme("http").serverName("alt.example.net").serverPort(7070) + .secure(false) + .requestURL("http://alt.example.net:7070/example") + ), + Arguments.of( + new Request("HTTP/1.0 - Empty Host header, with X-Forwarded-Host") + .headers( + "GET /example HTTP/1.0", + "Host:", + "X-Forwarded-Host: alt.example.net:7070" + ), + new Expectations() + .scheme("http").serverName("alt.example.net").serverPort(7070) + .secure(false) + .requestURL("http://alt.example.net:7070/example") + ), // Host IPv4 Arguments.of( new Request("IPv4 Host Only") @@ -144,6 +188,7 @@ public static Stream cases() ), new Expectations() .scheme("http").serverName("1.2.3.4").serverPort(2222) + .secure(false) .requestURL("http://1.2.3.4:2222/") ), Arguments.of(new Request("IPv6 Host Only") @@ -153,16 +198,18 @@ public static Stream cases() ), new Expectations() .scheme("http").serverName("[::1]").serverPort(2222) + .secure(false) .requestURL("http://[::1]:2222/") ), Arguments.of(new Request("IPv4 in Request Line") .headers( - "GET http://1.2.3.4:2222/ HTTP/1.1", + "GET https://1.2.3.4:2222/ HTTP/1.1", "Host: wrong" ), new Expectations() - .scheme("http").serverName("1.2.3.4").serverPort(2222) - .requestURL("http://1.2.3.4:2222/") + .scheme("https").serverName("1.2.3.4").serverPort(2222) + .secure(true) + .requestURL("https://1.2.3.4:2222/") ), Arguments.of(new Request("IPv6 in Request Line") .headers( @@ -171,6 +218,7 @@ public static Stream cases() ), new Expectations() .scheme("http").serverName("[::1]").serverPort(2222) + .secure(false) .requestURL("http://[::1]:2222/") ), @@ -187,6 +235,7 @@ public static Stream cases() ), new Expectations() .scheme("http").serverName("myhost").serverPort(80) + .secure(false) .requestURL("http://myhost/") .remoteAddr("[2001:db8:cafe::17]").remotePort(4711) ), @@ -200,6 +249,7 @@ public static Stream cases() ), new Expectations() .scheme("http").serverName("myhost").serverPort(80) + .secure(false) .requestURL("http://myhost/") .remoteAddr("192.0.2.43").remotePort(0) ), @@ -213,6 +263,7 @@ public static Stream cases() ), new Expectations() .scheme("http").serverName("myhost").serverPort(80) + .secure(false) .requestURL("http://myhost/") .remoteAddr("192.0.2.43").remotePort(0) ), @@ -227,6 +278,7 @@ public static Stream cases() ), new Expectations() .scheme("http").serverName("myhost").serverPort(80) + .secure(false) .requestURL("http://myhost/") .remoteAddr("192.0.2.43").remotePort(0) ), @@ -240,6 +292,7 @@ public static Stream cases() ), new Expectations() .scheme("http").serverName("myhost").serverPort(80) + .secure(false) .requestURL("http://myhost/") .remoteAddr("192.0.2.43").remotePort(0) ), @@ -251,6 +304,7 @@ public static Stream cases() ), new Expectations() .scheme("http").serverName("myhost").serverPort(80) + .secure(false) .requestURL("http://myhost/") .remoteAddr("192.0.2.43").remotePort(0) ), @@ -264,6 +318,7 @@ public static Stream cases() ), new Expectations() .scheme("http").serverName("example.com").serverPort(80) + .secure(false) .requestURL("http://example.com/") .remoteAddr("192.0.2.43").remotePort(0) ), @@ -277,6 +332,7 @@ public static Stream cases() ), new Expectations() .scheme("https").serverName("myhost").serverPort(443) + .secure(true) .requestURL("https://myhost/") ), @@ -291,6 +347,7 @@ public static Stream cases() ), new Expectations() .scheme("http").serverName("example.com").serverPort(80) + .secure(false) .remoteAddr("10.20.30.40") .requestURL("http://example.com/") ), @@ -305,6 +362,7 @@ public static Stream cases() ), new Expectations() .scheme("https").serverName("example.com").serverPort(81) + .secure(true) .remoteAddr("10.20.30.40") .requestURL("https://example.com:81/") ), @@ -317,6 +375,7 @@ public static Stream cases() ), new Expectations() .scheme("https").serverName("example.com").serverPort(443) + .secure(true) .requestURL("https://example.com/") ), Arguments.of(new Request("ProxyPass (IPv6 from [::1]:80 to localhost:8080)") @@ -328,6 +387,7 @@ public static Stream cases() ), new Expectations() .scheme("http").serverName("[::1]").serverPort(80) + .secure(false) .remoteAddr("10.20.30.40") .requestURL("http://[::1]/") ), @@ -340,6 +400,7 @@ public static Stream cases() ), new Expectations() .scheme("http").serverName("[::1]").serverPort(8888) + .secure(false) .remoteAddr("10.20.30.40") .requestURL("http://[::1]:8888/") ), @@ -354,6 +415,7 @@ public static Stream cases() ), new Expectations() .scheme("https").serverName("example.com").serverPort(443) + .secure(true) .remoteAddr("10.20.30.40") .requestURL("https://example.com/") ), @@ -367,6 +429,7 @@ public static Stream cases() ), new Expectations() .scheme("https").serverName("myhost").serverPort(443) + .secure(true) .requestURL("https://myhost/") ), Arguments.of(new Request("X-Forwarded-For (multiple headers)") @@ -378,6 +441,7 @@ public static Stream cases() ), new Expectations() .scheme("http").serverName("myhost").serverPort(80) + .secure(false) .requestURL("http://myhost/") .remoteAddr("10.9.8.7").remotePort(0) ), @@ -389,6 +453,7 @@ public static Stream cases() ), new Expectations() .scheme("http").serverName("myhost").serverPort(80) + .secure(false) .requestURL("http://myhost/") .remoteAddr("10.9.8.7").remotePort(1111) ), @@ -400,6 +465,7 @@ public static Stream cases() ), new Expectations() .scheme("http").serverName("myhost").serverPort(80) + .secure(false) .requestURL("http://myhost/") .remoteAddr("[2001:db8:cafe::17]").remotePort(1111) ), @@ -412,6 +478,7 @@ public static Stream cases() ), new Expectations() .scheme("http").serverName("myhost").serverPort(2222) + .secure(false) .requestURL("http://myhost:2222/") .remoteAddr("[1:2:3:4:5:6:7:8]").remotePort(0) ), @@ -426,6 +493,7 @@ public static Stream cases() ), new Expectations() .scheme("http").serverName("myhost").serverPort(2222) + .secure(false) .requestURL("http://myhost:2222/") .remoteAddr("[1:2:3:4:5:6:7:8]").remotePort(0) ), @@ -438,6 +506,7 @@ public static Stream cases() ), new Expectations() .scheme("http").serverName("myhost").serverPort(2222) + .secure(false) .requestURL("http://myhost:2222/") .remoteAddr("[1:2:3:4:5:6:7:8]").remotePort(0) ), @@ -450,6 +519,7 @@ public static Stream cases() ), new Expectations() .scheme("http").serverName("myhost").serverPort(4444) + .secure(false) .requestURL("http://myhost:4444/") .remoteAddr("192.168.1.200").remotePort(0) ), @@ -463,6 +533,7 @@ public static Stream cases() ), new Expectations() .scheme("http").serverName("myhost").serverPort(80) + .secure(false) .requestURL("http://myhost/") .remoteAddr("192.168.1.200").remotePort(4444) ), @@ -475,6 +546,7 @@ public static Stream cases() ), new Expectations() .scheme("http").serverName("myhost").serverPort(4444) + .secure(false) .requestURL("http://myhost:4444/") .remoteAddr("192.168.1.200").remotePort(0) ), @@ -489,6 +561,7 @@ public static Stream cases() ), new Expectations() .scheme("https").serverName("www.example.com").serverPort(4333) + .secure(true) .requestURL("https://www.example.com:4333/") .remoteAddr("8.5.4.3").remotePort(2222) ), @@ -503,6 +576,7 @@ public static Stream cases() ), new Expectations() .scheme("https").serverName("www.example.com").serverPort(4333) + .secure(true) .requestURL("https://www.example.com:4333/") .remoteAddr("8.5.4.3").remotePort(2222) ), @@ -518,6 +592,7 @@ public static Stream cases() ), new Expectations() .scheme("https").serverName("www.example.com").serverPort(4333) + .secure(true) .requestURL("https://www.example.com:4333/") .remoteAddr("8.5.4.3").remotePort(2222) ), @@ -533,6 +608,7 @@ public static Stream cases() ), new Expectations() .scheme("https").serverName("www.example.com").serverPort(4333) + .secure(true) .requestURL("https://www.example.com:4333/") .remoteAddr("8.5.4.3").remotePort(2222) ), @@ -548,6 +624,7 @@ public static Stream cases() ), new Expectations() .scheme("https").serverName("www.example.com").serverPort(4333) + .secure(true) .requestURL("https://www.example.com:4333/") .remoteAddr("8.5.4.3").remotePort(2222) ), @@ -561,6 +638,7 @@ public static Stream cases() ), new Expectations() .scheme("http").serverName("fw.example.com").serverPort(4333) + .secure(false) .requestURL("http://fw.example.com:4333/") .remoteAddr("8.5.4.3").remotePort(2222) ), @@ -574,6 +652,7 @@ public static Stream cases() ), new Expectations() .scheme("http").serverName("fw.example.com").serverPort(4333) + .secure(false) .requestURL("http://fw.example.com:4333/") .remoteAddr("8.5.4.3").remotePort(2222) ), @@ -589,6 +668,7 @@ public static Stream cases() ), new Expectations() .scheme("https").serverName("sub1.example.com").serverPort(10003) + .secure(true) .requestURL("https://sub1.example.com:10003/") .remoteAddr("127.0.0.1").remotePort(8888) ), @@ -604,6 +684,7 @@ public static Stream cases() ), new Expectations() .scheme("https").serverName("sub1.example.com").serverPort(10003) + .secure(true) .requestURL("https://sub1.example.com:10003/") .remoteAddr("127.0.0.1").remotePort(8888) ), @@ -620,6 +701,7 @@ public static Stream cases() ), new Expectations() .scheme("https").serverName("sub1.example.com").serverPort(10003) + .secure(true) .requestURL("https://sub1.example.com:10003/") .remoteAddr("127.0.0.1").remotePort(8888) ), @@ -635,6 +717,7 @@ public static Stream cases() ), new Expectations() .scheme("https").serverName("sub1.example.com").serverPort(10003) + .secure(true) .requestURL("https://sub1.example.com:10003/") .remoteAddr("127.0.0.1").remotePort(8888) ), @@ -651,9 +734,35 @@ public static Stream cases() ), new Expectations() .scheme("http").serverName("example.com").serverPort(80) + .secure(false) .requestURL("http://example.com/") .remoteAddr("192.0.2.43").remotePort(0) ), + Arguments.of( + new Request("RFC7239 - mixed with HTTP/1.0 - No Host header") + .headers( + "GET /example HTTP/1.0", + "Forwarded: for=1.1.1.1:6060,proto=http;host=alt.example.net:7070" + ), + new Expectations() + .scheme("http").serverName("alt.example.net").serverPort(7070) + .secure(false) + .requestURL("http://alt.example.net:7070/example") + .remoteAddr("1.1.1.1").remotePort(6060) + ), + Arguments.of( + new Request("RFC7239 - mixed with HTTP/1.0 - Empty Host header") + .headers( + "GET /example HTTP/1.0", + "Host:", + "Forwarded: for=1.1.1.1:6060,proto=http;host=alt.example.net:7070" + ), + new Expectations() + .scheme("http").serverName("alt.example.net").serverPort(7070) + .secure(false) + .requestURL("http://alt.example.net:7070/example") + .remoteAddr("1.1.1.1").remotePort(6060) + ), // ================================================================= // Forced Behavior Arguments.of(new Request("Forced Host (no port)") @@ -666,6 +775,7 @@ public static Stream cases() ), new Expectations() .scheme("http").serverName("always.example.com").serverPort(80) + .secure(false) .requestURL("http://always.example.com/") .remoteAddr("11.9.8.7").remotePort(1111) ), @@ -679,6 +789,7 @@ public static Stream cases() ), new Expectations() .scheme("http").serverName("always.example.com").serverPort(9090) + .secure(false) .requestURL("http://always.example.com:9090/") .remoteAddr("11.9.8.7").remotePort(1111) ), @@ -692,6 +803,7 @@ public static Stream cases() ), new Expectations() .scheme("https").serverName("myhost").serverPort(443) + .secure(true) .requestURL("https://myhost/") .remoteAddr("0.0.0.0").remotePort(0) ), @@ -704,6 +816,7 @@ public static Stream cases() ), new Expectations() .scheme("http").serverName("myhost").serverPort(80) + .secure(false) .requestURL("http://myhost/") .remoteAddr("0.0.0.0").remotePort(0) .sslSession("Wibble") @@ -717,6 +830,7 @@ public static Stream cases() ), new Expectations() .scheme("https").serverName("myhost").serverPort(443) + .secure(true) .requestURL("https://myhost/") .remoteAddr("0.0.0.0").remotePort(0) .sslSession("0123456789abcdef") @@ -730,6 +844,7 @@ public static Stream cases() ), new Expectations() .scheme("http").serverName("myhost").serverPort(80) + .secure(false) .requestURL("http://myhost/") .remoteAddr("0.0.0.0").remotePort(0) .sslCertificate("Wibble") @@ -743,9 +858,121 @@ public static Stream cases() ), new Expectations() .scheme("https").serverName("myhost").serverPort(443) + .secure(true) .requestURL("https://myhost/") .remoteAddr("0.0.0.0").remotePort(0) .sslCertificate("0123456789abcdef") + ), + // ================================================================= + // Complicated scenarios + Arguments.of(new Request("No initial authority, X-Forwarded-Proto on http, Proxy-Ssl-Id exists (setSslIsSecure==true)") + .configureCustomizer((customizer) -> customizer.setSslIsSecure(true)) + .headers( + "GET /foo HTTP/1.1", + "Host: myhost", + "X-Forwarded-Proto: http", + "Proxy-Ssl-Id: Wibble" + ), + new Expectations() + .scheme("http").serverName("myhost").serverPort(80) + .secure(true) + .requestURL("http://myhost/foo") + .remoteAddr("0.0.0.0").remotePort(0) + .sslSession("Wibble") + ), + Arguments.of(new Request("https initial authority, X-Forwarded-Proto on http, Proxy-Ssl-Id exists (setSslIsSecure==false)") + .configureCustomizer((customizer) -> customizer.setSslIsSecure(false)) + .headers( + "GET https://alt.example.net/foo HTTP/1.1", + "Host: myhost", + "X-Forwarded-Proto: http", + "Proxy-Ssl-Id: Wibble" + ), + new Expectations() + .scheme("http").serverName("alt.example.net").serverPort(80) + .secure(false) + .requestURL("http://alt.example.net/foo") + .remoteAddr("0.0.0.0").remotePort(0) + .sslSession("Wibble") + ), + Arguments.of(new Request("No initial authority, X-Proxied-Https off, Proxy-Ssl-Id exists (setSslIsSecure==true)") + .configureCustomizer((customizer) -> customizer.setSslIsSecure(true)) + .headers( + "GET /foo HTTP/1.1", + "Host: myhost", + "X-Proxied-Https: off", // this wins for scheme and secure + "Proxy-Ssl-Id: Wibble" + ), + new Expectations() + .scheme("http").serverName("myhost").serverPort(80) + .secure(false) + .requestURL("http://myhost/foo") + .remoteAddr("0.0.0.0").remotePort(0) + .sslSession("Wibble") + ), + Arguments.of(new Request("Https initial authority, X-Proxied-Https off, Proxy-Ssl-Id exists (setSslIsSecure==true)") + .configureCustomizer((customizer) -> customizer.setSslIsSecure(true)) + .headers( + "GET https://alt.example.net/foo HTTP/1.1", + "Host: myhost", + "X-Proxied-Https: off", // this wins for scheme and secure + "Proxy-Ssl-Id: Wibble" + ), + new Expectations() + .scheme("http").serverName("alt.example.net").serverPort(80) + .secure(false) + .requestURL("http://alt.example.net/foo") + .remoteAddr("0.0.0.0").remotePort(0) + .sslSession("Wibble") + ), + Arguments.of(new Request("Https initial authority, X-Proxied-Https off, Proxy-Ssl-Id exists (setSslIsSecure==true) (alt order)") + .configureCustomizer((customizer) -> customizer.setSslIsSecure(true)) + .headers( + "GET https://alt.example.net/foo HTTP/1.1", + "Host: myhost", + "Proxy-Ssl-Id: Wibble", + "X-Proxied-Https: off" // this wins for scheme and secure + ), + new Expectations() + .scheme("http").serverName("alt.example.net").serverPort(80) + .secure(false) + .requestURL("http://alt.example.net/foo") + .remoteAddr("0.0.0.0").remotePort(0) + .sslSession("Wibble") + ), + Arguments.of(new Request("Http initial authority, X-Proxied-Https off, Proxy-Ssl-Id exists (setSslIsSecure==false)") + .configureCustomizer((customizer) -> customizer.setSslIsSecure(false)) + .headers( + "GET https://alt.example.net/foo HTTP/1.1", + "Host: myhost", + "X-Proxied-Https: off", + "Proxy-Ssl-Id: Wibble", + "Proxy-auth-cert: 0123456789abcdef" + ), + new Expectations() + .scheme("http").serverName("alt.example.net").serverPort(80) + .secure(false) + .requestURL("http://alt.example.net/foo") + .remoteAddr("0.0.0.0").remotePort(0) + .sslSession("Wibble") + .sslCertificate("0123456789abcdef") + ), + Arguments.of(new Request("Http initial authority, X-Proxied-Https off, Proxy-Ssl-Id exists (setSslIsSecure==false) (alt)") + .configureCustomizer((customizer) -> customizer.setSslIsSecure(false)) + .headers( + "GET https://alt.example.net/foo HTTP/1.1", + "Host: myhost", + "Proxy-Ssl-Id: Wibble", + "Proxy-auth-cert: 0123456789abcdef", + "X-Proxied-Https: off" + ), + new Expectations() + .scheme("http").serverName("alt.example.net").serverPort(80) + .secure(false) + .requestURL("http://alt.example.net/foo") + .remoteAddr("0.0.0.0").remotePort(0) + .sslSession("Wibble") + .sslCertificate("0123456789abcdef") ) ); } @@ -848,20 +1075,31 @@ public void testNonStandardPortBehavior(Request request, Expectations expectatio expectations.accept(actual); } - @Test - public void testBadInput() throws Exception + public static Stream badRequestCases() { - Request request = new Request("Bad port value") - .headers( - "GET / HTTP/1.1", - "Host: myhost", - "X-Forwarded-Port: " - ); + return Stream.of( + new Request("Bad port value") + .headers( + "GET / HTTP/1.1", + "Host: myhost", + "X-Forwarded-Port: " + ), + new Request("Invalid X-Proxied-Https value") + .headers( + "GET / HTTP/1.1", + "Host: myhost", + "X-Proxied-Https: foo" + ) + ); + } + @ParameterizedTest + @MethodSource("badRequestCases") + public void testBadInput(Request request) throws Exception + { request.configure(customizer); String rawRequest = request.getRawRequest((header) -> header); - // System.out.println(rawRequest); HttpTester.Response response = HttpTester.parseResponse(connector.getResponse(rawRequest)); assertThat("status", response.getStatus(), is(400)); @@ -926,12 +1164,13 @@ private static class Expectations implements Consumer int expectedRemotePort = 0; String expectedSslSession; String expectedSslCertificate; + Boolean secure; @Override public void accept(Actual actual) { assertThat("scheme", actual.scheme.get(), is(expectedScheme)); - if (actual.scheme.get().equals("https")) + if (secure != null && secure) { assertTrue(actual.wasSecure.get(), "wasSecure"); } @@ -953,6 +1192,12 @@ public void accept(Actual actual) } } + public Expectations secure(boolean flag) + { + this.secure = flag; + return this; + } + public Expectations scheme(String scheme) { this.expectedScheme = scheme;