http-client-branch: fixed TLS hostname checking issue, SSL session reuse, and changed HttpResponse to return SSLSession http-client-branch
authormichaelm
Wed, 14 Feb 2018 16:04:18 +0000
branchhttp-client-branch
changeset 56126 86e628130926
parent 56122 1d7d3d8f8021
child 56127 e2a780d8c6f0
http-client-branch: fixed TLS hostname checking issue, SSL session reuse, and changed HttpResponse to return SSLSession
src/java.net.http/share/classes/java/net/http/HttpResponse.java
src/java.net.http/share/classes/jdk/internal/net/http/AbstractAsyncSSLConnection.java
src/java.net.http/share/classes/jdk/internal/net/http/AsyncSSLConnection.java
src/java.net.http/share/classes/jdk/internal/net/http/AsyncSSLTunnelConnection.java
src/java.net.http/share/classes/jdk/internal/net/http/Exchange.java
src/java.net.http/share/classes/jdk/internal/net/http/Http1Response.java
src/java.net.http/share/classes/jdk/internal/net/http/HttpResponseImpl.java
src/java.net.http/share/classes/jdk/internal/net/http/Response.java
src/java.net.http/share/classes/jdk/internal/net/http/Stream.java
src/java.net.http/share/classes/jdk/internal/net/http/common/ImmutableExtendedSSLSession.java
src/java.net.http/share/classes/jdk/internal/net/http/common/ImmutableSSLSession.java
src/java.net.http/share/classes/jdk/internal/net/http/common/Log.java
src/java.net.http/share/classes/jdk/internal/net/http/common/Utils.java
test/jdk/java/net/httpclient/ManyRequestsLegacy.java
test/jdk/java/net/httpclient/SmokeTest.java
test/jdk/java/net/httpclient/http2/keystore.p12
test/jdk/java/net/httpclient/offline/FixedHttpResponse.java
test/jdk/java/net/httpclient/offline/FixedResponseHttpClient.java
test/jdk/java/net/httpclient/ssltest/CertificateTest.java
test/jdk/java/net/httpclient/ssltest/Server.java
test/jdk/java/net/httpclient/ssltest/bad.keystore
test/jdk/java/net/httpclient/ssltest/good.keystore
test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/AuthenticationFilterTest.java
test/jdk/lib/testlibrary/jdk/testlibrary/testkeys
--- a/src/java.net.http/share/classes/java/net/http/HttpResponse.java	Tue Feb 13 16:22:49 2018 +0000
+++ b/src/java.net.http/share/classes/java/net/http/HttpResponse.java	Wed Feb 14 16:04:18 2018 +0000
@@ -47,7 +47,7 @@
 import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.stream.Stream;
-import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLSession;
 import jdk.internal.net.http.BufferingSubscriber;
 import jdk.internal.net.http.LineSubscriberAdapter;
 import jdk.internal.net.http.ResponseBodyHandlers.FileDownloadBodyHandler;
@@ -132,12 +132,12 @@
     public abstract T body();
 
     /**
-     * Returns the {@link javax.net.ssl.SSLParameters} in effect for this
-     * response. Returns {@code null} if this is not a HTTPS response.
+     * Returns an {@link Optional} containing the {@link javax.net.ssl.SSLSession} in effect
+     * for this response. Returns an empty {@code Optional} if this is not a HTTPS response.
      *
-     * @return the SSLParameters associated with the response
+     * @return an {@code Optional} containing the SSLSession associated with the response
      */
-    public abstract SSLParameters sslParameters();
+    public abstract Optional<SSLSession> sslSession();
 
     /**
      * Returns the {@code URI} that the response was received from. This may be
--- a/src/java.net.http/share/classes/jdk/internal/net/http/AbstractAsyncSSLConnection.java	Tue Feb 13 16:22:49 2018 +0000
+++ b/src/java.net.http/share/classes/jdk/internal/net/http/AbstractAsyncSSLConnection.java	Wed Feb 14 16:04:18 2018 +0000
@@ -69,14 +69,14 @@
 
     AbstractAsyncSSLConnection(InetSocketAddress addr,
                                HttpClientImpl client,
-                               String serverName,
+                               String serverName, int port,
                                String[] alpn) {
         super(addr, client);
         this.serverName = serverName;
         SSLContext context = client.theSSLContext();
         sslParameters = createSSLParameters(client, serverName, alpn);
         Log.logParams(sslParameters);
-        engine = createEngine(context, sslParameters);
+        engine = createEngine(context, serverName, port, sslParameters);
     }
 
     abstract HttpConnection plainConnection();
@@ -94,6 +94,7 @@
                                                      String[] alpn) {
         SSLParameters sslp = client.sslParameters();
         SSLParameters sslParameters = Utils.copySSLParameters(sslp);
+        sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
         if (alpn != null) {
             Log.logSSL("AbstractAsyncSSLConnection: Setting application protocols: {0}",
                        Arrays.toString(alpn));
@@ -107,9 +108,9 @@
         return sslParameters;
     }
 
-    private static SSLEngine createEngine(SSLContext context,
+    private static SSLEngine createEngine(SSLContext context, String serverName, int port,
                                           SSLParameters sslParameters) {
-        SSLEngine engine = context.createSSLEngine();
+        SSLEngine engine = context.createSSLEngine(serverName, port);
         engine.setUseClientMode(true);
         engine.setSSLParameters(sslParameters);
         return engine;
--- a/src/java.net.http/share/classes/jdk/internal/net/http/AsyncSSLConnection.java	Tue Feb 13 16:22:49 2018 +0000
+++ b/src/java.net.http/share/classes/jdk/internal/net/http/AsyncSSLConnection.java	Wed Feb 14 16:04:18 2018 +0000
@@ -46,7 +46,7 @@
     AsyncSSLConnection(InetSocketAddress addr,
                        HttpClientImpl client,
                        String[] alpn) {
-        super(addr, client, Utils.getServerName(addr), alpn);
+        super(addr, client, Utils.getServerName(addr), addr.getPort(), alpn);
         plainConnection = new PlainHttpConnection(addr, client);
         writePublisher = new PlainHttpPublisher();
     }
--- a/src/java.net.http/share/classes/jdk/internal/net/http/AsyncSSLTunnelConnection.java	Tue Feb 13 16:22:49 2018 +0000
+++ b/src/java.net.http/share/classes/jdk/internal/net/http/AsyncSSLTunnelConnection.java	Wed Feb 14 16:04:18 2018 +0000
@@ -49,7 +49,7 @@
                              InetSocketAddress proxy,
                              HttpHeaders proxyHeaders)
     {
-        super(addr, client, Utils.getServerName(addr), alpn);
+        super(addr, client, Utils.getServerName(addr), addr.getPort(), alpn);
         this.plainConnection = new PlainTunnelingConnection(addr, proxy, client, proxyHeaders);
         this.writePublisher = new PlainHttpPublisher();
     }
--- a/src/java.net.http/share/classes/jdk/internal/net/http/Exchange.java	Tue Feb 13 16:22:49 2018 +0000
+++ b/src/java.net.http/share/classes/jdk/internal/net/http/Exchange.java	Wed Feb 14 16:04:18 2018 +0000
@@ -310,8 +310,9 @@
         if (t instanceof ProxyAuthenticationRequired) {
             bodyIgnored = MinimalFuture.completedFuture(null);
             Response proxyResponse = ((ProxyAuthenticationRequired)t).proxyResponse;
+            HttpConnection c = ex == null ? null : ex.connection();
             Response syntheticResponse = new Response(request, this,
-                    proxyResponse.headers, proxyResponse.statusCode,
+                    proxyResponse.headers, c, proxyResponse.statusCode,
                     proxyResponse.version, true);
             return MinimalFuture.completedFuture(syntheticResponse);
         } else if (t != null) {
--- a/src/java.net.http/share/classes/jdk/internal/net/http/Http1Response.java	Tue Feb 13 16:22:49 2018 +0000
+++ b/src/java.net.http/share/classes/jdk/internal/net/http/Http1Response.java	Wed Feb 14 16:04:18 2018 +0000
@@ -107,6 +107,7 @@
                 response = new Response(request,
                                         exchange.getExchange(),
                                         headers,
+                                        connection,
                                         responseCode,
                                         HTTP_1_1);
                 return response;
--- a/src/java.net.http/share/classes/jdk/internal/net/http/HttpResponseImpl.java	Tue Feb 13 16:22:49 2018 +0000
+++ b/src/java.net.http/share/classes/jdk/internal/net/http/HttpResponseImpl.java	Wed Feb 14 16:04:18 2018 +0000
@@ -31,7 +31,7 @@
 import java.util.Optional;
 import java.util.concurrent.CompletableFuture;
 import java.util.function.Supplier;
-import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLSession;
 import java.net.http.HttpClient;
 import java.net.http.HttpHeaders;
 import java.net.http.HttpRequest;
@@ -48,7 +48,7 @@
     final HttpRequest initialRequest;
     final Optional<HttpResponse<T>> previousResponse;
     final HttpHeaders headers;
-    final SSLParameters sslParameters;
+    final Optional<SSLSession> sslSession;
     final URI uri;
     final HttpClient.Version version;
     RawChannel rawchan;
@@ -67,7 +67,7 @@
         this.previousResponse = Optional.ofNullable(previousResponse);
         this.headers = response.headers();
         //this.trailers = trailers;
-        this.sslParameters = exch.client().sslParameters();
+        this.sslSession = Optional.ofNullable(response.getSSLSession());
         this.uri = response.request().uri();
         this.version = response.version();
         this.connection = connection(exch);
@@ -113,8 +113,8 @@
     }
 
     @Override
-    public SSLParameters sslParameters() {
-        return sslParameters;
+    public Optional<SSLSession> sslSession() {
+        return sslSession;
     }
 
     @Override
--- a/src/java.net.http/share/classes/jdk/internal/net/http/Response.java	Tue Feb 13 16:22:49 2018 +0000
+++ b/src/java.net.http/share/classes/jdk/internal/net/http/Response.java	Wed Feb 14 16:04:18 2018 +0000
@@ -26,8 +26,13 @@
 package jdk.internal.net.http;
 
 import java.net.URI;
+import java.io.IOException;
 import java.net.http.HttpClient;
 import java.net.http.HttpHeaders;
+import java.net.InetSocketAddress;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLSession;
+import jdk.internal.net.http.common.Utils;
 
 /**
  * Response headers and status code.
@@ -39,19 +44,23 @@
     final Exchange<?> exchange;
     final HttpClient.Version version;
     final boolean isConnectResponse;
+    final SSLSession sslSession;
+    final InetSocketAddress localAddress;
 
     Response(HttpRequestImpl req,
              Exchange<?> exchange,
              HttpHeaders headers,
+             HttpConnection connection,
              int statusCode,
              HttpClient.Version version) {
-        this(req, exchange, headers, statusCode, version,
+        this(req, exchange, headers, connection, statusCode, version,
                 "CONNECT".equalsIgnoreCase(req.method()));
     }
 
     Response(HttpRequestImpl req,
              Exchange<?> exchange,
              HttpHeaders headers,
+             HttpConnection connection,
              int statusCode,
              HttpClient.Version version,
              boolean isConnectResponse) {
@@ -60,7 +69,21 @@
         this.version = version;
         this.exchange = exchange;
         this.statusCode = statusCode;
+        InetSocketAddress a;
+        try {
+            a = (InetSocketAddress)connection.channel().getLocalAddress();
+        } catch (IOException e) {
+            a = null;
+        }
+        this.localAddress = a;
         this.isConnectResponse = isConnectResponse;
+        if (connection != null && connection instanceof AbstractAsyncSSLConnection) {
+            AbstractAsyncSSLConnection cc = (AbstractAsyncSSLConnection)connection;
+            SSLEngine engine = cc.getEngine();
+            sslSession = Utils.immutableSession(engine.getSession());
+        } else {
+            sslSession = null;
+        }
     }
 
     HttpRequestImpl request() {
@@ -83,6 +106,10 @@
         return statusCode;
     }
 
+    SSLSession getSSLSession() {
+        return sslSession;
+    }
+
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder();
@@ -95,6 +122,9 @@
           .append(uristring)
           .append(") ")
           .append(statusCode());
+        sb.append(" ").append(version);
+        if (localAddress != null)
+            sb.append(" Local port:  ").append(localAddress.getPort());
         return sb.toString();
     }
 }
--- a/src/java.net.http/share/classes/jdk/internal/net/http/Stream.java	Tue Feb 13 16:22:49 2018 +0000
+++ b/src/java.net.http/share/classes/jdk/internal/net/http/Stream.java	Wed Feb 14 16:04:18 2018 +0000
@@ -363,7 +363,7 @@
                 .orElseThrow(() -> new IOException("no statuscode in response"));
 
         response = new Response(
-                request, exchange, responseHeaders,
+                request, exchange, responseHeaders, connection(),
                 responseCode, HttpClient.Version.HTTP_2);
 
         /* TODO: review if needs to be removed
@@ -1120,7 +1120,7 @@
             }
 
             this.response = new Response(
-                pushReq, exchange, responseHeaders,
+                pushReq, exchange, responseHeaders, connection(),
                 responseCode, HttpClient.Version.HTTP_2);
 
             /* TODO: review if needs to be removed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.net.http/share/classes/jdk/internal/net/http/common/ImmutableExtendedSSLSession.java	Wed Feb 14 16:04:18 2018 +0000
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.internal.net.http.common;
+
+import java.security.Principal;
+import java.util.List;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.ExtendedSSLSession;
+import javax.net.ssl.SSLSessionContext;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SNIServerName;
+
+/**
+ * All mutating methods throw UnsupportedOperationException
+ */
+public class ImmutableExtendedSSLSession extends ExtendedSSLSession {
+    private final ExtendedSSLSession delegate;
+
+    ImmutableExtendedSSLSession(ExtendedSSLSession session) {
+        this.delegate = session;
+    }
+
+    public byte[] getId() {
+        return delegate.getId();
+    }
+
+    public SSLSessionContext getSessionContext() {
+        return delegate.getSessionContext();
+    }
+
+    public long getCreationTime() {
+        return delegate.getCreationTime();
+    }
+
+    public long getLastAccessedTime() {
+        return delegate.getLastAccessedTime();
+    }
+
+    public void invalidate() {
+        throw new UnsupportedOperationException("session is not mutable");
+    }
+
+    public boolean isValid() {
+        return delegate.isValid();
+    }
+
+    public void putValue(String name, Object value) {
+        throw new UnsupportedOperationException("session is not mutable");
+    }
+
+    public Object getValue(String name) {
+        return delegate.getValue(name);
+    }
+
+    public void removeValue(String name) {
+        throw new UnsupportedOperationException("session is not mutable");
+    }
+
+    public String [] getValueNames() {
+        return delegate.getValueNames();
+    }
+
+    public java.security.cert.Certificate [] getPeerCertificates()
+            throws SSLPeerUnverifiedException {
+        return delegate.getPeerCertificates();
+    }
+
+    public java.security.cert.Certificate [] getLocalCertificates() {
+        return delegate.getLocalCertificates();
+    }
+
+    @Deprecated
+    public javax.security.cert.X509Certificate [] getPeerCertificateChain()
+            throws SSLPeerUnverifiedException {
+        return delegate.getPeerCertificateChain();
+    }
+
+    public Principal getPeerPrincipal()
+            throws SSLPeerUnverifiedException {
+        return delegate.getPeerPrincipal();
+    }
+
+    public Principal getLocalPrincipal() {
+        return delegate.getLocalPrincipal();
+    }
+
+    public String getCipherSuite() {
+        return delegate.getCipherSuite();
+    }
+
+    public String getProtocol() {
+        return delegate.getProtocol();
+    }
+
+    public String getPeerHost() {
+        return delegate.getPeerHost();
+    }
+
+    public int getPeerPort() {
+        return delegate.getPeerPort();
+    }
+
+    public int getPacketBufferSize() {
+        return delegate.getPacketBufferSize();
+    }
+
+    public int getApplicationBufferSize() {
+        return delegate.getApplicationBufferSize();
+    }
+
+    public String[] getLocalSupportedSignatureAlgorithms() {
+        return delegate.getLocalSupportedSignatureAlgorithms();
+    }
+
+    public String[] getPeerSupportedSignatureAlgorithms() {
+        return delegate.getPeerSupportedSignatureAlgorithms();
+    }
+
+    public List<SNIServerName> getRequestedServerNames()  {
+        return delegate.getRequestedServerNames();
+    }
+
+    public List<byte[]> getStatusResponses() {
+        return delegate.getStatusResponses();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.net.http/share/classes/jdk/internal/net/http/common/ImmutableSSLSession.java	Wed Feb 14 16:04:18 2018 +0000
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.internal.net.http.common;
+
+import java.security.Principal;
+import java.util.List;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSessionContext;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SNIServerName;
+
+/**
+ * All mutating methods throw UnsupportedOperationException
+ */
+public class ImmutableSSLSession implements SSLSession {
+    private final SSLSession delegate;
+
+    ImmutableSSLSession(SSLSession session) {
+        this.delegate = session;
+    }
+
+    public byte[] getId() {
+        return delegate.getId();
+    }
+
+    public SSLSessionContext getSessionContext() {
+        return delegate.getSessionContext();
+    }
+
+    public long getCreationTime() {
+        return delegate.getCreationTime();
+    }
+
+    public long getLastAccessedTime() {
+        return delegate.getLastAccessedTime();
+    }
+
+    public void invalidate() {
+        throw new UnsupportedOperationException("session is not mutable");
+    }
+
+    public boolean isValid() {
+        return delegate.isValid();
+    }
+
+    public void putValue(String name, Object value) {
+        throw new UnsupportedOperationException("session is not mutable");
+    }
+
+    public Object getValue(String name) {
+        return delegate.getValue(name);
+    }
+
+    public void removeValue(String name) {
+        throw new UnsupportedOperationException("session is not mutable");
+    }
+
+    public String [] getValueNames() {
+        return delegate.getValueNames();
+    }
+
+    public java.security.cert.Certificate [] getPeerCertificates()
+            throws SSLPeerUnverifiedException {
+        return delegate.getPeerCertificates();
+    }
+
+    public java.security.cert.Certificate [] getLocalCertificates() {
+        return delegate.getLocalCertificates();
+    }
+
+    @Deprecated
+    public javax.security.cert.X509Certificate [] getPeerCertificateChain()
+            throws SSLPeerUnverifiedException {
+        return delegate.getPeerCertificateChain();
+    }
+
+    public Principal getPeerPrincipal()
+            throws SSLPeerUnverifiedException {
+        return delegate.getPeerPrincipal();
+    }
+
+    public Principal getLocalPrincipal() {
+        return delegate.getLocalPrincipal();
+    }
+
+    public String getCipherSuite() {
+        return delegate.getCipherSuite();
+    }
+
+    public String getProtocol() {
+        return delegate.getProtocol();
+    }
+
+    public String getPeerHost() {
+        return delegate.getPeerHost();
+    }
+
+    public int getPeerPort() {
+        return delegate.getPeerPort();
+    }
+
+    public int getPacketBufferSize() {
+        return delegate.getPacketBufferSize();
+    }
+
+    public int getApplicationBufferSize() {
+        return delegate.getApplicationBufferSize();
+    }
+}
--- a/src/java.net.http/share/classes/jdk/internal/net/http/common/Log.java	Tue Feb 13 16:22:49 2018 +0000
+++ b/src/java.net.http/share/classes/jdk/internal/net/http/common/Log.java	Wed Feb 14 16:04:18 2018 +0000
@@ -272,6 +272,12 @@
             }
         }
 
+        if (p.getEndpointIdentificationAlgorithm() != null) {
+            sb.append("\n    endpointIdAlg: {")
+                .append(params.size()).append("}");
+            params.add(p.getEndpointIdentificationAlgorithm());
+        }
+
         if (p.getServerNames() != null) {
             for (SNIServerName sname : p.getServerNames()) {
                 sb.append("\n    server name: {")
--- a/src/java.net.http/share/classes/jdk/internal/net/http/common/Utils.java	Tue Feb 13 16:22:49 2018 +0000
+++ b/src/java.net.http/share/classes/jdk/internal/net/http/common/Utils.java	Wed Feb 14 16:04:18 2018 +0000
@@ -30,7 +30,6 @@
 import sun.net.util.IPAddressUtil;
 import sun.net.www.HeaderParser;
 
-import javax.net.ssl.SSLParameters;
 import java.io.ByteArrayOutputStream;
 import java.io.Closeable;
 import java.io.IOException;
@@ -60,6 +59,9 @@
 import java.util.function.Supplier;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.ExtendedSSLSession;
 
 import static java.util.stream.Collectors.joining;
 
@@ -284,17 +286,48 @@
     /**
      * If the address was created with a domain name, then return
      * the domain name string. If created with a literal IP address
-     * then return null. We do this to avoid doing a reverse lookup
+     * then return null except if the literal is loopback.
+     * We do this to avoid doing a reverse lookup
      * Used to populate the TLS SNI parameter. So, SNI is only set
-     * when a domain name was supplied.
+     * when a domain name was supplied except in case of loopback
+     * where we return "localhost".
      */
     public static String getServerName(InetSocketAddress addr) {
         String host = addr.getHostString();
-        if (IPAddressUtil.textToNumericFormatV4(host) != null)
-            return null;
-        if (IPAddressUtil.textToNumericFormatV6(host) != null)
-            return null;
-        return host;
+        byte[] literal = IPAddressUtil.textToNumericFormatV4(host);
+        if (literal == null) {
+            // not IPv4 literal
+            literal = IPAddressUtil.textToNumericFormatV6(host);
+            if (literal == null) {
+                // not IPv6 literal. Must be domain name
+                return host;
+            } else { // check if loopback
+                if (isLoopbackLiteral(literal))
+                    return "localhost";
+                else
+                    return null;
+            }
+        } else {
+            // check if IPv4 loopback
+            if (isLoopbackLiteral(literal))
+                    return "localhost";
+                else
+                    return null;
+        }
+    }
+
+    private static boolean isLoopbackLiteral(byte[] bytes) {
+        if (bytes.length == 4) {
+            return bytes[0] == 127;
+        } else if (bytes.length == 16) {
+            for (int i=0; i<14; i++)
+                if (bytes[i] != 0)
+                    return false;
+            if (bytes[15] != 1)
+                return false;
+            return true;
+        } else
+            throw new InternalError();
     }
 
     /*
@@ -752,4 +785,14 @@
         Level outLevel = on ? Level.ALL : Level.OFF;
         return getHpackLogger(dbgTag, outLevel);
     }
+
+    /**
+     * SSLSessions returned to user are wrapped in an immutable object
+     */
+    public static SSLSession immutableSession(SSLSession session) {
+        if (session instanceof ExtendedSSLSession)
+            return new ImmutableExtendedSSLSession((ExtendedSSLSession)session);
+        else
+            return new ImmutableSSLSession(session);
+    }
 }
--- a/test/jdk/java/net/httpclient/ManyRequestsLegacy.java	Tue Feb 13 16:22:49 2018 +0000
+++ b/test/jdk/java/net/httpclient/ManyRequestsLegacy.java	Wed Feb 14 16:04:18 2018 +0000
@@ -139,12 +139,8 @@
             @Override
             public byte[] body() {return response;}
             @Override
-            public SSLParameters sslParameters() {
-                try {
-                    return SSLContext.getDefault().getDefaultSSLParameters();
-                } catch (NoSuchAlgorithmException ex) {
-                    throw new UnsupportedOperationException(ex);
-                }
+            public Optional<SSLSession> sslSession() {
+                return Optional.empty(); // for now
             }
             @Override
             public URI uri() { return request.uri();}
--- a/test/jdk/java/net/httpclient/SmokeTest.java	Tue Feb 13 16:22:49 2018 +0000
+++ b/test/jdk/java/net/httpclient/SmokeTest.java	Wed Feb 14 16:04:18 2018 +0000
@@ -32,7 +32,7 @@
  * @compile ../../../com/sun/net/httpserver/LogFilter.java
  * @compile ../../../com/sun/net/httpserver/EchoHandler.java
  * @compile ../../../com/sun/net/httpserver/FileServerHandler.java
- * @run main/othervm -Djdk.internal.httpclient.debug=true -Djdk.httpclient.HttpClient.log=errors,trace SmokeTest
+ * @run main/othervm -Djdk.internal.httpclient.debugX=true -Djdk.httpclient.HttpClient.log=errors,ssl,trace SmokeTest
  */
 
 import com.sun.net.httpserver.Headers;
@@ -753,7 +753,8 @@
         s1.setExecutor(executor);
         s2.setExecutor(executor);
         ctx = new SimpleSSLContext().get();
-        sslparams = ctx.getSupportedSSLParameters();
+        sslparams = ctx.getDefaultSSLParameters();
+        //sslparams.setProtocols(new String[]{"TLSv1.2"});
         s2.setHttpsConfigurator(new Configurator(ctx));
         s1.start();
         s2.start();
@@ -874,7 +875,9 @@
         }
 
         public void configure (HttpsParameters params) {
-            params.setSSLParameters (getSSLContext().getSupportedSSLParameters());
+            SSLParameters p = getSSLContext().getDefaultSSLParameters();
+            //p.setProtocols(new String[]{"TLSv1.2"});
+            params.setSSLParameters (p);
         }
     }
 
Binary file test/jdk/java/net/httpclient/http2/keystore.p12 has changed
--- a/test/jdk/java/net/httpclient/offline/FixedHttpResponse.java	Tue Feb 13 16:22:49 2018 +0000
+++ b/test/jdk/java/net/httpclient/offline/FixedHttpResponse.java	Wed Feb 14 16:04:18 2018 +0000
@@ -21,7 +21,7 @@
  * questions.
  */
 
-import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLSession;
 import java.net.URI;
 import java.util.Optional;
 import java.net.http.HttpClient;
@@ -38,7 +38,7 @@
     private final HttpRequest request;
     private final HttpHeaders headers;
     private final T body;
-    private final SSLParameters sslParameters;
+    private final SSLSession sslSession;
     private final URI uri;
     private final HttpClient.Version version;
 
@@ -46,14 +46,14 @@
                              HttpRequest request,
                              HttpHeaders headers,
                              T body,
-                             SSLParameters sslParameters,
+                             SSLSession sslSession,
                              URI uri,
                              HttpClient.Version version) {
         this.statusCode = statusCode;
         this.request = request;
         this.headers = headers;
         this.body = body;
-        this.sslParameters = sslParameters;
+        this.sslSession = sslSession;
         this.uri = uri;
         this.version = version;
     }
@@ -84,8 +84,8 @@
     }
 
     @Override
-    public SSLParameters sslParameters() {
-        return sslParameters;
+    public Optional<SSLSession> sslSession() {
+        return Optional.ofNullable(sslSession);
     }
 
     @Override
--- a/test/jdk/java/net/httpclient/offline/FixedResponseHttpClient.java	Tue Feb 13 16:22:49 2018 +0000
+++ b/test/jdk/java/net/httpclient/offline/FixedResponseHttpClient.java	Wed Feb 14 16:04:18 2018 +0000
@@ -169,7 +169,7 @@
                                 request,
                                 responseHeaders,
                                 body,
-                                this.sslParameters(),
+                                null,
                                 request.uri(),
                                 request.version().orElse(Version.HTTP_2)));
                     else
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/ssltest/CertificateTest.java	Wed Feb 14 16:04:18 2018 +0000
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpClient.Version;
+import java.net.http.HttpResponse.BodyHandler;
+import static java.net.http.HttpResponse.BodyHandler.asString;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLParameters;
+
+/*
+ * @test
+ * @build Server CertificateTest
+ * @run main/othervm CertificateTest good
+ * @run main/othervm CertificateTest bad
+ */
+
+/**
+ * The test runs twice. In both cases it uses a valid self-signed certificate
+ * that is installed in the trust store (so is trusted) and the same cert is supplied
+ * by the server for its own identity. Two servers on two different ports are used
+ * on the remote end.
+ *
+ * For the "good" run the cert contains the correct hostname of the target server
+ * and therefore should be accepted by the cert checking code in the client.
+ * For the "bad" run, the cert contains an invalid hostname, and should be rejected.
+ */
+public class CertificateTest {
+    static SSLContext ctx;
+    static SSLParameters params;
+    static boolean good;
+    static String trustStoreProp;
+    static String suffix;
+    static Server server;
+    static int port;
+
+    static String TESTSRC = System.getProperty("test.src");
+    public static void main(String[] args) throws Exception
+    {
+        try {
+            if (args[0].equals("good")) {
+                good = true;
+                trustStoreProp = TESTSRC + "/good.keystore";
+            } else {
+                good = false;
+                trustStoreProp = TESTSRC + "/bad.keystore";
+            }
+            server = new Server(trustStoreProp);
+            port = server.getPort();
+            System.setProperty("javax.net.ssl.trustStore", trustStoreProp);
+            System.setProperty("javax.net.ssl.trustStorePassword", "passphrase");
+            init();
+            test(args);
+        } finally {
+            server.stop();
+        }
+    }
+
+    static void init() throws Exception
+    {
+        ctx = SSLContext.getDefault();
+        params = ctx.getDefaultSSLParameters();
+        //params.setProtocols(new String[] { "TLSv1.2" });
+    }
+
+    static void test(String[] args) throws Exception
+    {
+        String uri_s = "https://127.0.0.1:" + Integer.toString(port) + "/foo";
+        String error = null;
+        Exception exception = null;
+        System.out.println("Making request to " + uri_s);
+        HttpClient client = HttpClient.newBuilder()
+                .sslContext(ctx)
+                .sslParameters(params)
+                .build();
+
+        HttpRequest request = HttpRequest.newBuilder(new URI(uri_s))
+                .version(HttpClient.Version.HTTP_1_1)
+                .GET()
+                .build();
+
+        try {
+            HttpResponse<String> response = client.send(request, asString());
+            System.out.printf("Status code %d received\n", response.statusCode());
+            if (good && response.statusCode() != 200)
+                error = "Test failed: good: status should be 200";
+            else if (!good)
+                error = "Test failed: bad: status should not be 200";
+        } catch (Exception e) {
+            System.err.println("Exception good = " + good);
+            exception = e;
+            if (good)
+                error = "Test failed: good: got exception";
+        }
+        if (error != null)
+            throw new RuntimeException(error, exception);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/ssltest/Server.java	Wed Feb 14 16:04:18 2018 +0000
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import com.sun.net.httpserver.*;
+import java.io.*;
+import java.net.InetSocketAddress;
+import java.net.URI;
+import java.security.*;
+import java.util.*;
+import java.util.logging.*;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import javax.net.ssl.*;
+
+public class Server {
+
+    HttpsServer server;
+    final ExecutorService exec;
+    final int port;
+
+    // certfile: needs to be good or bad, ie. bad contains an otherwise valid
+    // cert but whose CN contains a different host. good must be correct
+
+    // assuming the TLS handshake succeeds, the server returns a 200 OK
+    // response with a short text string.
+    public Server(String certfile) throws Exception {
+        initLogger();
+        SSLContext ctx = getContext("TLSv1.2", certfile);
+        Configurator cfg = new Configurator(ctx);
+        server = HttpsServer.create(new InetSocketAddress(0), 10);
+        server.setHttpsConfigurator(cfg);
+        server.createContext("/", new MyHandler());
+        server.setExecutor((exec=Executors.newCachedThreadPool()));
+        port = server.getAddress().getPort();
+        System.out.println ("Listening on port " + port);
+        server.start();
+    }
+
+    int getPort() {
+        return port;
+    }
+
+    void stop() {
+        server.stop(1);
+        exec.shutdownNow();
+    }
+
+    SSLContext getContext(String protocol, String certfile) throws Exception {
+        char[] passphrase = "passphrase".toCharArray();
+        KeyStore ks = KeyStore.getInstance("JKS");
+        ks.load(new FileInputStream(certfile), passphrase);
+
+        KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
+        kmf.init(ks, passphrase);
+
+        TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
+        tmf.init(ks);
+
+        SSLContext ssl = SSLContext.getInstance(protocol);
+        ssl.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
+        return ssl;
+    }
+
+    Logger logger;
+
+    void initLogger() {
+        logger = Logger.getLogger("com.sun.net.httpserver");
+        Handler h = new ConsoleHandler();
+        logger.setLevel(Level.ALL);
+        h.setLevel(Level.ALL);
+        logger.addHandler(h);
+    }
+
+    String responseBody = "Greetings from localhost";
+
+    class MyHandler implements HttpHandler {
+
+        @Override
+        public void handle(HttpExchange e) throws IOException {
+            System.out.println("Server: received " + e.getRequestURI());
+            InputStream is = e.getRequestBody();
+            byte[] buf = new byte[128];
+            while (is.read(buf) != -1);
+            is.close();
+            e.sendResponseHeaders(200, responseBody.length());
+            OutputStream os = e.getResponseBody();
+            os.write(responseBody.getBytes("ISO8859_1"));
+            os.close();
+        }
+    }
+
+    class Configurator extends HttpsConfigurator {
+        public Configurator(SSLContext ctx) throws Exception {
+            super(ctx);
+        }
+
+        public void configure(HttpsParameters params) {
+            SSLParameters p = getSSLContext().getDefaultSSLParameters();
+            for (String cipher : p.getCipherSuites())
+                System.out.println("Cipher: " + cipher);
+            System.err.println("PArams = " + p);
+            params.setSSLParameters(p);
+        }
+    }
+}
Binary file test/jdk/java/net/httpclient/ssltest/bad.keystore has changed
Binary file test/jdk/java/net/httpclient/ssltest/good.keystore has changed
--- a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/AuthenticationFilterTest.java	Tue Feb 13 16:22:49 2018 +0000
+++ b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/AuthenticationFilterTest.java	Wed Feb 14 16:04:18 2018 +0000
@@ -209,7 +209,7 @@
         HttpHeadersImpl headers = new HttpHeadersImpl();
         headers.addHeader(authenticate(proxy!=null),
                 "Basic realm=\"earth\"");
-        Response response = new Response(req, exchange, headers, unauthorized, v);
+        Response response = new Response(req, exchange, headers, null, unauthorized, v);
         out.println("Simulating " + unauthorized
                 + " response from " + uri);
         HttpRequestImpl next = filter.response(response);
@@ -232,7 +232,7 @@
         check(reqURI, next.getSystemHeaders(), proxy);
         check(next.uri(), next.getSystemHeaders(), proxy);
         out.println("Simulating  successful response 200 from " + uri);
-        response = new Response(next, exchange, new HttpHeadersImpl(), 200, v);
+        response = new Response(next, exchange, new HttpHeadersImpl(), null, 200, v);
         next = filter.response(response);
         assertTrue(next == null, "next should be null");
         assertEquals(authenticator.COUNTER.get(), 1);
@@ -401,7 +401,7 @@
             headers5.addHeader(authenticate(false),
                     "Basic realm=\"earth\"");
             unauthorized = 401;
-            Response response5 = new Response(req5, exchange5, headers5, unauthorized, v);
+            Response response5 = new Response(req5, exchange5, headers5, null, unauthorized, v);
             out.println("Simulating " + unauthorized
                     + " response from " + uri);
             HttpRequestImpl next5 = filter.response(response5);
@@ -415,7 +415,7 @@
             // now simulate a 200 response from the server
             exchange5 = new Exchange<>(next5, multi5);
             filter.request(next5, multi5);
-            response5 = new Response(next5, exchange5, new HttpHeadersImpl(), 200, v);
+            response5 = new Response(next5, exchange5, new HttpHeadersImpl(), null, 200, v);
             filter.response(response5);
             assertEquals(authenticator.COUNTER.get(), 2);
 
Binary file test/jdk/lib/testlibrary/jdk/testlibrary/testkeys has changed