diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java index f7d3ab5671c..a60e676a1d1 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java @@ -50,6 +50,7 @@ import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.ErrorHandler; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; +import org.eclipse.jetty.util.HostPort; import org.eclipse.jetty.util.SharedBlockingCallback.Blocker; import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.thread.Scheduler; @@ -122,6 +123,15 @@ public abstract class HttpChannel implements Runnable, HttpOutput.Interceptor return _state.isSendError(); } + /** Format the address or host returned from Request methods + * @param addr The address or host + * @return Default implementation returns {@link HostPort#normalizeHost(String)} + */ + protected String formatAddrOrHost(String addr) + { + return HostPort.normalizeHost(addr); + } + private HttpInput newHttpInput(HttpChannelState state) { return new HttpInput(state); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java index 33115fc6e57..922bd1c53a6 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java @@ -985,7 +985,7 @@ public class Request implements HttpServletRequest String name = InetAddress.getLocalHost().getHostAddress(); if (StringUtil.ALL_INTERFACES.equals(name)) return null; - return HostPort.normalizeHost(name); + return formatAddrOrHost(name); } catch (UnknownHostException e) { @@ -1001,7 +1001,7 @@ public class Request implements HttpServletRequest String result = address == null ? local.getHostString() : address.getHostAddress(); - return HostPort.normalizeHost(result); + return formatAddrOrHost(result); } @Override @@ -1011,7 +1011,7 @@ public class Request implements HttpServletRequest { InetSocketAddress local = _channel.getLocalAddress(); if (local != null) - return HostPort.normalizeHost(local.getHostString()); + return formatAddrOrHost(local.getHostString()); } try @@ -1019,7 +1019,7 @@ public class Request implements HttpServletRequest String name = InetAddress.getLocalHost().getHostName(); if (StringUtil.ALL_INTERFACES.equals(name)) return null; - return HostPort.normalizeHost(name); + return formatAddrOrHost(name); } catch (UnknownHostException e) { @@ -1211,12 +1211,10 @@ public class Request implements HttpServletRequest InetAddress address = remote.getAddress(); String result = address == null - ? remote.getHostString() - : address.getHostAddress(); - // Add IPv6 brackets if necessary, to be consistent - // with cases where _remote has been built from other - // sources such as forward headers or PROXY protocol. - return HostPort.normalizeHost(result); + ? remote.getHostString() + : address.getHostAddress(); + + return formatAddrOrHost(result); } @Override @@ -1227,8 +1225,9 @@ public class Request implements HttpServletRequest remote = _channel.getRemoteAddress(); if (remote == null) return ""; + // We want the URI host, so add IPv6 brackets if necessary. - return HostPort.normalizeHost(remote.getHostString()); + return formatAddrOrHost(remote.getHostString()); } @Override @@ -1322,7 +1321,7 @@ public class Request implements HttpServletRequest @Override public String getServerName() { - return _uri == null ? findServerName() : _uri.getHost(); + return _uri == null ? findServerName() : formatAddrOrHost(_uri.getHost()); } private String findServerName() @@ -1330,12 +1329,12 @@ public class Request implements HttpServletRequest // Return host from connection String name = getLocalName(); if (name != null) - return HostPort.normalizeHost(name); + return formatAddrOrHost(name); // Return the local host try { - return HostPort.normalizeHost(InetAddress.getLocalHost().getHostAddress()); + return formatAddrOrHost(InetAddress.getLocalHost().getHostAddress()); } catch (UnknownHostException e) { @@ -2541,4 +2540,9 @@ public class Request implements HttpServletRequest // which we recover from the IncludeAttributes wrapper. return findServletPathMapping(); } + + private String formatAddrOrHost(String name) + { + return _channel == null ? HostPort.normalizeHost(name) : _channel.formatAddrOrHost(name); + } } diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java index 7f2f91d7a64..f2b0ac124ea 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java @@ -20,6 +20,9 @@ import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.io.UnsupportedEncodingException; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.InetSocketAddress; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; @@ -59,6 +62,8 @@ import org.eclipse.jetty.http.MetaData; import org.eclipse.jetty.http.MimeTypes; import org.eclipse.jetty.http.UriCompliance; import org.eclipse.jetty.http.pathmap.ServletPathSpec; +import org.eclipse.jetty.io.Connection; +import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.logging.StacklessLogging; import org.eclipse.jetty.server.LocalConnector.LocalEndPoint; import org.eclipse.jetty.server.handler.AbstractHandler; @@ -105,12 +110,37 @@ public class RequestTest private Server _server; private LocalConnector _connector; private RequestHandler _handler; + private boolean _normalizeAddress = true; @BeforeEach public void init() throws Exception { _server = new Server(); - HttpConnectionFactory http = new HttpConnectionFactory(); + HttpConnectionFactory http = new HttpConnectionFactory() + { + @Override + public Connection newConnection(Connector connector, EndPoint endPoint) + { + HttpConnection conn = new HttpConnection(getHttpConfiguration(), connector, endPoint, isRecordHttpComplianceViolations()) + { + @Override + protected HttpChannelOverHttp newHttpChannel() + { + return new HttpChannelOverHttp(this, getConnector(), getHttpConfiguration(), getEndPoint(), this) + { + @Override + protected String formatAddrOrHost(String addr) + { + if (_normalizeAddress) + return super.formatAddrOrHost(addr); + return addr; + } + }; + } + }; + return configure(conn, connector, endPoint); + } + }; http.setInputBufferSize(1024); http.getHttpConfiguration().setRequestHeaderSize(512); http.getHttpConfiguration().setResponseHeaderSize(512); @@ -858,6 +888,65 @@ public class RequestTest assertEquals("8888", results.get(i)); } + @Test + public void testIPv6() throws Exception + { + final ArrayList results = new ArrayList<>(); + final InetAddress local = Inet6Address.getByAddress("localIPv6", new byte[]{ + 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8 + }); + final InetSocketAddress localAddr = new InetSocketAddress(local, 32768); + _handler._checker = new RequestTester() + { + @Override + public boolean check(HttpServletRequest request, HttpServletResponse response) + { + ((Request)request).setRemoteAddr(localAddr); + results.add(request.getRemoteAddr()); + results.add(request.getRemoteHost()); + results.add(Integer.toString(request.getRemotePort())); + results.add(request.getServerName()); + results.add(Integer.toString(request.getServerPort())); + results.add(request.getLocalAddr()); + results.add(Integer.toString(request.getLocalPort())); + return true; + } + }; + + _normalizeAddress = true; + String response = _connector.getResponse( + "GET / HTTP/1.1\n" + + "Host: [::1]:8888\n" + + "Connection: close\n" + + "\n"); + int i = 0; + assertThat(response, containsString("200 OK")); + assertEquals("[1:2:3:4:5:6:7:8]", results.get(i++)); + assertEquals("localIPv6", results.get(i++)); + assertEquals("32768", results.get(i++)); + assertEquals("[::1]", results.get(i++)); + assertEquals("8888", results.get(i++)); + assertEquals("0.0.0.0", results.get(i++)); + assertEquals("0", results.get(i)); + + _normalizeAddress = false; + results.clear(); + response = _connector.getResponse( + "GET / HTTP/1.1\n" + + "Host: [::1]:8888\n" + + "Connection: close\n" + + "\n"); + i = 0; + assertThat(response, containsString("200 OK")); + assertEquals("1:2:3:4:5:6:7:8", results.get(i++)); + assertEquals("localIPv6", results.get(i++)); + assertEquals("32768", results.get(i++)); + assertEquals("[::1]", results.get(i++)); + assertEquals("8888", results.get(i++)); + assertEquals("0.0.0.0", results.get(i++)); + assertEquals("0", results.get(i)); + } + @Test public void testContent() throws Exception { diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/HostPort.java b/jetty-util/src/main/java/org/eclipse/jetty/util/HostPort.java index b24d810e94d..b0980c9617f 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/HostPort.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/HostPort.java @@ -144,7 +144,7 @@ public class HostPort 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) + if (host == null || host.isEmpty() || host.charAt(0) == '[' || host.indexOf(':') < 0) return host; // normalize with [ ]