Merge remote-tracking branch 'origin/jetty-9.3.x' into jetty-9.4.x

This commit is contained in:
Greg Wilkins 2016-08-31 20:32:54 +10:00
commit 5ab9846ac5
17 changed files with 347 additions and 98 deletions

View File

@ -37,6 +37,7 @@ import org.eclipse.jetty.io.ClientConnectionFactory;
import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
import org.eclipse.jetty.util.BlockingArrayQueue;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.HostPort;
import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
@ -87,7 +88,7 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
}
this.connectionFactory = connectionFactory;
String host = getHost();
String host = HostPort.normalizeHost(getHost());
if (!client.isDefaultPort(getScheme(), getPort()))
host += ":" + getPort();
hostField = new HttpField(HttpHeader.HOST, host);

View File

@ -25,6 +25,7 @@ import java.util.List;
import java.util.Set;
import org.eclipse.jetty.io.ClientConnectionFactory;
import org.eclipse.jetty.util.HostPort;
/**
* The configuration of the forward proxy to use with {@link org.eclipse.jetty.client.HttpClient}.
@ -58,6 +59,7 @@ public class ProxyConfiguration
public static abstract class Proxy
{
// TO use IPAddress Map
private final Set<String> included = new HashSet<>();
private final Set<String> excluded = new HashSet<>();
private final Origin.Address address;
@ -149,12 +151,10 @@ public class ProxyConfiguration
private boolean matches(Origin.Address address, String pattern)
{
// TODO: add support for CIDR notation like 192.168.0.0/24, see DoSFilter
int colon = pattern.indexOf(':');
if (colon < 0)
return pattern.equals(address.getHost());
String host = pattern.substring(0, colon);
String port = pattern.substring(colon + 1);
return host.equals(address.getHost()) && port.equals(String.valueOf(address.getPort()));
HostPort hostPort = new HostPort(pattern);
String host = hostPort.getHost();
int port = hostPort.getPort();
return host.equals(address.getHost()) && ( port<=0 || port==address.getPort() );
}
/**

View File

@ -89,6 +89,7 @@ import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.SocketAddressResolver;
import org.eclipse.jetty.util.log.StacklessLogging;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.Rule;
@ -1535,6 +1536,31 @@ public class HttpClientTest extends AbstractHttpClientServerTest
}
}
@Test
public void test_IPv6_Host() throws Exception
{
start(new AbstractHandler()
{
@Override
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
baseRequest.setHandled(true);
response.setContentType("text/plain");
response.getOutputStream().print(request.getHeader("Host"));
}
});
URI uri = URI.create(scheme + "://[::1]:" + connector.getLocalPort() + "/path");
ContentResponse response = client.newRequest(uri)
.method(HttpMethod.PUT)
.timeout(5, TimeUnit.SECONDS)
.send();
Assert.assertNotNull(response);
Assert.assertEquals(200, response.getStatus());
Assert.assertThat(new String(response.getContent(), StandardCharsets.ISO_8859_1),Matchers.startsWith("[::1]:"));
}
private void consume(InputStream input, boolean eof) throws IOException
{
int crlfs = 0;

View File

@ -63,4 +63,16 @@ public class ProxyConfigurationTest
Assert.assertTrue(proxy.matches(new Origin("http", "1.2.3.4", 0)));
Assert.assertFalse(proxy.matches(new Origin("http", "1.2.3.4", 5)));
}
@Test
public void testProxyMatchesWithIncludesAndExcludesIPv6() throws Exception
{
HttpProxy proxy = new HttpProxy("host", 0);
proxy.getIncludedAddresses().add("[1::2:3:4]");
proxy.getExcludedAddresses().add("[1::2:3:4]:5");
Assert.assertFalse(proxy.matches(new Origin("http", "any", 0)));
Assert.assertTrue(proxy.matches(new Origin("http", "[1::2:3:4]", 0)));
Assert.assertFalse(proxy.matches(new Origin("http", "[1::2:3:4]", 5)));
}
}

View File

@ -19,7 +19,7 @@
package org.eclipse.jetty.http;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.HostPort;
@ -28,57 +28,20 @@ import org.eclipse.jetty.util.StringUtil;
*/
public class HostPortHttpField extends HttpField
{
private final String _host;
private final int _port;
final HostPort _hostPort;
public HostPortHttpField(String authority)
{
this(HttpHeader.HOST,HttpHeader.HOST.asString(),authority);
}
public HostPortHttpField(HttpHeader header, String name, String authority)
/* ------------------------------------------------------------ */
protected HostPortHttpField(HttpHeader header, String name, String authority)
{
super(header,name,authority);
if (authority==null || authority.length()==0)
throw new IllegalArgumentException("No Authority");
try
{
if (authority.charAt(0)=='[')
{
// ipv6reference
int close=authority.lastIndexOf(']');
if (close<0)
throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Bad ipv6");
_host=authority.substring(0,close+1);
if (authority.length()>close+1)
{
if (authority.charAt(close+1)!=':')
throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Bad ipv6 port");
_port=StringUtil.toInt(authority,close+2);
}
else
_port=0;
}
else
{
// ipv4address or hostname
int c = authority.lastIndexOf(':');
if (c>=0)
{
_host=authority.substring(0,c);
_port=StringUtil.toInt(authority,c+1);
}
else
{
_host=authority;
_port=0;
}
}
}
catch (BadMessageException bm)
{
throw bm;
_hostPort=new HostPort(authority);
}
catch(Exception e)
{
@ -92,7 +55,7 @@ public class HostPortHttpField extends HttpField
*/
public String getHost()
{
return _host;
return _hostPort.getHost();
}
/* ------------------------------------------------------------ */
@ -101,6 +64,16 @@ public class HostPortHttpField extends HttpField
*/
public int getPort()
{
return _port;
return _hostPort.getPort();
}
/* ------------------------------------------------------------ */
/** Get the port.
* @param defaultPort
* @return the port
*/
public int getPort(int defaultPort)
{
return _hostPort.getPort(defaultPort);
}
}

View File

@ -28,6 +28,8 @@ import org.eclipse.jetty.http.HttpTokens.EndOfContent;
import org.eclipse.jetty.util.ArrayTernaryTrie;
import org.eclipse.jetty.util.ArrayTrie;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.HostPort;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.Trie;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.Utf8StringBuilder;
@ -933,6 +935,7 @@ public class HttpParser
break;
default: break;
}
if (add_to_connection_trie && !_connectionFields.isFull() && _header!=null && _valueString!=null)

View File

@ -25,6 +25,7 @@ import java.util.List;
import org.eclipse.jetty.http.HttpParser.State;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.log.StacklessLogging;
import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Before;
@ -1632,16 +1633,19 @@ public class HttpParserTest
@Test
public void testBadIPv6Host() throws Exception
{
ByteBuffer buffer = BufferUtil.toBuffer(
try(StacklessLogging s = new StacklessLogging(HttpParser.class))
{
ByteBuffer buffer = BufferUtil.toBuffer(
"GET / HTTP/1.1\r\n"
+ "Host: [::1\r\n"
+ "Connection: close\r\n"
+ "\r\n");
+ "Host: [::1\r\n"
+ "Connection: close\r\n"
+ "\r\n");
HttpParser.RequestHandler handler = new Handler();
HttpParser parser = new HttpParser(handler);
parser.parseNext(buffer);
Assert.assertThat(_bad, Matchers.containsString("Bad"));
HttpParser.RequestHandler handler = new Handler();
HttpParser parser = new HttpParser(handler);
parser.parseNext(buffer);
Assert.assertThat(_bad, Matchers.containsString("Bad"));
}
}
@Test

View File

@ -32,6 +32,7 @@ import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;
import org.eclipse.jetty.util.HostPort;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@ -134,15 +135,10 @@ public class ConnectorServer extends AbstractLifeCycle
*/
private String startRegistry(String hostPath) throws Exception
{
int rmiPort = 1099; // default RMI registry port
String rmiHost = hostPath;
HostPort hostPort = new HostPort(hostPath);
int idx = hostPath.indexOf(':');
if (idx > 0)
{
rmiPort = Integer.parseInt(hostPath.substring(idx + 1));
rmiHost = hostPath.substring(0,idx);
}
String rmiHost = hostPort.getHost();
int rmiPort = hostPort.getPort(1099);
// Verify that local registry is being used
InetAddress hostAddress = InetAddress.getByName(rmiHost);
@ -171,7 +167,7 @@ public class ConnectorServer extends AbstractLifeCycle
_registry = LocateRegistry.createRegistry(rmiPort);
Thread.sleep(1000);
rmiHost = InetAddress.getLocalHost().getCanonicalHostName();
rmiHost = HostPort.normalizeHost(InetAddress.getLocalHost().getCanonicalHostName());
return rmiHost + ':' + Integer.toString(rmiPort);
}

View File

@ -54,6 +54,7 @@ import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.HandlerWrapper;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.HostPort;
import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.log.Log;
@ -224,14 +225,9 @@ public class ConnectHandler extends HandlerWrapper
return;
}
String host = serverAddress;
int port = 80;
int colon = serverAddress.indexOf(':');
if (colon > 0)
{
host = serverAddress.substring(0, colon);
port = Integer.parseInt(serverAddress.substring(colon + 1));
}
HostPort hostPort = new HostPort(serverAddress);
String host = hostPort.getHost();
int port = hostPort.getPort(80);
if (!validateDestination(host, port))
{

View File

@ -18,9 +18,11 @@
package org.eclipse.jetty.proxy;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
@ -65,7 +67,7 @@ public class ConnectHandlerTest extends AbstractConnectHandlerTest
@Test
public void testCONNECT() throws Exception
{
{
String hostPort = "localhost:" + serverConnector.getLocalPort();
String request = "" +
"CONNECT " + hostPort + " HTTP/1.1\r\n" +
@ -84,6 +86,27 @@ public class ConnectHandlerTest extends AbstractConnectHandlerTest
}
}
@Test
public void testCONNECTwithIPv6() throws Exception
{
String hostPort = "[::1]:" + serverConnector.getLocalPort();
String request = "" +
"CONNECT " + hostPort + " HTTP/1.1\r\n" +
"Host: " + hostPort + "\r\n" +
"\r\n";
try (Socket socket = newSocket())
{
OutputStream output = socket.getOutputStream();
output.write(request.getBytes(StandardCharsets.UTF_8));
output.flush();
// Expect 200 OK from the CONNECT request
HttpTester.Response response = readResponse(socket.getInputStream());
Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
}
}
@Test
public void testCONNECTAndGET() throws Exception
{

View File

@ -80,6 +80,7 @@ import org.eclipse.jetty.server.session.Session;
import org.eclipse.jetty.server.session.SessionHandler;
import org.eclipse.jetty.util.Attributes;
import org.eclipse.jetty.util.AttributesMap;
import org.eclipse.jetty.util.HostPort;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.MultiMap;
import org.eclipse.jetty.util.MultiPartInputStreamParser;

View File

@ -22,22 +22,21 @@ package org.eclipse.jetty.util;
* A {@link Throwable} that may be used in static contexts. It uses Java 7
* constructor that prevents setting stackTrace inside exception object.
*/
public class ConstantThrowable extends Throwable {
private String name;
public ConstantThrowable() {
public class ConstantThrowable extends Throwable
{
public ConstantThrowable()
{
this(null);
}
public ConstantThrowable(String name) {
super(null, null, false, false);
this.name = name;
public ConstantThrowable(String name)
{
super(name, null, false, false);
}
@Override
public String toString() {
return name;
public String toString()
{
return String.valueOf(getMessage());
}
}

View File

@ -0,0 +1,129 @@
//
// ========================================================================
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.util;
/**
* Parse an authority string into Host and Port
* <p>Parse a string in the form "host:port", handling IPv4 an IPv6 hosts</p>
*
*/
public class HostPort
{
private final String _host;
private final int _port;
public HostPort(String authority) throws IllegalArgumentException
{
if (authority==null || authority.length()==0)
throw new IllegalArgumentException("No Authority");
try
{
if (authority.charAt(0)=='[')
{
// ipv6reference
int close=authority.lastIndexOf(']');
if (close<0)
throw new IllegalArgumentException("Bad IPv6 host");
_host=authority.substring(0,close+1);
if (authority.length()>close+1)
{
if (authority.charAt(close+1)!=':')
throw new IllegalArgumentException("Bad IPv6 port");
_port=StringUtil.toInt(authority,close+2);
}
else
_port=0;
}
else
{
// ipv4address or hostname
int c = authority.lastIndexOf(':');
if (c>=0)
{
_host=authority.substring(0,c);
_port=StringUtil.toInt(authority,c+1);
}
else
{
_host=authority;
_port=0;
}
}
}
catch (IllegalArgumentException iae)
{
throw iae;
}
catch(final Exception ex)
{
throw new IllegalArgumentException("Bad HostPort")
{
{initCause(ex);}
};
}
if(_host.isEmpty())
throw new IllegalArgumentException("Bad host");
if(_port<0)
throw new IllegalArgumentException("Bad port");
}
/* ------------------------------------------------------------ */
/** Get the host.
* @return the host
*/
public String getHost()
{
return _host;
}
/* ------------------------------------------------------------ */
/** Get the port.
* @return the port
*/
public int getPort()
{
return _port;
}
/* ------------------------------------------------------------ */
/** Get the port.
* @param defaultPort, the default port to return if a port is not specified
* @return the port
*/
public int getPort(int defaultPort)
{
return _port>0?_port:defaultPort;
}
/* ------------------------------------------------------------ */
/** Normalize IPv6 address as per https://www.ietf.org/rfc/rfc2732.txt
* @param host A host name
* @return Host name surrounded by '[' and ']' as needed.
*/
public static String normalizeHost(String host)
{
// if it is normalized IPv6 or could not be IPv6, return
if (host.isEmpty() || host.charAt(0)=='[' || host.indexOf(':')<0)
return host;
// normalize with [ ]
return "["+host+"]";
}
}

View File

@ -41,6 +41,7 @@ import java.util.StringTokenizer;
* a,b,... - a list of wildcard specifications
* </pre>
* @param <TYPE> the Map Entry value type
* @deprecated
*/
@SuppressWarnings("serial")
public class IPAddressMap<TYPE> extends HashMap<String, TYPE>

View File

@ -682,7 +682,6 @@ public class StringUtil
return sidBytes;
}
/**
* Convert String to an integer. Parses up to the first non-numeric character. If no number is found an IllegalArgumentException is thrown

View File

@ -720,10 +720,7 @@ public class URIUtil
*/
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);
url.append(scheme).append("://").append(HostPort.normalizeHost(server));
if (port > 0)
{
@ -757,10 +754,7 @@ public class URIUtil
{
synchronized (url)
{
if (server.indexOf(':')>=0&&server.charAt(0)!='[')
url.append(scheme).append("://").append('[').append(server).append(']');
else
url.append(scheme).append("://").append(server);
url.append(scheme).append("://").append(HostPort.normalizeHost(server));
if (port > 0)
{

View File

@ -0,0 +1,92 @@
//
// ========================================================================
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.util;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.*;
import java.util.Arrays;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class HostPortTest
{
@Parameters(name="{0}")
public static List<String[]> testCases()
{
String data[][] = new String[][] {
{"host","host",null},
{"host:80","host","80"},
{"10.10.10.1","10.10.10.1",null},
{"10.10.10.1:80","10.10.10.1","80"},
{"[0::0::0::1]","[0::0::0::1]",null},
{"[0::0::0::1]:80","[0::0::0::1]","80"},
{null,null,null},
{"host:",null,null},
{"",null,null},
{":80",null,"80"},
{"127.0.0.1:",null,null},
{"[0::0::0::0::1]:",null,null},
{"host:xxx",null,null},
{"127.0.0.1:xxx",null,null},
{"[0::0::0::0::1]:xxx",null,null},
{"host:-80",null,null},
{"127.0.0.1:-80",null,null},
{"[0::0::0::0::1]:-80",null,null},
};
return Arrays.asList(data);
}
@Parameter(0)
public String _authority;
@Parameter(1)
public String _expectedHost;
@Parameter(2)
public String _expectedPort;
@Test
public void test()
{
try
{
HostPort hostPort = new HostPort(_authority);
assertThat(hostPort.getHost(),is(_expectedHost));
if (_expectedPort==null)
assertThat(hostPort.getPort(),is(0));
else
assertThat(hostPort.getPort(),is(Integer.valueOf(_expectedPort)));
}
catch (Exception e)
{
assertNull(_expectedHost);
}
}
}