From cec50b3d2cea7bdf6b2f00d45c8554139aebbf87 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Wed, 8 May 2019 21:07:22 +0200 Subject: [PATCH] Issue #3630 Optimized ForwardedRequestCustomizer Signed-off-by: Greg Wilkins --- .../eclipse/jetty/http/HostPortHttpField.java | 17 +- .../org/eclipse/jetty/http/QuotedCSV.java | 264 +--------------- .../eclipse/jetty/http/QuotedCSVParser.java | 288 ++++++++++++++++++ .../server/ForwardedRequestCustomizer.java | 249 +++++++-------- .../ForwardedRequestCustomizerTest.java | 99 +++++- 5 files changed, 507 insertions(+), 410 deletions(-) create mode 100644 jetty-http/src/main/java/org/eclipse/jetty/http/QuotedCSVParser.java diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HostPortHttpField.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HostPortHttpField.java index dc386665339..6c9f3b921e8 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HostPortHttpField.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HostPortHttpField.java @@ -22,9 +22,9 @@ import org.eclipse.jetty.util.HostPort; - -/* ------------------------------------------------------------ */ /** + * A HttpField holding a preparsed Host and port number + * @see HostPort */ public class HostPortHttpField extends HttpField { @@ -35,7 +35,6 @@ public HostPortHttpField(String authority) this(HttpHeader.HOST,HttpHeader.HOST.asString(),authority); } - /* ------------------------------------------------------------ */ protected HostPortHttpField(HttpHeader header, String name, String authority) { super(header,name,authority); @@ -49,20 +48,17 @@ protected HostPortHttpField(HttpHeader header, String name, String authority) } } - /* ------------------------------------------------------------ */ public HostPortHttpField(String host, int port) { this(new HostPort(host, port)); } - /* ------------------------------------------------------------ */ - protected HostPortHttpField(HostPort hostport) + public HostPortHttpField(HostPort hostport) { super(HttpHeader.HOST,HttpHeader.HOST.asString(),hostport.toString()); _hostPort = hostport; } - /* ------------------------------------------------------------ */ /** Get the host. * @return the host */ @@ -71,7 +67,6 @@ public String getHost() return _hostPort.getHost(); } - /* ------------------------------------------------------------ */ /** Get the port. * @return the port */ @@ -80,7 +75,6 @@ public int getPort() return _hostPort.getPort(); } - /* ------------------------------------------------------------ */ /** Get the port. * @param defaultPort The default port to return if no port set * @return the port @@ -89,4 +83,9 @@ public int getPort(int defaultPort) { return _hostPort.getPort(defaultPort); } + + public HostPort getHostPort() + { + return _hostPort; + } } diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/QuotedCSV.java b/jetty-http/src/main/java/org/eclipse/jetty/http/QuotedCSV.java index 9ca7dbeec2c..8ebc2c36307 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/QuotedCSV.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/QuotedCSV.java @@ -22,8 +22,6 @@ import java.util.Iterator; import java.util.List; -import org.eclipse.jetty.util.QuotedStringTokenizer; - /* ------------------------------------------------------------ */ /** * Implements a quoted comma separated list of values @@ -32,226 +30,26 @@ * @see "https://tools.ietf.org/html/rfc7230#section-3.2.6" * @see "https://tools.ietf.org/html/rfc7230#section-7" */ -public class QuotedCSV implements Iterable -{ - private enum State { VALUE, PARAM_NAME, PARAM_VALUE}; - +public class QuotedCSV extends QuotedCSVParser implements Iterable +{ protected final List _values = new ArrayList<>(); - protected final boolean _keepQuotes; - - /* ------------------------------------------------------------ */ + public QuotedCSV(String... values) { this(true,values); } - /* ------------------------------------------------------------ */ public QuotedCSV(boolean keepQuotes,String... values) { - _keepQuotes=keepQuotes; + super(keepQuotes); for (String v:values) addValue(v); } - - /* ------------------------------------------------------------ */ - /** Add and parse a value string(s) - * @param value A value that may contain one or more Quoted CSV items. - */ - public void addValue(String value) - { - if (value == null) - return; - - StringBuffer buffer = new StringBuffer(); - - int l=value.length(); - State state=State.VALUE; - boolean quoted=false; - boolean sloshed=false; - int nws_length=0; - int last_length=0; - int value_length=-1; - int param_name=-1; - int param_value=-1; - - for (int i=0;i<=l;i++) - { - char c=i==l?0:value.charAt(i); - - // Handle quoting https://tools.ietf.org/html/rfc7230#section-3.2.6 - if (quoted && c!=0) - { - if (sloshed) - sloshed=false; - else - { - switch(c) - { - case '\\': - sloshed=true; - if (!_keepQuotes) - continue; - break; - case '"': - quoted=false; - if (!_keepQuotes) - continue; - break; - } - } - - buffer.append(c); - nws_length=buffer.length(); - continue; - } - - // Handle common cases - switch(c) - { - case ' ': - case '\t': - if (buffer.length()>last_length) // not leading OWS - buffer.append(c); - continue; - - case '"': - quoted=true; - if (_keepQuotes) - { - if (state==State.PARAM_VALUE && param_value<0) - param_value=nws_length; - buffer.append(c); - } - else if (state==State.PARAM_VALUE && param_value<0) - param_value=nws_length; - nws_length=buffer.length(); - continue; - - case ';': - buffer.setLength(nws_length); // trim following OWS - if (state==State.VALUE) - { - parsedValue(buffer); - value_length=buffer.length(); - } - else - parsedParam(buffer,value_length,param_name,param_value); - nws_length=buffer.length(); - param_name=param_value=-1; - buffer.append(c); - last_length=++nws_length; - state=State.PARAM_NAME; - continue; - - case ',': - case 0: - if (nws_length>0) - { - buffer.setLength(nws_length); // trim following OWS - switch(state) - { - case VALUE: - parsedValue(buffer); - value_length=buffer.length(); - break; - case PARAM_NAME: - case PARAM_VALUE: - parsedParam(buffer,value_length,param_name,param_value); - break; - } - _values.add(buffer.toString()); - } - buffer.setLength(0); - last_length=0; - nws_length=0; - value_length=param_name=param_value=-1; - state=State.VALUE; - continue; - - case '=': - switch (state) - { - case VALUE: - // It wasn't really a value, it was a param name - value_length=param_name=0; - buffer.setLength(nws_length); // trim following OWS - String param = buffer.toString(); - buffer.setLength(0); - parsedValue(buffer); - value_length=buffer.length(); - buffer.append(param); - buffer.append(c); - last_length=++nws_length; - state=State.PARAM_VALUE; - continue; - - case PARAM_NAME: - buffer.setLength(nws_length); // trim following OWS - buffer.append(c); - last_length=++nws_length; - state=State.PARAM_VALUE; - continue; - - case PARAM_VALUE: - if (param_value<0) - param_value=nws_length; - buffer.append(c); - nws_length=buffer.length(); - continue; - } - continue; - - default: - { - switch (state) - { - case VALUE: - { - buffer.append(c); - nws_length=buffer.length(); - continue; - } - - case PARAM_NAME: - { - if (param_name<0) - param_name=nws_length; - buffer.append(c); - nws_length=buffer.length(); - continue; - } - - case PARAM_VALUE: - { - if (param_value<0) - param_value=nws_length; - buffer.append(c); - nws_length=buffer.length(); - continue; - } - } - } - } - } - } - - /** - * Called when a value has been parsed - * @param buffer Containing the trimmed value, which may be mutated - */ - protected void parsedValue(StringBuffer buffer) - { - } - /** - * Called when a parameter has been parsed - * @param buffer Containing the trimmed value and all parameters, which may be mutated - * @param valueLength The length of the value - * @param paramName The index of the start of the parameter just parsed - * @param paramValue The index of the start of the parameter value just parsed, or -1 - */ - protected void parsedParam(StringBuffer buffer, int valueLength, int paramName, int paramValue) + @Override + protected void parsedValueAndParams(StringBuffer buffer) { + _values.add(buffer.toString()); } public int size() @@ -274,55 +72,7 @@ public boolean isEmpty() { return _values.iterator(); } - - public static String unquote(String s) - { - // handle trivial cases - int l=s.length(); - if (s==null || l==0) - return s; - - // Look for any quotes - int i=0; - for (;ilast_length) // not leading OWS + buffer.append(c); + continue; + + case '"': + quoted=true; + if (_keepQuotes) + { + if (state==State.PARAM_VALUE && param_value<0) + param_value=nws_length; + buffer.append(c); + } + else if (state==State.PARAM_VALUE && param_value<0) + param_value=nws_length; + nws_length=buffer.length(); + continue; + + case ';': + buffer.setLength(nws_length); // trim following OWS + if (state==State.VALUE) + { + parsedValue(buffer); + value_length=buffer.length(); + } + else + parsedParam(buffer,value_length,param_name,param_value); + nws_length=buffer.length(); + param_name=param_value=-1; + buffer.append(c); + last_length=++nws_length; + state=State.PARAM_NAME; + continue; + + case ',': + case 0: + if (nws_length>0) + { + buffer.setLength(nws_length); // trim following OWS + switch(state) + { + case VALUE: + parsedValue(buffer); + value_length=buffer.length(); + break; + case PARAM_NAME: + case PARAM_VALUE: + parsedParam(buffer,value_length,param_name,param_value); + break; + } + parsedValueAndParams(buffer); + } + buffer.setLength(0); + last_length=0; + nws_length=0; + value_length=param_name=param_value=-1; + state=State.VALUE; + continue; + + case '=': + switch (state) + { + case VALUE: + // It wasn't really a value, it was a param name + value_length=param_name=0; + buffer.setLength(nws_length); // trim following OWS + String param = buffer.toString(); + buffer.setLength(0); + parsedValue(buffer); + value_length=buffer.length(); + buffer.append(param); + buffer.append(c); + last_length=++nws_length; + state=State.PARAM_VALUE; + continue; + + case PARAM_NAME: + buffer.setLength(nws_length); // trim following OWS + buffer.append(c); + last_length=++nws_length; + state=State.PARAM_VALUE; + continue; + + case PARAM_VALUE: + if (param_value<0) + param_value=nws_length; + buffer.append(c); + nws_length=buffer.length(); + continue; + } + continue; + + default: + { + switch (state) + { + case VALUE: + { + buffer.append(c); + nws_length=buffer.length(); + continue; + } + + case PARAM_NAME: + { + if (param_name<0) + param_name=nws_length; + buffer.append(c); + nws_length=buffer.length(); + continue; + } + + case PARAM_VALUE: + { + if (param_value<0) + param_value=nws_length; + buffer.append(c); + nws_length=buffer.length(); + continue; + } + } + } + } + } + } + + /** + * Called when a value and it's parameters has been parsed + * @param buffer Containing the trimmed value and parameters + */ + protected void parsedValueAndParams(StringBuffer buffer) + { + } + + /** + * Called when a value has been parsed (prior to any parameters) + * @param buffer Containing the trimmed value, which may be mutated + */ + protected void parsedValue(StringBuffer buffer) + { + } + + /** + * Called when a parameter has been parsed + * @param buffer Containing the trimmed value and all parameters, which may be mutated + * @param valueLength The length of the value + * @param paramName The index of the start of the parameter just parsed + * @param paramValue The index of the start of the parameter value just parsed, or -1 + */ + protected void parsedParam(StringBuffer buffer, int valueLength, int paramName, int paramValue) + { + } + +} 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 be3990794ed..e098a68e635 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 @@ -30,7 +30,7 @@ import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpScheme; -import org.eclipse.jetty.http.QuotedCSV; +import org.eclipse.jetty.http.QuotedCSVParser; import org.eclipse.jetty.server.HttpConfiguration.Customizer; import org.eclipse.jetty.util.ArrayTrie; import org.eclipse.jetty.util.HostPort; @@ -42,8 +42,6 @@ import static java.lang.invoke.MethodType.methodType; -/* ------------------------------------------------------------ */ - /** * Customize Requests for Proxy Forwarding. *

@@ -157,7 +155,7 @@ public String getForcedHost() */ public void setForcedHost(String hostAndPort) { - _forcedHost = new HostPortHttpField(hostAndPort); + _forcedHost = new HostPortHttpField(new ForcedHostPort(hostAndPort)); } /** @@ -377,76 +375,26 @@ public void customize(Connector connector, HttpConfiguration config, Request req throw new RuntimeException(e); } - // Determine host - if (_forcedHost != null) - { - // Update host header - httpFields.put(_forcedHost); - request.setAuthority(_forcedHost.getHost(), _forcedHost.getPort()); - } - else if (forwarded._rfc7239 != null && forwarded._rfc7239._host != null) - { - HostPortHttpField auth = forwarded._rfc7239._host; - httpFields.put(auth); - request.setAuthority(auth.getHost(), auth.getPort()); - } - else if (forwarded._forwardedHost != null) - { - HostPortHttpField auth = new HostPortHttpField(forwarded._forwardedHost); - httpFields.put(auth); - request.setAuthority(auth.getHost(), auth.getPort()); - } - else if (_proxyAsAuthority) + if (forwarded._proto!=null) { - if (forwarded._rfc7239 != null && forwarded._rfc7239._by != null) - { - HostPortHttpField auth = forwarded._rfc7239._by; - httpFields.put(auth); - request.setAuthority(auth.getHost(), auth.getPort()); - } - else if (forwarded._forwardedServer != null) - { - request.setAuthority(forwarded._forwardedServer, request.getServerPort()); - } + request.setScheme(forwarded._proto); + if (forwarded._proto.equalsIgnoreCase(config.getSecureScheme())) + request.setSecure(true); } - // handle remote end identifier - if (forwarded._rfc7239 != null && forwarded._rfc7239._for != null) - { - request.setRemoteAddr(InetSocketAddress.createUnresolved(forwarded._rfc7239._for.getHost(), forwarded._rfc7239._for.getPort())); - } - else if (forwarded._forwardedFor != null) + if (forwarded._host!=null) { - int port = (forwarded._forwardedPort > 0) - ? forwarded._forwardedPort - : (forwarded._forwardedFor.getPort() > 0) - ? forwarded._forwardedFor.getPort() - : request.getRemotePort(); - request.setRemoteAddr(InetSocketAddress.createUnresolved(forwarded._forwardedFor.getHost(), port)); + httpFields.put(new HostPortHttpField(forwarded._host)); + request.setAuthority(forwarded._host.getHost(), forwarded._host.getPort()); } - // handle protocol identifier - if (forwarded._rfc7239 != null && forwarded._rfc7239._proto != null) + if (forwarded._for!=null) { - request.setScheme(forwarded._rfc7239._proto); - if (forwarded._rfc7239._proto.equals(config.getSecureScheme())) - request.setSecure(true); - } - else if (forwarded._forwardedProto != null) - { - request.setScheme(forwarded._forwardedProto); - if (forwarded._forwardedProto.equals(config.getSecureScheme())) - request.setSecure(true); - } - else if (forwarded._forwardedHttps != null && ("on".equalsIgnoreCase(forwarded._forwardedHttps) || "true".equalsIgnoreCase(forwarded._forwardedHttps))) - { - request.setScheme(HttpScheme.HTTPS.asString()); - if (HttpScheme.HTTPS.asString().equals(config.getSecureScheme())) - request.setSecure(true); + int port = forwarded._for.getPort()>0 ? forwarded._for.getPort() : request.getRemotePort(); + request.setRemoteAddr(InetSocketAddress.createUnresolved(forwarded._for.getHost(),port)); } } - /* ------------------------------------------------------------ */ protected String getLeftMost(String headerValue) { if (headerValue == null) @@ -464,26 +412,6 @@ protected String getLeftMost(String headerValue) return headerValue.substring(0, commaIndex).trim(); } - protected HostPort getRemoteAddr(String headerValue) - { - String leftMost = getLeftMost(headerValue); - if (leftMost == null) - { - return null; - } - - try - { - return new HostPort(leftMost); - } - catch (Exception e) - { - // failed to parse in host[:port] format - LOG.ignore(e); - return null; - } - } - @Override public String toString() { @@ -507,48 +435,6 @@ public void setHostHeader(String hostHeader) _forcedHost = new HostPortHttpField(hostHeader); } - private final class RFC7239 extends QuotedCSV - { - HostPortHttpField _by; - HostPortHttpField _for; - HostPortHttpField _host; - String _proto; - - private RFC7239() - { - super(false); - } - - @Override - protected void parsedParam(StringBuffer buffer, int valueLength, int paramName, int paramValue) - { - if (valueLength == 0 && paramValue > paramName) - { - String name = StringUtil.asciiToLowerCase(buffer.substring(paramName, paramValue - 1)); - String value = buffer.substring(paramValue); - switch (name) - { - case "by": - if (_by == null && !value.startsWith("_") && !"unknown".equals(value)) - _by = new HostPortHttpField(value); - break; - case "for": - if (_for == null && !value.startsWith("_") && !"unknown".equals(value)) - _for = new HostPortHttpField(value); - break; - case "host": - if (_host == null) - _host = new HostPortHttpField(value); - break; - case "proto": - if (_proto == null) - _proto = value; - break; - } - } - } - } - private void updateHandles() { int size = 0; @@ -589,23 +475,52 @@ private void updateHandles() } } - private class Forwarded + private static class ForcedHostPort extends HostPort + { + ForcedHostPort(String authority) + { + super(authority); + } + } + + private static class XHostPort extends HostPort + { + XHostPort(String authority) + { + super(authority); + } + + XHostPort(String host, int port) + { + super(host, port); + } + } + + private static class Rfc7239HostPort extends HostPort + { + Rfc7239HostPort(String authority) + { + super(authority); + } + } + + private class Forwarded extends QuotedCSVParser { HttpConfiguration _config; Request _request; - RFC7239 _rfc7239 = null; - String _forwardedHost = null; - String _forwardedServer = null; - String _forwardedProto = null; - HostPort _forwardedFor = null; - int _forwardedPort = -1; - String _forwardedHttps = null; + boolean _protoRfc7239; + String _proto; + HostPort _for; + HostPort _host; public Forwarded(Request request, HttpConfiguration config) { + super(false); _request = request; _config = config; + if (_forcedHost!=null) + _host = _forcedHost.getHostPort(); } public void handleCipherSuite(HttpField field) @@ -630,39 +545,87 @@ public void handleSslSessionId(HttpField field) public void handleHost(HttpField field) { - _forwardedHost = getLeftMost(field.getValue()); + if (_host==null) + _host = new XHostPort(getLeftMost(field.getValue())); } public void handleServer(HttpField field) { - _forwardedServer = getLeftMost(field.getValue()); + if (_proxyAsAuthority && _host==null) + _host = new XHostPort(getLeftMost(field.getValue())); } public void handleProto(HttpField field) { - _forwardedProto = getLeftMost(field.getValue()); + if (_proto==null) + _proto = getLeftMost(field.getValue()); } public void handleFor(HttpField field) { - _forwardedFor = getRemoteAddr(field.getValue()); + if (_for==null) + _for = new XHostPort(getLeftMost(field.getValue())); + else if (_for instanceof XHostPort && "unknown".equals(_for.getHost())) + _for = new XHostPort(HostPort.normalizeHost(getLeftMost(field.getValue())),_for.getPort()); } public void handlePort(HttpField field) { - _forwardedPort = field.getIntValue(); + if (_for == null) + _for = new XHostPort("unknown", field.getIntValue()); + else if (_for instanceof XHostPort && _for.getPort()<=0) + _for = new XHostPort(HostPort.normalizeHost(_for.getHost()), field.getIntValue()); } public void handleHttps(HttpField field) { - _forwardedHttps = getLeftMost(field.getValue()); + if (_proto==null && ("on".equalsIgnoreCase(field.getValue()) || "true".equalsIgnoreCase(field.getValue()))) + _proto = HttpScheme.HTTPS.asString(); } public void handleRFC7239(HttpField field) { - if (_rfc7239 == null) - _rfc7239 = new RFC7239(); - _rfc7239.addValue(field.getValue()); + addValue(field.getValue()); + } + + @Override + protected void parsedParam(StringBuffer buffer, int valueLength, int paramName, int paramValue) + { + if (valueLength == 0 && paramValue > paramName) + { + String name = StringUtil.asciiToLowerCase(buffer.substring(paramName, paramValue - 1)); + String value = buffer.substring(paramValue); + switch (name) + { + case "by": + if (!_proxyAsAuthority) + break; + if (value.startsWith("_") || "unknown".equals(value)) + break; + if (_host == null || _host instanceof XHostPort) + _host = new Rfc7239HostPort(value); + break; + case "for": + if (value.startsWith("_") || "unknown".equals(value)) + break; + if (_for == null || _for instanceof XHostPort) + _for = new Rfc7239HostPort(value); + break; + case "host": + if (value.startsWith("_") || "unknown".equals(value)) + break; + if (_host == null || _host instanceof XHostPort) + _host = new Rfc7239HostPort(value); + break; + case "proto": + if (_proto == null || !_protoRfc7239) + { + _protoRfc7239 = true; + _proto = value; + } + break; + } + } } } } 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 0a1cecec0c2..db8a9ad64e0 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 @@ -95,6 +95,68 @@ public void destroy() throws Exception _server.join(); } + @Test + public void testHostIpv4() throws Exception + { + String response=_connector.getResponse( + "GET / HTTP/1.1\n"+ + "Host: 1.2.3.4:2222\n"+ + "\n"); + assertThat(response, Matchers.containsString("200 OK")); + assertEquals("http",_results.poll()); + assertEquals("1.2.3.4",_results.poll()); + assertEquals("2222",_results.poll()); + assertEquals("0.0.0.0",_results.poll()); + assertEquals("0",_results.poll()); + } + + @Test + public void testHostIpv6() throws Exception + { + String response=_connector.getResponse( + "GET / HTTP/1.1\n"+ + "Host: [::1]:2222\n"+ + "\n"); + assertThat(response, Matchers.containsString("200 OK")); + assertEquals("http",_results.poll()); + assertEquals("[::1]",_results.poll()); + assertEquals("2222",_results.poll()); + assertEquals("0.0.0.0",_results.poll()); + assertEquals("0",_results.poll()); + } + + + + @Test + public void testURIIpv4() throws Exception + { + String response=_connector.getResponse( + "GET http://1.2.3.4:2222/ HTTP/1.1\n"+ + "Host: wrong\n"+ + "\n"); + assertThat(response, Matchers.containsString("200 OK")); + assertEquals("http",_results.poll()); + assertEquals("1.2.3.4",_results.poll()); + assertEquals("2222",_results.poll()); + assertEquals("0.0.0.0",_results.poll()); + assertEquals("0",_results.poll()); + } + + @Test + public void testURIIpv6() throws Exception + { + String response=_connector.getResponse( + "GET http://[::1]:2222/ HTTP/1.1\n"+ + "Host: wrong\n"+ + "\n"); + assertThat(response, Matchers.containsString("200 OK")); + assertEquals("http",_results.poll()); + assertEquals("[::1]",_results.poll()); + assertEquals("2222",_results.poll()); + assertEquals("0.0.0.0",_results.poll()); + assertEquals("0",_results.poll()); + } + @Test public void testRFC7239_Examples_4() throws Exception @@ -208,6 +270,7 @@ public void testFor() throws Exception "GET / HTTP/1.1\n"+ "Host: myhost\n"+ "X-Forwarded-For: 10.9.8.7,6.5.4.3\n"+ + "X-Forwarded-For: 8.9.8.7,7.5.4.3\n"+ "\n"); assertThat(response, Matchers.containsString("200 OK")); assertEquals("http",_results.poll()); @@ -264,6 +327,21 @@ public void testForIpv6AndPort() throws Exception assertEquals("80",_results.poll()); assertEquals("[1:2:3:4:5:6:7:8]",_results.poll()); assertEquals("2222",_results.poll()); + + response=_connector.getResponse( + "GET / HTTP/1.1\n"+ + "Host: myhost\n"+ + "X-Forwarded-Port: 2222\n"+ + "X-Forwarded-For: 1:2:3:4:5:6:7:8\n"+ + "X-Forwarded-For: 7:7:7:7:7:7:7:7\n"+ + "X-Forwarded-Port: 3333\n"+ + "\n"); + assertThat(response, Matchers.containsString("200 OK")); + assertEquals("http",_results.poll()); + assertEquals("myhost",_results.poll()); + assertEquals("80",_results.poll()); + assertEquals("[1:2:3:4:5:6:7:8]",_results.poll()); + assertEquals("2222",_results.poll()); } @Test @@ -355,7 +433,26 @@ public void testSslCertificate() throws Exception assertTrue(_wasSecure.get()); assertEquals("0123456789abcdef",_sslCertificate.get()); } - + + + @Test + public void testMixed() throws Exception + { + String response = _connector.getResponse( + "GET / HTTP/1.1\n" + + "Host: myhost\n" + + "X-Forwarded-For: 11.9.8.7:1111,8.5.4.3:2222\n" + + "X-Forwarded-Port: 3333\n" + + "Forwarded: for=192.0.2.43,for=198.51.100.17;by=203.0.113.60;proto=http;host=example.com\n"+ + "X-Forwarded-For: 11.9.8.7:1111,8.5.4.3:2222\n" + + "\n"); + + assertEquals("http",_results.poll()); + assertEquals("example.com",_results.poll()); + assertEquals("80",_results.poll()); + assertEquals("192.0.2.43",_results.poll()); + assertEquals("0",_results.poll()); + } interface RequestTester