Merge branch 'jetty-10.0.x' of github.com:eclipse/jetty.project into jetty-10.0.x

This commit is contained in:
Jan Bartel 2020-09-23 16:20:21 +02:00
commit 90e61eb07f
9 changed files with 161 additions and 51 deletions

View File

@ -1126,18 +1126,12 @@ public class HttpClient extends ContainerLifeCycle
{ {
if (port > 0) if (port > 0)
return port; return port;
else if (isSchemeSecure(scheme)) return HttpScheme.getDefaultPort(scheme);
return 443;
else
return 80;
} }
public boolean isDefaultPort(String scheme, int port) public boolean isDefaultPort(String scheme, int port)
{ {
if (isSchemeSecure(scheme)) return HttpScheme.getDefaultPort(scheme) == port;
return port == 443;
else
return port == 80;
} }
public static boolean isSchemeSecure(String scheme) public static boolean isSchemeSecure(String scheme)

View File

@ -639,22 +639,28 @@ public interface HttpFields extends Iterable<HttpField>
public Mutable add(HttpFields fields) public Mutable add(HttpFields fields)
{ {
if (_fields == null)
_fields = new HttpField[fields.size() + 4];
else if (_size + fields.size() >= _fields.length)
_fields = Arrays.copyOf(_fields, _size + fields.size() + 4);
if (fields.size() == 0)
return this;
if (fields instanceof Immutable) if (fields instanceof Immutable)
{ {
Immutable b = (Immutable)fields; Immutable b = (Immutable)fields;
_fields = Arrays.copyOf(b._fields, b._fields.length + 4); System.arraycopy(b._fields, 0, _fields, _size, b._fields.length);
_size = b._fields.length; _size += b._fields.length;
} }
else if (fields instanceof Mutable) else if (fields instanceof Mutable)
{ {
Mutable b = (Mutable)fields; Mutable b = (Mutable)fields;
_fields = Arrays.copyOf(b._fields, b._fields.length); System.arraycopy(b._fields, 0, _fields, _size, b._size);
_size = b._size; _size += b._size;
} }
else else
{ {
_fields = new HttpField[fields.size() + 4];
_size = 0;
for (HttpField f : fields) for (HttpField f : fields)
_fields[_size++] = f; _fields[_size++] = f;
} }

View File

@ -25,14 +25,14 @@ import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Trie; import org.eclipse.jetty.util.Trie;
/** /**
* * HTTP and WebSocket Schemes
*/ */
public enum HttpScheme public enum HttpScheme
{ {
HTTP("http"), HTTP("http", 80),
HTTPS("https"), HTTPS("https", 443),
WS("ws"), WS("ws", 80),
WSS("wss"); WSS("wss", 443);
public static final Trie<HttpScheme> CACHE = new ArrayTrie<HttpScheme>(); public static final Trie<HttpScheme> CACHE = new ArrayTrie<HttpScheme>();
@ -46,11 +46,13 @@ public enum HttpScheme
private final String _string; private final String _string;
private final ByteBuffer _buffer; private final ByteBuffer _buffer;
private final int _defaultPort;
HttpScheme(String s) HttpScheme(String s, int port)
{ {
_string = s; _string = s;
_buffer = BufferUtil.toBuffer(s); _buffer = BufferUtil.toBuffer(s);
_defaultPort = port;
} }
public ByteBuffer asByteBuffer() public ByteBuffer asByteBuffer()
@ -60,7 +62,7 @@ public enum HttpScheme
public boolean is(String s) public boolean is(String s)
{ {
return s != null && _string.equalsIgnoreCase(s); return _string.equalsIgnoreCase(s);
} }
public String asString() public String asString()
@ -68,9 +70,31 @@ public enum HttpScheme
return _string; return _string;
} }
public int getDefaultPort()
{
return _defaultPort;
}
public int normalizePort(int port)
{
return port == _defaultPort ? 0 : port;
}
@Override @Override
public String toString() public String toString()
{ {
return _string; return _string;
} }
public static int getDefaultPort(String scheme)
{
HttpScheme httpScheme = scheme == null ? null : CACHE.get(scheme);
return httpScheme == null ? HTTP.getDefaultPort() : httpScheme.getDefaultPort();
}
public static int normalizePort(String scheme, int port)
{
HttpScheme httpScheme = scheme == null ? null : CACHE.get(scheme);
return httpScheme == null ? port : httpScheme.normalizePort(port);
}
} }

View File

@ -638,11 +638,12 @@ public interface HttpURI
public Mutable normalize() public Mutable normalize()
{ {
if (_port == 80 && HttpScheme.HTTP.is(_scheme)) HttpScheme scheme = _scheme == null ? null : HttpScheme.CACHE.get(_scheme);
if (scheme != null && _port == scheme.getDefaultPort())
{
_port = 0; _port = 0;
if (_port == 443 && HttpScheme.HTTPS.is(_scheme)) _uri = null;
_port = 0; }
_uri = null;
return this; return this;
} }

View File

@ -742,6 +742,25 @@ public class HttpFieldsTest
assertThat(fields.size(), is(0)); assertThat(fields.size(), is(0));
} }
@Test
public void testAddHttpFields()
{
HttpFields.Mutable fields = new HttpFields.Mutable(new HttpFields.Mutable());
fields.add("One", "1");
fields = new HttpFields.Mutable(fields);
fields.add(HttpFields.build().add("two", "2").add("three", "3"));
fields.add(HttpFields.build().add("four", "4").add("five", "5").asImmutable());
assertThat(fields.size(), is(5));
assertThat(fields.get("one"), is("1"));
assertThat(fields.get("two"), is("2"));
assertThat(fields.get("three"), is("3"));
assertThat(fields.get("four"), is("4"));
assertThat(fields.get("five"), is("5"));
}
@Test @Test
public void testPutNullName() public void testPutNullName()
{ {

View File

@ -18,31 +18,34 @@
package org.eclipse.jetty.server; package org.eclipse.jetty.server;
import java.util.Objects; import org.eclipse.jetty.http.HostPortHttpField;
import javax.servlet.http.HttpServletRequest; import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.http.HttpURI; import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.http.HttpVersion;
/** /**
* Customizes requests that lack the {@code Host} header (for example, HTTP 1.0 requests). * Adds a missing {@code Host} header (for example, HTTP 1.0 or 2.0 requests).
* <p> * <p>
* In case of HTTP 1.0 requests that lack the {@code Host} header, the application may issue * The host and port may be provided in the constructor or taken from the
* a redirect, and the {@code Location} header is usually constructed from the {@code Host} * {@link Request#getServerName()} and {@link Request#getServerPort()} methods.
* header; if the {@code Host} header is missing, the server may query the connector for its * </p>
* IP address in order to construct the {@code Location} header, and thus leak to clients
* internal IP addresses.
* <p>
* This {@link HttpConfiguration.Customizer} is configured with a {@code serverName} and
* optionally a {@code serverPort}.
* If the {@code Host} header is absent, the configured {@code serverName} will be set on
* the request so that {@link HttpServletRequest#getServerName()} will return that value,
* and likewise for {@code serverPort} and {@link HttpServletRequest#getServerPort()}.
*/ */
public class HostHeaderCustomizer implements HttpConfiguration.Customizer public class HostHeaderCustomizer implements HttpConfiguration.Customizer
{ {
private final String serverName; private final String serverName;
private final int serverPort; private final int serverPort;
/**
* Construct customizer that uses {@link Request#getServerName()} and
* {@link Request#getServerPort()} to create a host header.
*/
public HostHeaderCustomizer()
{
this(null, 0);
}
/** /**
* @param serverName the {@code serverName} to set on the request (the {@code serverPort} will not be set) * @param serverName the {@code serverName} to set on the request (the {@code serverPort} will not be set)
*/ */
@ -57,15 +60,26 @@ public class HostHeaderCustomizer implements HttpConfiguration.Customizer
*/ */
public HostHeaderCustomizer(String serverName, int serverPort) public HostHeaderCustomizer(String serverName, int serverPort)
{ {
this.serverName = Objects.requireNonNull(serverName); this.serverName = serverName;
this.serverPort = serverPort; this.serverPort = serverPort;
} }
@Override @Override
public void customize(Connector connector, HttpConfiguration channelConfig, Request request) public void customize(Connector connector, HttpConfiguration channelConfig, Request request)
{ {
if (request.getHeader("Host") == null) if (request.getHttpVersion() != HttpVersion.HTTP_1_1 && !request.getHttpFields().contains(HttpHeader.HOST))
// TODO set the field as well? {
request.setHttpURI(HttpURI.build(request.getHttpURI()).host(serverName).port(serverPort)); String host = serverName == null ? request.getServerName() : serverName;
int port = HttpScheme.normalizePort(request.getScheme(), serverPort == 0 ? request.getServerPort() : serverPort);
if (serverName != null || serverPort > 0)
request.setHttpURI(HttpURI.build(request.getHttpURI()).authority(host, port));
HttpFields original = request.getHttpFields();
HttpFields.Mutable httpFields = HttpFields.build(original.size() + 1);
httpFields.add(new HostPortHttpField(host, port));
httpFields.add(request.getHttpFields());
request.setHttpFields(httpFields);
}
} }
} }

View File

@ -1355,11 +1355,7 @@ public class Request implements HttpServletRequest
// If no port specified, return the default port for the scheme // If no port specified, return the default port for the scheme
if (port <= 0) if (port <= 0)
{ return HttpScheme.getDefaultPort(getScheme());
if (getScheme().equalsIgnoreCase(URIUtil.HTTPS))
return 443;
return 80;
}
// return a specific port // return a specific port
return port; return port;

View File

@ -330,7 +330,7 @@ public class Response implements HttpServletResponse
path = (path == null ? "" : path); path = (path == null ? "" : path);
int port = uri.getPort(); int port = uri.getPort();
if (port < 0) if (port < 0)
port = HttpScheme.HTTPS.asString().equalsIgnoreCase(uri.getScheme()) ? 443 : 80; port = HttpScheme.getDefaultPort(uri.getScheme());
// Is it the same server? // Is it the same server?
if (!request.getServerName().equalsIgnoreCase(uri.getHost())) if (!request.getServerName().equalsIgnoreCase(uri.getHost()))

View File

@ -37,7 +37,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
public class HostHeaderCustomizerTest public class HostHeaderCustomizerTest
{ {
@Test @Test
public void testHostHeaderCustomizer() throws Exception public void testFixedHostPort() throws Exception
{ {
Server server = new Server(); Server server = new Server();
HttpConfiguration httpConfig = new HttpConfiguration(); HttpConfiguration httpConfig = new HttpConfiguration();
@ -55,6 +55,7 @@ public class HostHeaderCustomizerTest
baseRequest.setHandled(true); baseRequest.setHandled(true);
assertEquals(serverName, request.getServerName()); assertEquals(serverName, request.getServerName());
assertEquals(serverPort, request.getServerPort()); assertEquals(serverPort, request.getServerPort());
assertEquals(serverName + ":" + serverPort, request.getHeader("Host"));
response.sendRedirect(redirectPath); response.sendRedirect(redirectPath);
} }
}); });
@ -89,4 +90,59 @@ public class HostHeaderCustomizerTest
server.stop(); server.stop();
} }
} }
@Test
public void testHostPort() throws Exception
{
Server server = new Server();
HttpConfiguration httpConfig = new HttpConfiguration();
final String serverName = "127.0.0.1";
final String redirectPath = "/redirect";
httpConfig.addCustomizer(new HostHeaderCustomizer());
final ServerConnector connector = new ServerConnector(server, new HttpConnectionFactory(httpConfig));
server.addConnector(connector);
server.setHandler(new AbstractHandler()
{
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
baseRequest.setHandled(true);
assertEquals(serverName, request.getServerName());
assertEquals(connector.getLocalPort(), request.getServerPort());
assertEquals(serverName + ":" + connector.getLocalPort(), request.getHeader("Host"));
response.sendRedirect(redirectPath);
}
});
server.start();
try
{
try (Socket socket = new Socket("localhost", connector.getLocalPort()))
{
try (OutputStream output = socket.getOutputStream())
{
String request =
"GET / HTTP/1.0\r\n" +
"\r\n";
output.write(request.getBytes(StandardCharsets.UTF_8));
output.flush();
HttpTester.Input input = HttpTester.from(socket.getInputStream());
HttpTester.Response response = HttpTester.parseResponse(input);
String location = response.get("location");
assertNotNull(location);
String schemePrefix = "http://";
assertTrue(location.startsWith(schemePrefix));
assertTrue(location.endsWith(redirectPath));
String hostPort = location.substring(schemePrefix.length(), location.length() - redirectPath.length());
assertEquals(serverName + ":" + connector.getLocalPort(), hostPort);
}
}
}
finally
{
server.stop();
}
}
} }