406861 Fixed IPv6 redirect

This commit is contained in:
Greg Wilkins 2013-04-30 18:49:29 +10:00
parent 4e62b953e1
commit e26d8e67e5
9 changed files with 374 additions and 109 deletions

View File

@ -609,7 +609,8 @@ public class HttpParser
return true;
}
loop: for (int i = host.length(); i-- > 0;)
int len=host.length();
loop: for (int i = len; i-- > 0;)
{
char c2 = (char)(0xff & host.charAt(i));
switch (c2)
@ -620,6 +621,7 @@ public class HttpParser
case ':':
try
{
len=i;
port = StringUtil.toInt(host.substring(i+1));
}
catch (NumberFormatException e)
@ -628,10 +630,21 @@ public class HttpParser
badMessage(buffer,HttpStatus.BAD_REQUEST_400,"Bad Host header");
return true;
}
host = host.substring(0,i);
break loop;
}
}
if (host.charAt(0)=='[')
{
if (host.charAt(len-1)!=']')
{
badMessage(buffer,HttpStatus.BAD_REQUEST_400,"Bad IPv6 Host header");
return true;
}
host = host.substring(1,len-1);
}
else if (len!=host.length())
host = host.substring(0,len);
if (_requestHandler!=null)
_requestHandler.parsedHostHeader(host,port);

View File

@ -546,6 +546,8 @@ public class HttpURI
{
if (_host==_port)
return null;
if (_raw[_host]=='[')
return new String(_raw,_host+1,_port-_host-2,_charset);
return new String(_raw,_host,_port-_host,_charset);
}

View File

@ -809,7 +809,132 @@ public class HttpParserTest
assertFalse(buffer.hasRemaining());
assertEquals(HttpParser.State.CLOSED,parser.getState());
}
@Test
public void testHost() throws Exception
{
ByteBuffer buffer= BufferUtil.toBuffer(
"GET / HTTP/1.1\015\012"
+ "Host: host\015\012"
+ "Connection: close\015\012"
+ "\015\012");
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
parser.parseNext(buffer);
assertEquals("host",_host);
assertEquals(0,_port);
}
@Test
public void testIPHost() throws Exception
{
ByteBuffer buffer= BufferUtil.toBuffer(
"GET / HTTP/1.1\015\012"
+ "Host: 192.168.0.1\015\012"
+ "Connection: close\015\012"
+ "\015\012");
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
parser.parseNext(buffer);
assertEquals("192.168.0.1",_host);
assertEquals(0,_port);
}
@Test
public void testIPv6Host() throws Exception
{
ByteBuffer buffer= BufferUtil.toBuffer(
"GET / HTTP/1.1\015\012"
+ "Host: [::1]\015\012"
+ "Connection: close\015\012"
+ "\015\012");
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
parser.parseNext(buffer);
assertEquals("::1",_host);
assertEquals(0,_port);
}
@Test
public void testBadIPv6Host() throws Exception
{
ByteBuffer buffer= BufferUtil.toBuffer(
"GET / HTTP/1.1\015\012"
+ "Host: [::1\015\012"
+ "Connection: close\015\012"
+ "\015\012");
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
parser.parseNext(buffer);
assertEquals("Bad IPv6 Host header",_bad);
}
@Test
public void testHostPort() throws Exception
{
ByteBuffer buffer= BufferUtil.toBuffer(
"GET / HTTP/1.1\015\012"
+ "Host: myhost:8888\015\012"
+ "Connection: close\015\012"
+ "\015\012");
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
parser.parseNext(buffer);
assertEquals("myhost",_host);
assertEquals(8888,_port);
}
@Test
public void testHostBadPort() throws Exception
{
ByteBuffer buffer= BufferUtil.toBuffer(
"GET / HTTP/1.1\015\012"
+ "Host: myhost:xxx\015\012"
+ "Connection: close\015\012"
+ "\015\012");
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
parser.parseNext(buffer);
assertEquals("Bad Host header",_bad);
}
@Test
public void testIPHostPort() throws Exception
{
ByteBuffer buffer= BufferUtil.toBuffer(
"GET / HTTP/1.1\015\012"
+ "Host: 192.168.0.1:8888\015\012"
+ "Connection: close\015\012"
+ "\015\012");
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
parser.parseNext(buffer);
assertEquals("192.168.0.1",_host);
assertEquals(8888,_port);
}
@Test
public void testIPv6HostPort() throws Exception
{
ByteBuffer buffer= BufferUtil.toBuffer(
"GET / HTTP/1.1\015\012"
+ "Host: [::1]:8888\015\012"
+ "Connection: close\015\012"
+ "\015\012");
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
parser.parseNext(buffer);
assertEquals("::1",_host);
assertEquals(8888,_port);
}
@Before
public void init()
@ -826,6 +951,8 @@ public class HttpParserTest
_messageCompleted=false;
}
private String _host;
private int _port;
private String _bad;
private String _content;
private String _methodOrVersion;
@ -884,7 +1011,8 @@ public class HttpParserTest
@Override
public boolean parsedHostHeader(String host,int port)
{
// TODO test this
_host=host;
_port=port;
return false;
}

View File

@ -29,39 +29,51 @@ import org.junit.Test;
/* ------------------------------------------------------------ */
public class HttpURITest
{
public static final String __input = "http://example.com:8080/path/to/context?parameter=%22value%22#fragment";
public static final String __scheme = "http";
public static final String __host = "example.com";
public static final int __port = 8080;
public static final String __path = "/path/to/context";
public static final String __query = "parameter=%22value%22";
public static final String __fragment = "fragment";
String[][] tests=
{
{"/path/to/context",null,null,"-1","/path/to/context",null,null,null},
{"http://example.com/path/to/context;param?query=%22value%22#fragment","http","example.com","-1","/path/to/context","param","query=%22value%22","fragment"},
{"http://[::1]/path/to/context;param?query=%22value%22#fragment","http","::1","-1","/path/to/context","param","query=%22value%22","fragment"},
{"http://example.com:8080/path/to/context;param?query=%22value%22#fragment","http","example.com","8080","/path/to/context","param","query=%22value%22","fragment"},
{"http://[::1]:8080/path/to/context;param?query=%22value%22#fragment","http","::1","8080","/path/to/context","param","query=%22value%22","fragment"},
};
public static int
INPUT=0,SCHEME=1,HOST=2,PORT=3,PATH=4,PARAM=5,QUERY=6,FRAGMENT=7;
/* ------------------------------------------------------------ */
@Test
public void testFromString() throws Exception
{
HttpURI uri = new HttpURI(__input);
for (String[] test:tests)
{
HttpURI uri = new HttpURI(test[INPUT]);
assertEquals(__scheme, uri.getScheme());
assertEquals(__host,uri.getHost());
assertEquals(__port,uri.getPort());
assertEquals(__path,uri.getPath());
assertEquals(__query,uri.getQuery());
assertEquals(__fragment,uri.getFragment());
assertEquals(test[SCHEME], uri.getScheme());
assertEquals(test[HOST], uri.getHost());
assertEquals(Integer.parseInt(test[PORT]), uri.getPort());
assertEquals(test[PATH], uri.getPath());
assertEquals(test[PARAM], uri.getParam());
assertEquals(test[QUERY], uri.getQuery());
assertEquals(test[FRAGMENT], uri.getFragment());
}
}
/* ------------------------------------------------------------ */
@Test
public void testFromURI() throws Exception
{
HttpURI uri = new HttpURI(new URI(__input));
for (String[] test:tests)
{
HttpURI uri = new HttpURI(new URI(test[INPUT]));
assertEquals(__scheme, uri.getScheme());
assertEquals(__host,uri.getHost());
assertEquals(__port,uri.getPort());
assertEquals(__path,uri.getPath());
assertEquals(__query,uri.getQuery());
assertEquals(__fragment,uri.getFragment());
assertEquals(test[SCHEME], uri.getScheme());
assertEquals(test[HOST], uri.getHost());
assertEquals(Integer.parseInt(test[PORT]), uri.getPort());
assertEquals(test[PATH], uri.getPath());
assertEquals(test[PARAM], uri.getParam());
assertEquals(test[QUERY], uri.getQuery());
assertEquals(test[FRAGMENT], uri.getFragment());
}
}
}

View File

@ -1030,19 +1030,8 @@ public class Request implements HttpServletRequest
@Override
public StringBuffer getRequestURL()
{
final StringBuffer url = new StringBuffer(48);
String scheme = getScheme();
int port = getServerPort();
url.append(scheme);
url.append("://");
url.append(getServerName());
if (_port > 0 && ((scheme.equalsIgnoreCase(URIUtil.HTTP) && port != 80) || (scheme.equalsIgnoreCase(URIUtil.HTTPS) && port != 443)))
{
url.append(':');
url.append(_port);
}
final StringBuffer url = new StringBuffer(128);
URIUtil.appendSchemeHostPort(url,getScheme(),getServerName(),getServerPort());
url.append(getRequestURI());
return url;
}
@ -1066,20 +1055,8 @@ public class Request implements HttpServletRequest
*/
public StringBuilder getRootURL()
{
StringBuilder url = new StringBuilder(48);
String scheme = getScheme();
String server=getServerName();
int port = getServerPort();
if (server.indexOf(':')>=0)
url.append(scheme).append("://").append('[').append(server).append(']');
else
url.append(scheme).append("://").append(getServerName());
if (port > 0 && ((HttpScheme.HTTP.is(scheme) && port != 80) || (HttpScheme.HTTPS.is(scheme) && port != 443)))
{
url.append(':').append(port);
}
StringBuilder url = new StringBuilder(128);
URIUtil.appendSchemeHostPort(url,getScheme(),getServerName(),getServerPort());
return url;
}
@ -1109,41 +1086,58 @@ public class Request implements HttpServletRequest
// Return host from absolute URI
_serverName = _uri.getHost();
_port = _uri.getPort();
if (_serverName != null)
{
_port = _uri.getPort();
return _serverName;
}
// Return host from header field
String hostPort = _fields.getStringField(HttpHeader.HOST);
_port=0;
if (hostPort != null)
{
loop: for (int i = hostPort.length(); i-- > 0;)
int len=hostPort.length();
loop: for (int i = len; i-- > 0;)
{
char ch = (char)(0xff & hostPort.charAt(i));
switch (ch)
char c2 = (char)(0xff & hostPort.charAt(i));
switch (c2)
{
case ']':
break loop;
case ':':
_serverName = hostPort.substring(0,i);
try
{
len=i;
_port = StringUtil.toInt(hostPort.substring(i+1));
}
catch (NumberFormatException e)
{
LOG.warn(e);
_serverName=hostPort;
_port=0;
return _serverName;
}
return _serverName;
break loop;
}
}
if (_serverName == null || _port < 0)
if (hostPort.charAt(0)=='[')
{
_serverName = hostPort;
_port = 0;
if (hostPort.charAt(len-1)!=']')
{
LOG.warn("Bad IPv6 "+hostPort);
_serverName=hostPort;
_port=0;
return _serverName;
}
_serverName = hostPort.substring(1,len-1);
}
else if (len==hostPort.length())
_serverName=hostPort;
else
_serverName = hostPort.substring(0,len);
return _serverName;
}

View File

@ -23,6 +23,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
@ -44,6 +45,7 @@ public class CheckReverseProxyHeadersTest
"X-Forwarded-For: 10.20.30.40\n" +
"X-Forwarded-Host: example.com", new RequestValidator()
{
@Override
public void validate(HttpServletRequest request)
{
assertEquals("example.com", request.getServerName());
@ -55,6 +57,42 @@ public class CheckReverseProxyHeadersTest
assertFalse(request.isSecure());
}
});
// IPv6 ProxyPass from example.com:80 to localhost:8080
testRequest("Host: localhost:8080\n" +
"X-Forwarded-For: 10.20.30.40\n" +
"X-Forwarded-Host: [::1]", new RequestValidator()
{
@Override
public void validate(HttpServletRequest request)
{
assertEquals("::1", request.getServerName());
assertEquals(80, request.getServerPort());
assertEquals("10.20.30.40", request.getRemoteAddr());
assertEquals("10.20.30.40", request.getRemoteHost());
assertEquals("[::1]", request.getHeader("Host"));
assertEquals("http",request.getScheme());
assertFalse(request.isSecure());
}
});
// IPv6 ProxyPass from example.com:80 to localhost:8080
testRequest("Host: localhost:8080\n" +
"X-Forwarded-For: 10.20.30.40\n" +
"X-Forwarded-Host: [::1]:8888", new RequestValidator()
{
@Override
public void validate(HttpServletRequest request)
{
assertEquals("::1", request.getServerName());
assertEquals(8888, request.getServerPort());
assertEquals("10.20.30.40", request.getRemoteAddr());
assertEquals("10.20.30.40", request.getRemoteHost());
assertEquals("[::1]:8888", request.getHeader("Host"));
assertEquals("http",request.getScheme());
assertFalse(request.isSecure());
}
});
// ProxyPass from example.com:81 to localhost:8080
testRequest("Host: localhost:8080\n" +
@ -63,6 +101,7 @@ public class CheckReverseProxyHeadersTest
"X-Forwarded-Server: example.com\n"+
"X-Forwarded-Proto: https", new RequestValidator()
{
@Override
public void validate(HttpServletRequest request)
{
assertEquals("example.com", request.getServerName());
@ -82,6 +121,7 @@ public class CheckReverseProxyHeadersTest
"X-Forwarded-Server: example.com, rp.example.com\n"+
"X-Forwarded-Proto: https, http", new RequestValidator()
{
@Override
public void validate(HttpServletRequest request)
{
assertEquals("example.com", request.getServerName());
@ -111,7 +151,8 @@ public class CheckReverseProxyHeadersTest
try
{
server.start();
connector.getResponses("GET / HTTP/1.1\r\n" +"Connection: close\r\n" + headers + "\r\n\r\n");
connector.getResponses("GET / HTTP/1.1\r\n" +"Connection: close\r\n" + headers + "\r\n\r\n",
1000,TimeUnit.SECONDS);
Error error = validationHandler.getError();

View File

@ -135,11 +135,11 @@ public class HttpURITest
/*33*/ {"/?abc=test",null, null, null,null,"/", null,"abc=test",null},
/*34*/ {"/#fragment",null, null, null,null,"/", null,null,"fragment"},
/*35*/ {"http://192.0.0.1:8080/","http","//192.0.0.1:8080","192.0.0.1","8080","/",null,null,null},
/*36*/ {"http://[2001:db8::1]:8080/","http","//[2001:db8::1]:8080","[2001:db8::1]","8080","/",null,null,null},
/*37*/ {"http://user@[2001:db8::1]:8080/","http","//user@[2001:db8::1]:8080","[2001:db8::1]","8080","/",null,null,null},
/*38*/ {"http://[2001:db8::1]/","http","//[2001:db8::1]","[2001:db8::1]",null,"/",null,null,null},
/*36*/ {"http://[2001:db8::1]:8080/","http","//[2001:db8::1]:8080","2001:db8::1","8080","/",null,null,null},
/*37*/ {"http://user@[2001:db8::1]:8080/","http","//user@[2001:db8::1]:8080","2001:db8::1","8080","/",null,null,null},
/*38*/ {"http://[2001:db8::1]/","http","//[2001:db8::1]","2001:db8::1",null,"/",null,null,null},
/*39*/ {"//[2001:db8::1]:8080/",null,null,null,null,"//[2001:db8::1]:8080/",null,null,null},
/*40*/ {"http://user@[2001:db8::1]:8080/","http","//user@[2001:db8::1]:8080","[2001:db8::1]","8080","/",null,null,null},
/*40*/ {"http://user@[2001:db8::1]:8080/","http","//user@[2001:db8::1]:8080","2001:db8::1","8080","/",null,null,null},
/*41*/ {"*",null,null,null,null,"*",null, null,null}
};
@ -366,7 +366,7 @@ public class HttpURITest
{
/* 0*/ {" localhost:8080 ","localhost","8080"},
/* 1*/ {" 127.0.0.1:8080 ","127.0.0.1","8080"},
/* 2*/ {" [127::0::0::1]:8080 ","[127::0::0::1]","8080"},
/* 2*/ {" [127::0::0::1]:8080 ","127::0::0::1","8080"},
/* 3*/ {" error ",null,null},
/* 4*/ {" http://localhost:8080/ ",null,null},
};
@ -382,7 +382,7 @@ public class HttpURITest
byte[] buf = connect_tests[i][0].getBytes(StringUtil.__UTF8);
uri.parseConnect(buf,2,buf.length-4);
assertEquals("path"+i,connect_tests[i][1]+":"+connect_tests[i][2],uri.getPath());
assertEquals("path"+i,connect_tests[i][0].trim(),uri.getPath());
assertEquals("host"+i,connect_tests[i][1],uri.getHost());
assertEquals("port"+i,Integer.parseInt(connect_tests[i][2]),uri.getPort());
}

View File

@ -354,6 +354,7 @@ public class RequestTest
@Override
public boolean check(HttpServletRequest request,HttpServletResponse response)
{
results.add(request.getRequestURL().toString());
results.add(request.getRemoteAddr());
results.add(request.getServerName());
results.add(String.valueOf(request.getServerPort()));
@ -361,71 +362,121 @@ public class RequestTest
}
};
String responses=_connector.getResponses(
results.clear();
_connector.getResponses(
"GET / HTTP/1.1\n"+
"Host: myhost\n"+
"\n"+
"Connection: close\n"+
"\n");
int i=0;
assertEquals("http://myhost/",results.get(i++));
assertEquals("0.0.0.0",results.get(i++));
assertEquals("myhost",results.get(i++));
assertEquals("80",results.get(i++));
results.clear();
_connector.getResponses(
"GET / HTTP/1.1\n"+
"Host: myhost:8888\n"+
"\n"+
"Connection: close\n"+
"\n");
i=0;
assertEquals("http://myhost:8888/",results.get(i++));
assertEquals("0.0.0.0",results.get(i++));
assertEquals("myhost",results.get(i++));
assertEquals("8888",results.get(i++));
results.clear();
_connector.getResponses(
"GET / HTTP/1.1\n"+
"Host: 1.2.3.4\n"+
"\n"+
"Connection: close\n"+
"\n");
i=0;
assertEquals("http://1.2.3.4/",results.get(i++));
assertEquals("0.0.0.0",results.get(i++));
assertEquals("1.2.3.4",results.get(i++));
assertEquals("80",results.get(i++));
results.clear();
_connector.getResponses(
"GET / HTTP/1.1\n"+
"Host: 1.2.3.4:8888\n"+
"\n"+
"Connection: close\n"+
"\n");
i=0;
assertEquals("http://1.2.3.4:8888/",results.get(i++));
assertEquals("0.0.0.0",results.get(i++));
assertEquals("1.2.3.4",results.get(i++));
assertEquals("8888",results.get(i++));
results.clear();
_connector.getResponses(
"GET / HTTP/1.1\n"+
"Host: [::1]\n"+
"\n"+
"Connection: close\n"+
"\n");
i=0;
assertEquals("http://[::1]/",results.get(i++));
assertEquals("0.0.0.0",results.get(i++));
assertEquals("::1",results.get(i++));
assertEquals("80",results.get(i++));
results.clear();
_connector.getResponses(
"GET / HTTP/1.1\n"+
"Host: [::1]:8888\n"+
"\n"+
"Connection: close\n"+
"\n");
i=0;
assertEquals("http://[::1]:8888/",results.get(i++));
assertEquals("0.0.0.0",results.get(i++));
assertEquals("::1",results.get(i++));
assertEquals("8888",results.get(i++));
results.clear();
_connector.getResponses(
"GET / HTTP/1.1\n"+
"Host: [::1]\n"+
"x-forwarded-for: remote\n"+
"x-forwarded-proto: https\n"+
"\n"+
"Connection: close\n"+
"\n");
i=0;
assertEquals("https://[::1]/",results.get(i++));
assertEquals("remote",results.get(i++));
assertEquals("::1",results.get(i++));
assertEquals("443",results.get(i++));
results.clear();
_connector.getResponses(
"GET / HTTP/1.1\n"+
"Host: [::1]:8888\n"+
"Connection: close\n"+
"x-forwarded-for: remote\n"+
"x-forwarded-proto: https\n"+
"\n",10,TimeUnit.SECONDS);
int i=0;
assertEquals("0.0.0.0",results.get(i++));
assertEquals("myhost",results.get(i++));
assertEquals("80",results.get(i++));
assertEquals("0.0.0.0",results.get(i++));
assertEquals("myhost",results.get(i++));
assertEquals("8888",results.get(i++));
assertEquals("0.0.0.0",results.get(i++));
assertEquals("1.2.3.4",results.get(i++));
assertEquals("80",results.get(i++));
assertEquals("0.0.0.0",results.get(i++));
assertEquals("1.2.3.4",results.get(i++));
assertEquals("8888",results.get(i++));
assertEquals("0.0.0.0",results.get(i++));
assertEquals("[::1]",results.get(i++));
assertEquals("80",results.get(i++));
assertEquals("0.0.0.0",results.get(i++));
assertEquals("[::1]",results.get(i++));
assertEquals("8888",results.get(i++));
"\n");
i=0;
assertEquals("https://[::1]:8888/",results.get(i++));
assertEquals("remote",results.get(i++));
assertEquals("[::1]",results.get(i++));
assertEquals("443",results.get(i++));
assertEquals("remote",results.get(i++));
assertEquals("[::1]",results.get(i++));
assertEquals("::1",results.get(i++));
assertEquals("8888",results.get(i++));
}
@Test

View File

@ -657,6 +657,30 @@ public class URIUtil
return false;
}
public static void appendSchemeHostPort(StringBuilder url,String scheme,String server, int port)
{
if (server.indexOf(':')>=0&&server.charAt(0)!='[')
url.append(scheme).append("://").append('[').append(server).append(']');
else
url.append(scheme).append("://").append(server);
if (port > 0 && (("http".equalsIgnoreCase(scheme) && port != 80) || ("https".equalsIgnoreCase(scheme) && port != 443)))
url.append(':').append(port);
}
public static void appendSchemeHostPort(StringBuffer url,String scheme,String server, int port)
{
synchronized (url)
{
if (server.indexOf(':')>=0&&server.charAt(0)!='[')
url.append(scheme).append("://").append('[').append(server).append(']');
else
url.append(scheme).append("://").append(server);
if (port > 0 && (("http".equalsIgnoreCase(scheme) && port != 80) || ("https".equalsIgnoreCase(scheme) && port != 443)))
url.append(':').append(port);
}
}
}