diff --git a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java index c327e73588e..7f619611dd9 100644 --- a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java +++ b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java @@ -56,6 +56,7 @@ import org.eclipse.jetty.util.Jetty; import org.eclipse.jetty.util.ProcessorUtils; import org.eclipse.jetty.util.Promise; import org.eclipse.jetty.util.SocketAddressResolver; +import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.component.ContainerLifeCycle; @@ -1099,11 +1100,17 @@ public class HttpClient extends ContainerLifeCycle return proxyConfig; } + /** + * Return a normalized port suitable for use by Origin and Address + * @param scheme the scheme to use for the default port (if port is unspecified) + * @param port the port (0 or negative means the port is unspecified) + * @return the normalized port. + */ public static int normalizePort(String scheme, int port) { if (port > 0) return port; - return HttpScheme.getDefaultPort(scheme); + return URIUtil.getDefaultPortForScheme(scheme); } public ClientConnectionFactory newSslClientConnectionFactory(SslContextFactory.Client sslContextFactory, ClientConnectionFactory connectionFactory) diff --git a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRedirector.java b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRedirector.java index 2a9225e9ee0..15e62727a9f 100644 --- a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRedirector.java +++ b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRedirector.java @@ -30,6 +30,7 @@ import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.util.NanoTime; +import org.eclipse.jetty.util.URIUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -278,7 +279,7 @@ public class HttpRedirector Matcher matcher = URI_PATTERN.matcher(location); if (matcher.matches()) { - String scheme = matcher.group(2); + String scheme = URIUtil.normalizeScheme(matcher.group(2)); String authority = matcher.group(3); String path = matcher.group(4); String query = matcher.group(5); diff --git a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/Origin.java b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/Origin.java index 2f838969d6c..73b56b8a921 100644 --- a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/Origin.java +++ b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/Origin.java @@ -20,6 +20,7 @@ import java.util.Map; import java.util.Objects; import org.eclipse.jetty.client.transport.HttpClientTransportDynamic; +import org.eclipse.jetty.http.HttpURI; import org.eclipse.jetty.io.ClientConnectionFactory; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.util.HostPort; @@ -74,7 +75,7 @@ public class Origin public Origin(String scheme, Address address, Object tag, Protocol protocol) { - this.scheme = Objects.requireNonNull(scheme); + this.scheme = URIUtil.normalizeScheme(Objects.requireNonNull(scheme)); this.address = address; this.tag = tag; this.protocol = protocol; @@ -122,9 +123,7 @@ public class Origin public String asString() { - StringBuilder result = new StringBuilder(); - URIUtil.appendSchemeHostPort(result, scheme, address.host, address.port); - return result.toString(); + return HttpURI.from(scheme, address.host, address.port, null).asString(); } @Override diff --git a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/HttpDestination.java b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/HttpDestination.java index 4c61e00350b..ae55652baf0 100644 --- a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/HttpDestination.java +++ b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/HttpDestination.java @@ -39,6 +39,7 @@ import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.HostPort; import org.eclipse.jetty.util.NanoTime; import org.eclipse.jetty.util.Promise; +import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.component.ContainerLifeCycle; @@ -80,7 +81,8 @@ public class HttpDestination extends ContainerLifeCycle implements Destination, String host = HostPort.normalizeHost(getHost()); int port = getPort(); - if (port != HttpScheme.getDefaultPort(getScheme())) + String scheme = getScheme(); + if (port != URIUtil.getDefaultPortForScheme(scheme)) host += ":" + port; hostField = new HttpField(HttpHeader.HOST, host); diff --git a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/HttpRequest.java b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/HttpRequest.java index 86cb57949f8..8471655ef6f 100644 --- a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/HttpRequest.java +++ b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/HttpRequest.java @@ -55,6 +55,7 @@ import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpURI; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.util.Fields; import org.eclipse.jetty.util.NanoTime; @@ -122,9 +123,7 @@ public class HttpRequest implements Request { if (newURI == null) { - StringBuilder builder = new StringBuilder(64); - URIUtil.appendSchemeHostPort(builder, getScheme(), getHost(), getPort()); - newURI = URI.create(builder.toString()); + newURI = HttpURI.from(getScheme(), getHost(), getPort(), null).toURI(); } HttpRequest newRequest = copyInstance(newURI); @@ -184,7 +183,7 @@ public class HttpRequest implements Request @Override public Request scheme(String scheme) { - this.scheme = scheme; + this.scheme = URIUtil.normalizeScheme(scheme); this.uri = null; return this; } diff --git a/jetty-core/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientURITest.java b/jetty-core/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientURITest.java index 0a328e6cd87..13089955a7e 100644 --- a/jetty-core/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientURITest.java +++ b/jetty-core/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientURITest.java @@ -32,13 +32,13 @@ import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.http.HttpURI; import org.eclipse.jetty.http.UriCompliance; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.Response; import org.eclipse.jetty.toolchain.test.Net; import org.eclipse.jetty.util.Fields; import org.eclipse.jetty.util.StringUtil; -import org.eclipse.jetty.util.URIUtil; import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ArgumentsSource; @@ -68,9 +68,8 @@ public class HttpClientURITest extends AbstractHttpClientServerTest .timeout(5, TimeUnit.SECONDS); assertEquals(host, request.getHost()); - StringBuilder uri = new StringBuilder(); - URIUtil.appendSchemeHostPort(uri, scenario.getScheme(), host, connector.getLocalPort()); - assertEquals(uri.toString(), request.getURI().toString()); + HttpURI httpURI = HttpURI.from(scenario.getScheme(), host, connector.getLocalPort(), null); + assertEquals(httpURI.asString(), request.getURI().toString()); assertEquals(HttpStatus.OK_200, request.send().getStatus()); } diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/test/XmlConfiguredJetty.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/test/XmlConfiguredJetty.java index 69bea424aa5..50d88bcc2aa 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/test/XmlConfiguredJetty.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/test/XmlConfiguredJetty.java @@ -35,6 +35,7 @@ import java.util.Properties; import java.util.stream.Collectors; import org.eclipse.jetty.http.HttpScheme; +import org.eclipse.jetty.http.HttpURI; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.NetworkConnector; @@ -249,9 +250,7 @@ public class XmlConfiguredJetty public URI getServerURI() throws UnknownHostException { - StringBuilder uri = new StringBuilder(); - URIUtil.appendSchemeHostPort(uri, getScheme(), InetAddress.getLocalHost().getHostAddress(), getServerPort()); - return URI.create(uri.toString()); + return HttpURI.from(getScheme(), InetAddress.getLocalHost().getHostAddress(), getServerPort(), null).toURI(); } public Path getJettyBasePath() @@ -332,7 +331,7 @@ public class XmlConfiguredJetty public void setScheme(String scheme) { - this._scheme = scheme; + this._scheme = URIUtil.normalizeScheme(scheme); } public void start() throws Exception diff --git a/jetty-core/jetty-fcgi/jetty-fcgi-proxy/src/main/java/org/eclipse/jetty/fcgi/proxy/FastCGIProxyHandler.java b/jetty-core/jetty-fcgi/jetty-fcgi-proxy/src/main/java/org/eclipse/jetty/fcgi/proxy/FastCGIProxyHandler.java index e893b54ab14..5c5d2f88ce7 100644 --- a/jetty-core/jetty-fcgi/jetty-fcgi-proxy/src/main/java/org/eclipse/jetty/fcgi/proxy/FastCGIProxyHandler.java +++ b/jetty-core/jetty-fcgi/jetty-fcgi-proxy/src/main/java/org/eclipse/jetty/fcgi/proxy/FastCGIProxyHandler.java @@ -38,6 +38,7 @@ import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Response; import org.eclipse.jetty.server.handler.TryPathsHandler; import org.eclipse.jetty.util.Callback; +import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -317,7 +318,7 @@ public class FastCGIProxyHandler extends ProxyHandler.Reverse // If the Host header is missing, add it. if (!proxyToServerRequest.getHeaders().contains(HttpHeader.HOST)) { - if (serverPort != HttpScheme.getDefaultPort(scheme)) + if (serverPort != URIUtil.getDefaultPortForScheme(scheme)) serverName += ":" + serverPort; String host = serverName; proxyToServerRequest.headers(headers -> headers diff --git a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/HttpScheme.java b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/HttpScheme.java index 8505cfb15e4..77dbb18b349 100644 --- a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/HttpScheme.java +++ b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/HttpScheme.java @@ -17,16 +17,17 @@ import java.nio.ByteBuffer; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Index; +import org.eclipse.jetty.util.URIUtil; /** * HTTP and WebSocket Schemes */ public enum HttpScheme { - HTTP("http", 80), - HTTPS("https", 443), - WS("ws", 80), - WSS("wss", 443); + HTTP("http"), + HTTPS("https"), + WS("ws"), + WSS("wss"); public static final Index CACHE = new Index.Builder() .caseSensitive(false) @@ -37,11 +38,11 @@ public enum HttpScheme private final ByteBuffer _buffer; private final int _defaultPort; - HttpScheme(String s, int port) + HttpScheme(String s) { _string = s; _buffer = BufferUtil.toBuffer(s); - _defaultPort = port; + _defaultPort = URIUtil.getDefaultPortForScheme(s); } public ByteBuffer asByteBuffer() @@ -75,16 +76,29 @@ public enum HttpScheme return _string; } + /** + * Get the default port for a URI scheme + * @param scheme The scheme + * @return Default port for URI scheme + * @deprecated Use {@link URIUtil#getDefaultPortForScheme(String)} + */ + @Deprecated public static int getDefaultPort(String scheme) { - HttpScheme httpScheme = scheme == null ? null : CACHE.get(scheme); - return httpScheme == null ? HTTP.getDefaultPort() : httpScheme.getDefaultPort(); + return URIUtil.getDefaultPortForScheme(scheme); } + /** + * Normalize a port for a URI scheme + * @param scheme the scheme + * @param port the port to normalize + * @return The normalized port + * @deprecated Use {@link URIUtil#normalizePortForScheme(String, int)} + */ + @Deprecated public static int normalizePort(String scheme, int port) { - HttpScheme httpScheme = scheme == null ? null : CACHE.get(scheme); - return httpScheme == null ? port : httpScheme.normalizePort(port); + return URIUtil.normalizePortForScheme(scheme, port); } public static boolean isSecure(String scheme) diff --git a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java index aa8d827d331..57b45226527 100644 --- a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java +++ b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java @@ -151,6 +151,11 @@ public interface HttpURI return new Mutable(scheme, host, port, pathQuery).asImmutable(); } + static Immutable from(String scheme, String host, int port, String path, String query, String fragment) + { + return new Immutable(scheme, host, port, path, query, fragment); + } + Immutable asImmutable(); String asString(); @@ -302,18 +307,19 @@ public interface HttpURI _violations = Collections.unmodifiableSet(EnumSet.copyOf(builder._violations)); } - private Immutable(String uri) + private Immutable(String scheme, String host, int port, String path, String query, String fragment) { - _scheme = null; + _uri = null; + + _scheme = URIUtil.normalizeScheme(scheme); _user = null; - _host = null; - _port = -1; - _path = uri; + _host = host; + _port = port; + _path = path; + _canonicalPath = _path == null ? null : URIUtil.canonicalPath(_path); _param = null; - _query = null; - _fragment = null; - _uri = uri; - _canonicalPath = null; + _query = query; + _fragment = fragment; } @Override @@ -340,19 +346,26 @@ public interface HttpURI out.append(_host); } - if (_port > 0) - out.append(':').append(_port); + int normalizedPort = URIUtil.normalizePortForScheme(_scheme, _port); + if (normalizedPort > 0) + out.append(':').append(normalizedPort); + + // we output even if the input is an empty string (to match java URI / URL behaviors) + boolean hasQuery = _query != null; + boolean hasFragment = _fragment != null; if (_path != null) out.append(_path); + else if (hasQuery || hasFragment) + out.append('/'); - if (_query != null) + if (hasQuery) out.append('?').append(_query); - if (_fragment != null) + if (hasFragment) out.append('#').append(_fragment); - if (out.length() > 0) + if (!out.isEmpty()) _uri = out.toString(); else _uri = ""; @@ -504,7 +517,7 @@ public interface HttpURI { try { - return new URI(_scheme, null, _host, _port, _path, _query == null ? null : UrlEncoded.decodeString(_query), _fragment); + return new URI(_scheme, null, _host, URIUtil.normalizePortForScheme(_scheme, _port), _path, _query == null ? null : UrlEncoded.decodeString(_query), _fragment); } catch (URISyntaxException x) { @@ -616,7 +629,7 @@ public interface HttpURI { _uri = null; - _scheme = uri.getScheme(); + _scheme = URIUtil.normalizeScheme(uri.getScheme()); _host = uri.getHost(); if (_host == null && uri.getRawSchemeSpecificPart().startsWith("//")) _host = ""; @@ -631,13 +644,9 @@ public interface HttpURI private Mutable(String scheme, String host, int port, String pathQuery) { - // TODO review if this should be here - if (port == HttpScheme.getDefaultPort(scheme)) - port = 0; - _uri = null; - _scheme = scheme; + _scheme = URIUtil.normalizeScheme(scheme); _host = host; _port = port; @@ -960,7 +969,7 @@ public interface HttpURI public Mutable scheme(String scheme) { - _scheme = scheme; + _scheme = URIUtil.normalizeScheme(scheme); _uri = null; return this; } @@ -1122,7 +1131,7 @@ public interface HttpURI { case ':': // must have been a scheme - _scheme = uri.substring(mark, i); + _scheme = URIUtil.normalizeScheme(uri.substring(mark, i)); // Start again with scheme set state = State.START; break; diff --git a/jetty-core/jetty-http/src/test/java/org/eclipse/jetty/http/HttpURITest.java b/jetty-core/jetty-http/src/test/java/org/eclipse/jetty/http/HttpURITest.java index 5579dcd4ef4..aa54abe7a6a 100644 --- a/jetty-core/jetty-http/src/test/java/org/eclipse/jetty/http/HttpURITest.java +++ b/jetty-core/jetty-http/src/test/java/org/eclipse/jetty/http/HttpURITest.java @@ -940,4 +940,141 @@ public class HttpURITest { assertThat(UriCompliance.from(UriCompliance.DEFAULT.getName()), sameInstance(UriCompliance.DEFAULT)); } + + public static Stream concatNormalizedURIShortCases() + { + return Stream.of( + // Default behaviors of stripping a port number based on scheme + Arguments.of("http", "example.org", 80, "http://example.org"), + Arguments.of("https", "example.org", 443, "https://example.org"), + Arguments.of("ws", "example.org", 80, "ws://example.org"), + Arguments.of("wss", "example.org", 443, "wss://example.org"), + // Mismatches between scheme and port + Arguments.of("http", "example.org", 443, "http://example.org:443"), + Arguments.of("https", "example.org", 80, "https://example.org:80"), + Arguments.of("ws", "example.org", 443, "ws://example.org:443"), + Arguments.of("wss", "example.org", 80, "wss://example.org:80"), + // Odd ports + Arguments.of("http", "example.org", 12345, "http://example.org:12345"), + Arguments.of("https", "example.org", 54321, "https://example.org:54321"), + Arguments.of("ws", "example.org", 6666, "ws://example.org:6666"), + Arguments.of("wss", "example.org", 7777, "wss://example.org:7777"), + // Non-lowercase Schemes + Arguments.of("HTTP", "example.org", 8181, "http://example.org:8181"), + Arguments.of("hTTps", "example.org", 443, "https://example.org"), + Arguments.of("WS", "example.org", 8282, "ws://example.org:8282"), + Arguments.of("wsS", "example.org", 8383, "wss://example.org:8383"), + // Undefined Ports + Arguments.of("http", "example.org", 0, "http://example.org"), + Arguments.of("https", "example.org", -1, "https://example.org"), + Arguments.of("ws", "example.org", -80, "ws://example.org"), + Arguments.of("wss", "example.org", -2, "wss://example.org"), + // Unrecognized (non-http) schemes + Arguments.of("foo", "example.org", 0, "foo://example.org"), + Arguments.of("ssh", "example.org", 22, "ssh://example.org"), + Arguments.of("ftp", "example.org", 21, "ftp://example.org"), + Arguments.of("ssh", "example.org", 2222, "ssh://example.org:2222"), + Arguments.of("ftp", "example.org", 2121, "ftp://example.org:2121"), + Arguments.of("file", "etc", -1, "file://etc") + ); + } + + @ParameterizedTest + @MethodSource("concatNormalizedURIShortCases") + public void testFromShortAsStringNormalized(String scheme, String server, int port, String expectedStr) + { + HttpURI httpURI = HttpURI.from(scheme, server, port, null); + assertThat(httpURI.asString(), is(expectedStr)); + } + + public static Stream concatNormalizedURICases() + { + return Stream.of( + // Default behaviors of stripping a port number based on scheme + Arguments.of("http", "example.org", 80, "/", null, null, "http://example.org/"), + Arguments.of("https", "example.org", 443, "/", null, null, "https://example.org/"), + Arguments.of("ws", "example.org", 80, "/", null, null, "ws://example.org/"), + Arguments.of("wss", "example.org", 443, "/", null, null, "wss://example.org/"), + // Mismatches between scheme and port + Arguments.of("http", "example.org", 443, "/", null, null, "http://example.org:443/"), + Arguments.of("https", "example.org", 80, "/", null, null, "https://example.org:80/"), + Arguments.of("ws", "example.org", 443, "/", null, null, "ws://example.org:443/"), + Arguments.of("wss", "example.org", 80, "/", null, null, "wss://example.org:80/"), + // Odd ports + Arguments.of("http", "example.org", 12345, "/", null, null, "http://example.org:12345/"), + Arguments.of("https", "example.org", 54321, "/", null, null, "https://example.org:54321/"), + Arguments.of("ws", "example.org", 6666, "/", null, null, "ws://example.org:6666/"), + Arguments.of("wss", "example.org", 7777, "/", null, null, "wss://example.org:7777/"), + // Non-lowercase Schemes + Arguments.of("HTTP", "example.org", 8181, "/", null, null, "http://example.org:8181/"), + Arguments.of("hTTps", "example.org", 443, "/", null, null, "https://example.org/"), + Arguments.of("WS", "example.org", 8282, "/", null, null, "ws://example.org:8282/"), + Arguments.of("wsS", "example.org", 8383, "/", null, null, "wss://example.org:8383/"), + // Undefined Ports + Arguments.of("http", "example.org", 0, "/", null, null, "http://example.org/"), + Arguments.of("https", "example.org", -1, "/", null, null, "https://example.org/"), + Arguments.of("ws", "example.org", -80, "/", null, null, "ws://example.org/"), + Arguments.of("wss", "example.org", -2, "/", null, null, "wss://example.org/"), + // Unrecognized (non-http) schemes + Arguments.of("foo", "example.org", 0, "/", null, null, "foo://example.org/"), + Arguments.of("ssh", "example.org", 22, "/", null, null, "ssh://example.org/"), + Arguments.of("ftp", "example.org", 21, "/", null, null, "ftp://example.org/"), + Arguments.of("ssh", "example.org", 2222, "/", null, null, "ssh://example.org:2222/"), + Arguments.of("ftp", "example.org", 2121, "/", null, null, "ftp://example.org:2121/"), + // Path choices + Arguments.of("http", "example.org", 0, "/a/b/c/d", null, null, "http://example.org/a/b/c/d"), + Arguments.of("http", "example.org", 0, "/a%20b/c%20d", null, null, "http://example.org/a%20b/c%20d"), + // Query specified + Arguments.of("http", "example.org", 0, "/", "a=b", null, "http://example.org/?a=b"), + Arguments.of("http", "example.org", 0, "/documentation/latest/", "a=b", null, "http://example.org/documentation/latest/?a=b"), + Arguments.of("http", "example.org", 0, null, "a=b", null, "http://example.org/?a=b"), + Arguments.of("http", "example.org", 0, null, "", null, "http://example.org/?"), + // Fragment specified + Arguments.of("http", "example.org", 0, "/", null, "", "http://example.org/#"), + Arguments.of("http", "example.org", 0, "/", null, "toc", "http://example.org/#toc"), + Arguments.of("http", "example.org", 0, null, null, "toc", "http://example.org/#toc"), + // Empty query & fragment - behavior matches java URI and URL + Arguments.of("http", "example.org", 0, null, "", "", "http://example.org/?#") + ); + } + + @ParameterizedTest + @MethodSource("concatNormalizedURICases") + public void testFromAsStringNormalized(String scheme, String server, int port, String path, String query, String fragment, String expectedStr) + { + HttpURI httpURI = HttpURI.from(scheme, server, port, path, query, fragment); + assertThat(httpURI.asString(), is(expectedStr)); + } + + /** + * Tests of parameters that result in undesired behaviors. + * {@link HttpURI#from(String, String, int, String)} + */ + public static Stream fromBad() + { + return Stream.of( + // bad schemes + Arguments.of(null, "example.org", 0, "//example.org"), + Arguments.of("", "example.org", 0, "://example.org"), + Arguments.of("\t", "example.org", 0, "\t://example.org"), + Arguments.of(" ", "example.org", 0, " ://example.org"), + // bad ports + Arguments.of("http", "example.org", 1_000_000, "http://example.org:1000000"), + // bad ports + Arguments.of("ws", "example.org", -222333, "ws://example.org"), // negative port same as -1, i.e. not set. + // bad servers + Arguments.of("http", null, 0, "http:"), + Arguments.of("http", "", 0, "http://"), + Arguments.of("http", "\t", 0, "http://\t"), + Arguments.of("http", " ", 0, "http:// ") + ); + } + + @ParameterizedTest + @MethodSource("fromBad") + public void testFromBad(String scheme, String server, int port, String expectedStr) + { + HttpURI httpURI = HttpURI.from(scheme, server, port, null); + assertThat(httpURI.asString(), is(expectedStr)); + } } diff --git a/jetty-core/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdAuthenticator.java b/jetty-core/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdAuthenticator.java index d175e537ddb..16fab5da110 100644 --- a/jetty-core/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdAuthenticator.java +++ b/jetty-core/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdAuthenticator.java @@ -672,8 +672,7 @@ public class OpenIdAuthenticator extends LoginAuthenticator private String getRedirectUri(Request request) { - final StringBuffer redirectUri = new StringBuffer(128); - URIUtil.appendSchemeHostPort(redirectUri, request.getHttpURI().getScheme(), + final StringBuilder redirectUri = URIUtil.newURIBuilder(request.getHttpURI().getScheme(), Request.getServerName(request), Request.getServerPort(request)); redirectUri.append(URIUtil.addPaths(request.getContext().getContextPath(), _redirectPath)); return redirectUri.toString(); diff --git a/jetty-core/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/ForwardedSchemeHeaderRule.java b/jetty-core/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/ForwardedSchemeHeaderRule.java index 4194a42735d..54d7f8a3987 100644 --- a/jetty-core/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/ForwardedSchemeHeaderRule.java +++ b/jetty-core/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/ForwardedSchemeHeaderRule.java @@ -14,6 +14,7 @@ package org.eclipse.jetty.rewrite.handler; import org.eclipse.jetty.http.HttpURI; +import org.eclipse.jetty.util.URIUtil; /** *

Sets the request URI scheme, by default {@code https}.

@@ -29,7 +30,7 @@ public class ForwardedSchemeHeaderRule extends HeaderRule public void setScheme(String scheme) { - _scheme = scheme; + _scheme = URIUtil.normalizeScheme(scheme); } @Override diff --git a/jetty-core/jetty-security/src/test/java/org/eclipse/jetty/security/SecurityHandlerTest.java b/jetty-core/jetty-security/src/test/java/org/eclipse/jetty/security/SecurityHandlerTest.java index aabdc981f4e..22dbf23a5a4 100644 --- a/jetty-core/jetty-security/src/test/java/org/eclipse/jetty/security/SecurityHandlerTest.java +++ b/jetty-core/jetty-security/src/test/java/org/eclipse/jetty/security/SecurityHandlerTest.java @@ -134,7 +134,7 @@ public class SecurityHandlerTest response = _connector.getResponse("GET /ctx/confidential/info HTTP/1.0\r\n\r\n"); assertThat(response, containsString("HTTP/1.1 302 Found")); - assertThat(response, containsString("Location: BWTP://")); + assertThat(response, containsString("Location: bwtp://")); assertThat(response, containsString(":9999")); assertThat(response, not(containsString("OK"))); @@ -161,7 +161,7 @@ public class SecurityHandlerTest response = _connector.getResponse("GET /ctx/confidential/info HTTP/1.0\r\n\r\n"); assertThat(response, containsString("HTTP/1.1 302 Found")); - assertThat(response, containsString("Location: BWTP://")); + assertThat(response, containsString("Location: bwtp://")); assertThat(response, containsString(":9999")); assertThat(response, not(containsString("OK"))); diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/HostHeaderCustomizer.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/HostHeaderCustomizer.java index 4c1fdab0276..d55b0bb0022 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/HostHeaderCustomizer.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/HostHeaderCustomizer.java @@ -16,9 +16,9 @@ package org.eclipse.jetty.server; import org.eclipse.jetty.http.HostPortHttpField; 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.HttpVersion; +import org.eclipse.jetty.util.URIUtil; /** * Adds a missing {@code Host} header (for example, HTTP 1.0 or 2.0 requests). @@ -58,7 +58,7 @@ public class HostHeaderCustomizer implements HttpConfiguration.Customizer return request; String host = serverName == null ? Request.getServerName(request) : serverName; - int port = HttpScheme.normalizePort(request.getHttpURI().getScheme(), serverPort == 0 ? Request.getServerPort(request) : serverPort); + int port = URIUtil.normalizePortForScheme(request.getHttpURI().getScheme(), serverPort == 0 ? Request.getServerPort(request) : serverPort); HttpURI uri = (serverName != null || serverPort > 0) ? HttpURI.build(request.getHttpURI()).authority(host, port).asImmutable() diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java index 0812943338c..69d9396470e 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java @@ -32,6 +32,7 @@ import org.eclipse.jetty.http.UriCompliance; import org.eclipse.jetty.util.HostPort; import org.eclipse.jetty.util.Index; import org.eclipse.jetty.util.Jetty; +import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.component.Dumpable; @@ -476,7 +477,7 @@ public class HttpConfiguration implements Dumpable */ public void setSecureScheme(String secureScheme) { - _secureScheme = secureScheme; + _secureScheme = URIUtil.normalizeScheme(secureScheme); } /** diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java index d83973dd555..4e8b26a322a 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java @@ -356,10 +356,7 @@ public interface Response extends Content.Sink if (!request.getConnectionMetaData().getHttpConfiguration().isRelativeRedirectAllowed()) { // make the location an absolute URI - StringBuilder url = new StringBuilder(128); - URIUtil.appendSchemeHostPort(url, uri.getScheme(), Request.getServerName(request), Request.getServerPort(request)); - url.append(location); - location = url.toString(); + location = URIUtil.newURI(uri.getScheme(), Request.getServerName(request), Request.getServerPort(request), location, null); } } return location; diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpConnection.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpConnection.java index 1d938ee8f6f..ac189270bd6 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpConnection.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpConnection.java @@ -70,6 +70,7 @@ import org.eclipse.jetty.util.HostPort; import org.eclipse.jetty.util.IteratingCallback; import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.TypeUtil; +import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.thread.Invocable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -1218,7 +1219,7 @@ public class HttpConnection extends AbstractMetaDataConnection implements Runnab { HostPort hostPort = _hostField == null ? getServerAuthority() : _hostField.getHostPort(); int port = hostPort.getPort(); - if (port == HttpScheme.getDefaultPort(_uri.getScheme())) + if (port == URIUtil.getDefaultPortForScheme(_uri.getScheme())) port = -1; _uri.authority(hostPort.getHost(), port); } diff --git a/jetty-core/jetty-session/src/main/java/org/eclipse/jetty/session/AbstractSessionManager.java b/jetty-core/jetty-session/src/main/java/org/eclipse/jetty/session/AbstractSessionManager.java index 09b30e721b0..9ceac663f0d 100644 --- a/jetty-core/jetty-session/src/main/java/org/eclipse/jetty/session/AbstractSessionManager.java +++ b/jetty-core/jetty-session/src/main/java/org/eclipse/jetty/session/AbstractSessionManager.java @@ -666,7 +666,10 @@ public abstract class AbstractSessionManager extends ContainerLifeCycle implemen path = (path == null ? "" : path); int port = httpURI.getPort(); if (port < 0) - port = HttpScheme.getDefaultPort(httpURI.getScheme()); + { + String scheme = httpURI.getScheme(); + port = URIUtil.getDefaultPortForScheme(scheme); + } // Is it the same server? if (!Request.getServerName(request).equalsIgnoreCase(httpURI.getHost())) diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java index 658655e2a1b..f663a228569 100644 --- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java +++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java @@ -1359,6 +1359,19 @@ public final class URIUtil return true; } + /** + * Create a new URI from the arguments, handling IPv6 host encoding and default ports + * + * @param scheme the URI scheme + * @param server the URI server + * @param port the URI port + * @return A String URI + */ + public static String newURI(String scheme, String server, int port) + { + return newURI(scheme, server, port, null, null); + } + /** * Create a new URI from the arguments, handling IPv6 host encoding and default ports * @@ -1370,11 +1383,36 @@ public final class URIUtil * @return A String URI */ public static String newURI(String scheme, String server, int port, String path, String query) + { + return newURI(scheme, server, port, path, query, null); + } + + /** + * Create a new URI from the arguments, handling IPv6 host encoding and default ports + * + * @param scheme the URI scheme + * @param server the URI server + * @param port the URI port + * @param path the URI path + * @param query the URI query + * @param fragment the URI fragment + * @return A String URI + */ + public static String newURI(String scheme, String server, int port, String path, String query, String fragment) { StringBuilder builder = newURIBuilder(scheme, server, port); - builder.append(path); - if (query != null && query.length() > 0) + // check only for null, as empty query/fragment have meaning. + // this also matches the behavior of java URL & URI + boolean hasQuery = query != null; + boolean hasFragment = fragment != null; + if (StringUtil.isNotBlank(path)) + builder.append(path); + else if (hasQuery || hasFragment) + builder.append('/'); + if (hasQuery) builder.append('?').append(query); + if (hasFragment) + builder.append('#').append(fragment); return builder.toString(); } @@ -1388,7 +1426,7 @@ public final class URIUtil */ public static StringBuilder newURIBuilder(String scheme, String server, int port) { - StringBuilder builder = new StringBuilder(); + StringBuilder builder = new StringBuilder(128); appendSchemeHostPort(builder, scheme, server, port); return builder; } @@ -1403,28 +1441,11 @@ public final class URIUtil */ public static void appendSchemeHostPort(StringBuilder url, String scheme, String server, int port) { + scheme = normalizeScheme(scheme); url.append(scheme).append("://").append(HostPort.normalizeHost(server)); - + port = normalizePortForScheme(scheme, port); if (port > 0) - { - switch (scheme) - { - case "ws": - case "http": - if (port != 80) - url.append(':').append(port); - break; - - case "wss": - case "https": - if (port != 443) - url.append(':').append(port); - break; - - default: - url.append(':').append(port); - } - } + url.append(':').append(port); } /** @@ -1434,31 +1455,16 @@ public final class URIUtil * @param scheme the URI scheme * @param server the URI server * @param port the URI port + * @deprecated Use {@link #appendSchemeHostPort(StringBuilder, String, String, int)} */ + @Deprecated public static void appendSchemeHostPort(StringBuffer url, String scheme, String server, int port) { + scheme = normalizeScheme(scheme); url.append(scheme).append("://").append(HostPort.normalizeHost(server)); - + port = normalizePortForScheme(scheme, port); if (port > 0) - { - switch (scheme) - { - case "ws": - case "http": - if (port != 80) - url.append(':').append(port); - break; - - case "wss": - case "https": - if (port != 443) - url.append(':').append(port); - break; - - default: - url.append(':').append(port); - } - } + url.append(':').append(port); } /** @@ -1925,4 +1931,52 @@ public final class URIUtil .map(URIUtil::unwrapContainer) .map(URIUtil::correctFileURI); } + + private static final Index DEFAULT_PORT_FOR_SCHEME = new Index.Builder() + .caseSensitive(false) + .with("ftp", 21) + .with("ssh", 22) + .with("telnet", 23) + .with("smtp", 25) + .with("http", 80) + .with("ws", 80) + .with("https", 443) + .with("wss", 443) + .build(); + + /** + * Get the default port for some well known schemes + * @param scheme The scheme + * @return The default port or -1 if not known + */ + public static int getDefaultPortForScheme(String scheme) + { + if (scheme == null) + return -1; + Integer port = DEFAULT_PORT_FOR_SCHEME.get(scheme); + return port == null ? -1 : port; + } + + /** + * Normalize the scheme + * @param scheme The scheme to normalize + * @return The normalized version of the scheme + */ + public static String normalizeScheme(String scheme) + { + return scheme == null ? null : StringUtil.asciiToLowerCase(scheme); + } + + /** + * Normalize a port for a given scheme + * @param scheme The scheme + * @param port The port to normalize + * @return The port number or 0 if provided port was less than 0 or was equal to the default port for the scheme + */ + public static int normalizePortForScheme(String scheme, int port) + { + if (port <= 0) + return 0; + return port == getDefaultPortForScheme(scheme) ? 0 : port; + } } diff --git a/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilTest.java b/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilTest.java index 416b2f6e228..16a6e55e448 100644 --- a/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilTest.java +++ b/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilTest.java @@ -34,6 +34,7 @@ import org.junit.jupiter.api.condition.OS; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; @@ -47,6 +48,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assumptions.assumeTrue; /** * URIUtil Tests. @@ -1186,7 +1188,7 @@ public class URIUtilTest assertThat(uris, contains(expected)); } - public static Stream appendSchemeHostPortCases() + public static Stream schemeHostPortShortCases() { return Stream.of( // Default behaviors of stripping a port number based on scheme @@ -1203,12 +1205,29 @@ public class URIUtilTest Arguments.of("http", "example.org", 12345, "http://example.org:12345"), Arguments.of("https", "example.org", 54321, "https://example.org:54321"), Arguments.of("ws", "example.org", 6666, "ws://example.org:6666"), - Arguments.of("wss", "example.org", 7777, "wss://example.org:7777") + Arguments.of("wss", "example.org", 7777, "wss://example.org:7777"), + // Non-lowercase Schemes + Arguments.of("HTTP", "example.org", 8181, "http://example.org:8181"), + Arguments.of("hTTps", "example.org", 443, "https://example.org"), + Arguments.of("WS", "example.org", 8282, "ws://example.org:8282"), + Arguments.of("wsS", "example.org", 8383, "wss://example.org:8383"), + // Undefined Ports + Arguments.of("http", "example.org", 0, "http://example.org"), + Arguments.of("https", "example.org", -1, "https://example.org"), + Arguments.of("ws", "example.org", -80, "ws://example.org"), + Arguments.of("wss", "example.org", -2, "wss://example.org"), + // Unrecognized (non-http) schemes + Arguments.of("foo", "example.org", 0, "foo://example.org"), + Arguments.of("ssh", "example.org", 22, "ssh://example.org"), + Arguments.of("ftp", "example.org", 21, "ftp://example.org"), + Arguments.of("ssh", "example.org", 2222, "ssh://example.org:2222"), + Arguments.of("ftp", "example.org", 2121, "ftp://example.org:2121"), + Arguments.of("file", "etc", -1, "file://etc") ); } @ParameterizedTest - @MethodSource("appendSchemeHostPortCases") + @MethodSource("schemeHostPortShortCases") public void testAppendSchemeHostPortBuilder(String scheme, String server, int port, String expectedStr) { StringBuilder actual = new StringBuilder(); @@ -1217,11 +1236,211 @@ public class URIUtilTest } @ParameterizedTest - @MethodSource("appendSchemeHostPortCases") + @MethodSource("schemeHostPortShortCases") public void testAppendSchemeHostPortBuffer(String scheme, String server, int port, String expectedStr) { StringBuffer actual = new StringBuffer(); URIUtil.appendSchemeHostPort(actual, scheme, server, port); assertEquals(expectedStr, actual.toString()); } + + public static List getNewURICases() + { + List cases = new ArrayList<>(); + + cases.addAll(List.of( + // Default behaviors of stripping a port number based on scheme + // Query specified + Arguments.of("http", "example.org", 0, "/", "a=b", null, "http://example.org/?a=b"), + Arguments.of("http", "example.org", 0, "/documentation/latest/", "a=b", null, "http://example.org/documentation/latest/?a=b"), + Arguments.of("http", "example.org", 0, null, "a=b", null, "http://example.org/?a=b"), + Arguments.of("http", "example.org", 0, null, "", null, "http://example.org/?") + )); + return cases; + } + + public static List schemeHostPortCases() + { + return List.of( + // Default behaviors of stripping a port number based on scheme + Arguments.of("http", "example.org", 80, "http://example.org"), + Arguments.of("https", "example.org", 443, "https://example.org"), + Arguments.of("ws", "example.org", 80, "ws://example.org"), + Arguments.of("wss", "example.org", 443, "wss://example.org"), + // Mismatches between scheme and port + Arguments.of("http", "example.org", 443, "http://example.org:443"), + Arguments.of("https", "example.org", 80, "https://example.org:80"), + Arguments.of("ws", "example.org", 443, "ws://example.org:443"), + Arguments.of("wss", "example.org", 80, "wss://example.org:80"), + // Odd ports + Arguments.of("http", "example.org", 12345, "http://example.org:12345"), + Arguments.of("https", "example.org", 54321, "https://example.org:54321"), + Arguments.of("ws", "example.org", 6666, "ws://example.org:6666"), + Arguments.of("wss", "example.org", 7777, "wss://example.org:7777"), + // Non-lowercase Schemes + Arguments.of("HTTP", "example.org", 8181, "http://example.org:8181"), + Arguments.of("hTTps", "example.org", 443, "https://example.org"), + Arguments.of("WS", "example.org", 8282, "ws://example.org:8282"), + Arguments.of("wsS", "example.org", 8383, "wss://example.org:8383"), + // Undefined Ports + Arguments.of("http", "example.org", 0, "http://example.org"), + Arguments.of("https", "example.org", -1, "https://example.org"), + Arguments.of("ws", "example.org", -80, "ws://example.org"), + Arguments.of("wss", "example.org", -2, "wss://example.org"), + // Unrecognized (non-http) schemes + Arguments.of("foo", "example.org", 0, "foo://example.org"), + Arguments.of("ssh", "example.org", 22, "ssh://example.org"), + Arguments.of("ftp", "example.org", 21, "ftp://example.org"), + Arguments.of("ssh", "example.org", 2222, "ssh://example.org:2222"), + Arguments.of("ftp", "example.org", 2121, "ftp://example.org:2121") + ); + } + + public static List schemeHostPortPathCases() + { + List cases = new ArrayList<>(); + + cases.addAll(List.of( + // Default behaviors of stripping a port number based on scheme + Arguments.of("http", "example.org", 80, "/", null, null, "http://example.org/"), + Arguments.of("https", "example.org", 443, "/", null, null, "https://example.org/"), + Arguments.of("ws", "example.org", 80, "/", null, null, "ws://example.org/"), + Arguments.of("wss", "example.org", 443, "/", null, null, "wss://example.org/"), + // Mismatches between scheme and port + Arguments.of("http", "example.org", 443, "/", null, null, "http://example.org:443/"), + Arguments.of("https", "example.org", 80, "/", null, null, "https://example.org:80/"), + Arguments.of("ws", "example.org", 443, "/", null, null, "ws://example.org:443/"), + Arguments.of("wss", "example.org", 80, "/", null, null, "wss://example.org:80/"), + // Odd ports + Arguments.of("http", "example.org", 12345, "/", null, null, "http://example.org:12345/"), + Arguments.of("https", "example.org", 54321, "/", null, null, "https://example.org:54321/"), + Arguments.of("ws", "example.org", 6666, "/", null, null, "ws://example.org:6666/"), + Arguments.of("wss", "example.org", 7777, "/", null, null, "wss://example.org:7777/"), + // Non-lowercase Schemes + Arguments.of("HTTP", "example.org", 8181, "/", null, null, "http://example.org:8181/"), + Arguments.of("hTTps", "example.org", 443, "/", null, null, "https://example.org/"), + Arguments.of("WS", "example.org", 8282, "/", null, null, "ws://example.org:8282/"), + Arguments.of("wsS", "example.org", 8383, "/", null, null, "wss://example.org:8383/"), + // Undefined Ports + Arguments.of("http", "example.org", 0, "/", null, null, "http://example.org/"), + Arguments.of("https", "example.org", -1, "/", null, null, "https://example.org/"), + Arguments.of("ws", "example.org", -80, "/", null, null, "ws://example.org/"), + Arguments.of("wss", "example.org", -2, "/", null, null, "wss://example.org/"), + // Unrecognized (non-http) schemes + Arguments.of("foo", "example.org", 0, "/", null, null, "foo://example.org/"), + Arguments.of("ssh", "example.org", 22, "/", null, null, "ssh://example.org/"), + Arguments.of("ftp", "example.org", 21, "/", null, null, "ftp://example.org/"), + Arguments.of("ssh", "example.org", 2222, "/", null, null, "ssh://example.org:2222/"), + Arguments.of("ftp", "example.org", 2121, "/", null, null, "ftp://example.org:2121/"), + // Path choices + Arguments.of("http", "example.org", 0, "/a/b/c/d", null, null, "http://example.org/a/b/c/d"), + Arguments.of("http", "example.org", 0, "/a%20b/c%20d", null, null, "http://example.org/a%20b/c%20d"), + // Query specified + Arguments.of("http", "example.org", 0, "/", "a=b", null, "http://example.org/?a=b"), + Arguments.of("http", "example.org", 0, "/documentation/latest/", "a=b", null, "http://example.org/documentation/latest/?a=b"), + Arguments.of("http", "example.org", 0, null, "a=b", null, "http://example.org/?a=b"), + Arguments.of("http", "example.org", 0, null, "", null, "http://example.org/?") + )); + return cases; + } + + public static List schemeHostPortFragmentCases() + { + List cases = new ArrayList<>(); + cases.addAll(schemeHostPortPathCases()); + + cases.addAll(List.of( + // Fragment specified + Arguments.of("http", "example.org", 0, "/", null, "", "http://example.org/#"), + Arguments.of("http", "example.org", 0, "/", null, "toc", "http://example.org/#toc"), + Arguments.of("http", "example.org", 0, null, null, "toc", "http://example.org/#toc"), + // Empty query & fragment - behavior matches java URI and URL + Arguments.of("http", "example.org", 0, null, "", "", "http://example.org/?#") + )); + + return cases; + } + + @ParameterizedTest + @MethodSource("schemeHostPortCases") + public void testNewURIShort(String scheme, String server, int port, String expectedStr) + { + String actual = URIUtil.newURI(scheme, server, port); + assertEquals(expectedStr, actual.toString()); + } + + @ParameterizedTest + @MethodSource("schemeHostPortPathCases") + public void testNewURI(String scheme, String server, int port, String path, String query, String fragment, String expectedStr) + { + assumeTrue(StringUtil.isBlank(fragment), "Skip tests with fragments, as this newURI doesn't have them"); + String actual = URIUtil.newURI(scheme, server, port, path, query); + assertEquals(expectedStr, actual.toString()); + } + + @ParameterizedTest + @MethodSource("schemeHostPortFragmentCases") + public void testNewURIFragment(String scheme, String server, int port, String path, String query, String fragment, String expectedStr) + { + String actual = URIUtil.newURI(scheme, server, port, path, query, fragment); + assertEquals(expectedStr, actual.toString()); + } + + @ParameterizedTest + @CsvSource(value = { + "http,80", + "https,443", + "ws,80", + "wss,443", + "ssh,22", + "file,-1", + "bundle,-1", + "HTTP,80", + "HttPs,443", + "http+ssl,-1" + }) + public void testGetDefaultPortForScheme(String scheme, int expectedPort) + { + int actual = URIUtil.getDefaultPortForScheme(scheme); + assertEquals(expectedPort, actual); + } + + @ParameterizedTest + @CsvSource(value = { + "http,80,0", + "https,443,0", + "https,8443,8443", + "ws,80,0", + "ws,9999,9999", + "wss,443,0", + "wss,-1,0", + "wss,0,0", + "ssh,22,0", + "file,-1,0", + "bundle,-1,0", + "HTTP,80,0", + "HttPs,443,0", + "http+ssl,-1,0" + }) + public void testNormalizePortForScheme(String scheme, int port, int expectedPort) + { + int actual = URIUtil.normalizePortForScheme(scheme, port); + assertEquals(expectedPort, actual); + } + + @ParameterizedTest + @CsvSource(value = { + "http,http", + "https,https", + "HTTP,http", + "WSS,wss", + "WS,ws", + "XRTP,xrtp", + "Https,https" + }) + public void testNormalizeScheme(String input, String expected) + { + String actual = URIUtil.normalizeScheme(input); + assertThat(actual, is(expected)); + } } diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/WebSocketCloseTest.java b/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/WebSocketCloseTest.java index 66608a7dcd7..d2d1550c60d 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/WebSocketCloseTest.java +++ b/jetty-core/jetty-websocket/jetty-websocket-core-tests/src/test/java/org/eclipse/jetty/websocket/core/WebSocketCloseTest.java @@ -26,6 +26,7 @@ import org.eclipse.jetty.logging.StacklessLogging; import org.eclipse.jetty.util.BlockingArrayQueue; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; +import org.eclipse.jetty.util.URIUtil; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; @@ -77,7 +78,7 @@ public class WebSocketCloseTest extends WebSocketTester public void setup(State state, String scheme) throws Exception { boolean tls; - switch (scheme) + switch (URIUtil.normalizeScheme(scheme)) { case "ws": tls = false; diff --git a/jetty-core/jetty-websocket/jetty-websocket-jetty-api/src/main/java/org/eclipse/jetty/websocket/api/util/WSURI.java b/jetty-core/jetty-websocket/jetty-websocket-jetty-api/src/main/java/org/eclipse/jetty/websocket/api/util/WSURI.java index 5c372bccdf1..eaac42232bc 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-jetty-api/src/main/java/org/eclipse/jetty/websocket/api/util/WSURI.java +++ b/jetty-core/jetty-websocket/jetty-websocket-jetty-api/src/main/java/org/eclipse/jetty/websocket/api/util/WSURI.java @@ -102,17 +102,17 @@ public final class WSURI public static URI toWebsocket(final URI inputUri) throws URISyntaxException { Objects.requireNonNull(inputUri, "Input URI must not be null"); - String httpScheme = inputUri.getScheme(); - if (httpScheme == null) + String scheme = inputUri.getScheme(); + if (scheme == null) throw new URISyntaxException(inputUri.toString(), "Undefined HTTP scheme"); - if ("ws".equalsIgnoreCase(httpScheme) || "wss".equalsIgnoreCase(httpScheme)) + if ("ws".equalsIgnoreCase(scheme) || "wss".equalsIgnoreCase(scheme)) return inputUri; - String afterScheme = inputUri.toString().substring(httpScheme.length()); - if ("http".equalsIgnoreCase(httpScheme)) + String afterScheme = inputUri.toString().substring(scheme.length()); + if ("http".equalsIgnoreCase(scheme)) return new URI("ws" + afterScheme); - if ("https".equalsIgnoreCase(httpScheme)) + if ("https".equalsIgnoreCase(scheme)) return new URI("wss" + afterScheme); throw new URISyntaxException(inputUri.toString(), "Unrecognized HTTP scheme"); diff --git a/jetty-ee10/jetty-ee10-fcgi-proxy/src/main/java/org/eclipse/jetty/ee10/fcgi/proxy/FastCGIProxyServlet.java b/jetty-ee10/jetty-ee10-fcgi-proxy/src/main/java/org/eclipse/jetty/ee10/fcgi/proxy/FastCGIProxyServlet.java index 47cd4e5683a..3a22a0974b2 100644 --- a/jetty-ee10/jetty-ee10-fcgi-proxy/src/main/java/org/eclipse/jetty/ee10/fcgi/proxy/FastCGIProxyServlet.java +++ b/jetty-ee10/jetty-ee10-fcgi-proxy/src/main/java/org/eclipse/jetty/ee10/fcgi/proxy/FastCGIProxyServlet.java @@ -40,6 +40,7 @@ import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.io.ClientConnector; import org.eclipse.jetty.util.ProcessorUtils; +import org.eclipse.jetty.util.URIUtil; /** * Specific implementation of {@link org.eclipse.jetty.ee10.proxy.AsyncProxyServlet.Transparent} for FastCGI. @@ -195,7 +196,7 @@ public class FastCGIProxyServlet extends AsyncProxyServlet.Transparent { String server = request.getServerName(); int port = request.getServerPort(); - if (port != HttpScheme.getDefaultPort(request.getScheme())) + if (port != URIUtil.getDefaultPortForScheme(request.getScheme())) server += ":" + port; String host = server; proxyRequest.headers(headers -> headers diff --git a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletApiRequest.java b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletApiRequest.java index 540e3cbf22c..28a6facd63e 100644 --- a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletApiRequest.java +++ b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletApiRequest.java @@ -65,7 +65,6 @@ import org.eclipse.jetty.http.HttpException; import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpHeader; -import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpURI; import org.eclipse.jetty.http.HttpVersion; @@ -1017,7 +1016,7 @@ public class ServletApiRequest implements HttpServletRequest // If no port specified, return the default port for the scheme if (port <= 0) - return HttpScheme.getDefaultPort(getScheme()); + return URIUtil.getDefaultPortForScheme(getScheme()); // return a specific port return port; diff --git a/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/DispatcherTest.java b/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/DispatcherTest.java index 07dcfbb1eca..abb74a365af 100644 --- a/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/DispatcherTest.java +++ b/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/DispatcherTest.java @@ -35,9 +35,7 @@ import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; import jakarta.servlet.ServletOutputStream; import jakarta.servlet.ServletRequest; -import jakarta.servlet.ServletRequestWrapper; import jakarta.servlet.ServletResponse; -import jakarta.servlet.ServletResponseWrapper; import jakarta.servlet.WriteListener; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletMapping; @@ -1519,7 +1517,7 @@ public class DispatcherTest assertEquals("/context/AssertForwardServlet", request.getRequestURI()); assertEquals("/context", request.getContextPath()); assertEquals("/AssertForwardServlet", request.getServletPath()); - assertEquals("http://local:80/context/AssertForwardServlet", request.getRequestURL().toString()); + assertEquals("http://local/context/AssertForwardServlet", request.getRequestURL().toString()); response.setContentType("text/html"); response.setStatus(HttpServletResponse.SC_OK); diff --git a/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/ErrorPageTest.java b/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/ErrorPageTest.java index 28e97c319aa..c1b3d8234d8 100644 --- a/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/ErrorPageTest.java +++ b/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/ErrorPageTest.java @@ -679,7 +679,7 @@ public class ErrorPageTest assertThat(responseBody, Matchers.containsString("ERROR_SERVLET: " + failServlet.getClass().getName())); assertThat(responseBody, Matchers.containsString("ERROR_REQUEST_URI: /fail/599")); assertThat(responseBody, Matchers.containsString("getQueryString()=[code=param]")); - assertThat(responseBody, Matchers.containsString("getRequestURL()=[http://test:80/error/599?code=param]")); + assertThat(responseBody, Matchers.containsString("getRequestURL()=[http://test/error/599?code=param]")); assertThat(responseBody, Matchers.containsString("getParameterMap().size=2")); assertThat(responseBody, Matchers.containsString("getParameterMap()[code]=[param]")); assertThat(responseBody, Matchers.containsString("getParameterMap()[value]=[zed]")); diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/support/XmlBasedJettyServer.java b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/support/XmlBasedJettyServer.java index eba7c59f1d8..56fa13ab8aa 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/support/XmlBasedJettyServer.java +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/support/XmlBasedJettyServer.java @@ -29,6 +29,7 @@ import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.server.NetworkConnector; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.resource.ResourceFactory; import org.eclipse.jetty.xml.XmlConfiguration; @@ -168,7 +169,7 @@ public class XmlBasedJettyServer public void setScheme(String scheme) { - this._scheme = scheme; + this._scheme = URIUtil.normalizeScheme(scheme); } public void start() throws Exception @@ -196,11 +197,7 @@ public class XmlBasedJettyServer public URI getServerURI() { - StringBuffer uri = new StringBuffer(); - uri.append(this._scheme).append("://"); - uri.append(InetAddress.getLoopbackAddress().getHostAddress()); - uri.append(":").append(this._serverPort); - return URI.create(uri.toString()); + return URI.create(URIUtil.newURI(_scheme, InetAddress.getLoopbackAddress().getHostAddress(), _serverPort)); } public Server getServer() diff --git a/jetty-ee9/jetty-ee9-fcgi-proxy/src/main/java/org/eclipse/jetty/ee9/fcgi/proxy/FastCGIProxyServlet.java b/jetty-ee9/jetty-ee9-fcgi-proxy/src/main/java/org/eclipse/jetty/ee9/fcgi/proxy/FastCGIProxyServlet.java index 8f59797fa66..2f15cbf1005 100644 --- a/jetty-ee9/jetty-ee9-fcgi-proxy/src/main/java/org/eclipse/jetty/ee9/fcgi/proxy/FastCGIProxyServlet.java +++ b/jetty-ee9/jetty-ee9-fcgi-proxy/src/main/java/org/eclipse/jetty/ee9/fcgi/proxy/FastCGIProxyServlet.java @@ -40,6 +40,7 @@ import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.io.ClientConnector; import org.eclipse.jetty.util.ProcessorUtils; +import org.eclipse.jetty.util.URIUtil; /** * Specific implementation of {@link org.eclipse.jetty.ee9.proxy.AsyncProxyServlet.Transparent} for FastCGI. @@ -195,7 +196,8 @@ public class FastCGIProxyServlet extends AsyncProxyServlet.Transparent { String server = request.getServerName(); int port = request.getServerPort(); - if (port != HttpScheme.getDefaultPort(request.getScheme())) + String scheme = request.getScheme(); + if (port != URIUtil.getDefaultPortForScheme(scheme)) server += ":" + port; String host = server; proxyRequest.headers(headers -> headers diff --git a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/Request.java b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/Request.java index f9bbcf90cf5..62b53a49974 100644 --- a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/Request.java +++ b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/Request.java @@ -1177,9 +1177,7 @@ public class Request implements HttpServletRequest */ public StringBuilder getRootURL() { - StringBuilder url = new StringBuilder(128); - URIUtil.appendSchemeHostPort(url, getScheme(), getServerName(), getServerPort()); - return url; + return new StringBuilder(HttpURI.from(getScheme(), getServerName(), getServerPort(), null).asString()); } @Override diff --git a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/Response.java b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/Response.java index eb9265cbc2e..0a5e2788fef 100644 --- a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/Response.java +++ b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/Response.java @@ -347,7 +347,7 @@ public class Response implements HttpServletResponse path = (path == null ? "" : path); int port = uri.getPort(); if (port < 0) - port = HttpScheme.getDefaultPort(uri.getScheme()); + port = URIUtil.getDefaultPortForScheme(uri.getScheme()); // Is it the same server? if (!request.getServerName().equalsIgnoreCase(uri.getHost())) diff --git a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/SecuredRedirectHandler.java b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/SecuredRedirectHandler.java index 58165db65c3..668d0ef1f68 100644 --- a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/SecuredRedirectHandler.java +++ b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/SecuredRedirectHandler.java @@ -81,9 +81,9 @@ public class SecuredRedirectHandler extends HandlerWrapper if (securePort > 0) { String secureScheme = httpConfig.getSecureScheme(); - String url = URIUtil.newURI(secureScheme, baseRequest.getServerName(), securePort, baseRequest.getRequestURI(), baseRequest.getQueryString()); + String uri = URIUtil.newURI(secureScheme, baseRequest.getServerName(), securePort, baseRequest.getRequestURI(), baseRequest.getQueryString()); response.setContentLength(0); - baseRequest.getResponse().sendRedirect(_redirectCode, url, true); + baseRequest.getResponse().sendRedirect(_redirectCode, uri, true); } else { diff --git a/jetty-ee9/jetty-ee9-openid/src/main/java/org/eclipse/jetty/ee9/security/openid/OpenIdAuthenticator.java b/jetty-ee9/jetty-ee9-openid/src/main/java/org/eclipse/jetty/ee9/security/openid/OpenIdAuthenticator.java index 1037b24d008..97c4be36c1d 100644 --- a/jetty-ee9/jetty-ee9-openid/src/main/java/org/eclipse/jetty/ee9/security/openid/OpenIdAuthenticator.java +++ b/jetty-ee9/jetty-ee9-openid/src/main/java/org/eclipse/jetty/ee9/security/openid/OpenIdAuthenticator.java @@ -292,8 +292,7 @@ public class OpenIdAuthenticator extends LoginAuthenticator String redirectUri = null; if (_logoutRedirectPath != null) { - StringBuilder sb = new StringBuilder(128); - URIUtil.appendSchemeHostPort(sb, request.getScheme(), request.getServerName(), request.getServerPort()); + StringBuilder sb = URIUtil.newURIBuilder(request.getScheme(), request.getServerName(), request.getServerPort()); sb.append(baseRequest.getContextPath()); sb.append(_logoutRedirectPath); redirectUri = sb.toString(); @@ -614,8 +613,7 @@ public class OpenIdAuthenticator extends LoginAuthenticator private String getRedirectUri(HttpServletRequest request) { - final StringBuffer redirectUri = new StringBuffer(128); - URIUtil.appendSchemeHostPort(redirectUri, request.getScheme(), + final StringBuilder redirectUri = URIUtil.newURIBuilder(request.getScheme(), request.getServerName(), request.getServerPort()); redirectUri.append(request.getContextPath()); redirectUri.append(_redirectPath); diff --git a/jetty-ee9/jetty-ee9-proxy/src/test/java/org/eclipse/jetty/ee9/proxy/ProxyServletTest.java b/jetty-ee9/jetty-ee9-proxy/src/test/java/org/eclipse/jetty/ee9/proxy/ProxyServletTest.java index 08ccb0e8ae3..f1d5513df29 100644 --- a/jetty-ee9/jetty-ee9-proxy/src/test/java/org/eclipse/jetty/ee9/proxy/ProxyServletTest.java +++ b/jetty-ee9/jetty-ee9-proxy/src/test/java/org/eclipse/jetty/ee9/proxy/ProxyServletTest.java @@ -89,6 +89,7 @@ import org.eclipse.jetty.toolchain.test.MavenTestingUtils; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.StringUtil; +import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.junit.jupiter.api.AfterEach; @@ -841,7 +842,7 @@ public class ProxyServletTest int serverPort = serverConnector.getLocalPort(); if (HttpScheme.HTTPS.is(scheme)) serverPort = tlsServerConnector.getLocalPort(); - String proxyTo = scheme + "://localhost:" + serverPort; + String proxyTo = URIUtil.normalizeScheme(scheme) + "://localhost:" + serverPort; Map params = new HashMap<>(); params.put("proxyTo", proxyTo); params.put("prefix", prefix); diff --git a/jetty-ee9/jetty-ee9-security/src/test/java/org/eclipse/jetty/ee9/security/DataConstraintsTest.java b/jetty-ee9/jetty-ee9-security/src/test/java/org/eclipse/jetty/ee9/security/DataConstraintsTest.java index fc91bc105ba..0909d0dce24 100644 --- a/jetty-ee9/jetty-ee9-security/src/test/java/org/eclipse/jetty/ee9/security/DataConstraintsTest.java +++ b/jetty-ee9/jetty-ee9-security/src/test/java/org/eclipse/jetty/ee9/security/DataConstraintsTest.java @@ -138,7 +138,7 @@ public class DataConstraintsTest response = _connector.getResponse("GET /ctx/integral/info HTTP/1.0\r\n\r\n"); assertThat(response, Matchers.containsString("HTTP/1.1 302 Found")); - assertThat(response, Matchers.containsString("Location: BWTP://")); + assertThat(response, Matchers.containsString("Location: bwtp://")); assertThat(response, Matchers.containsString(":9999")); response = _connectorS.getResponse("GET /ctx/integral/info HTTP/1.0\r\n\r\n"); @@ -166,7 +166,7 @@ public class DataConstraintsTest response = _connector.getResponse("GET /ctx/confid/info HTTP/1.0\r\n\r\n"); assertThat(response, Matchers.containsString("HTTP/1.1 302 Found")); - assertThat(response, Matchers.containsString("Location: BWTP://")); + assertThat(response, Matchers.containsString("Location: bwtp://")); assertThat(response, Matchers.containsString(":9999")); response = _connectorS.getResponse("GET /ctx/confid/info HTTP/1.0\r\n\r\n"); diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/support/XmlBasedJettyServer.java b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/support/XmlBasedJettyServer.java index 9fa29cd1364..78608b6c4a6 100644 --- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/support/XmlBasedJettyServer.java +++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/support/XmlBasedJettyServer.java @@ -13,7 +13,6 @@ package org.eclipse.jetty.ee9.test.support; -import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.net.InetAddress; @@ -30,6 +29,7 @@ import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.server.NetworkConnector; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.resource.ResourceFactory; import org.eclipse.jetty.xml.XmlConfiguration; @@ -169,7 +169,7 @@ public class XmlBasedJettyServer public void setScheme(String scheme) { - this._scheme = scheme; + this._scheme = URIUtil.normalizeScheme(scheme); } public void start() throws Exception