# HG changeset patch # User michaelm # Date 1463585948 -3600 # Node ID a2105ea409ec3ab183ca5ae45546c44893f25c0d # Parent 24e6bb1a50ac694376e8b9e44b8d815c9b027817 8157107: HTTP/2 client may fail with NPE if additional logging enabled Reviewed-by: michaelm Contributed-by: artem.smotrakov@oracle.com diff -r 24e6bb1a50ac -r a2105ea409ec jdk/src/java.httpclient/share/classes/java/net/http/AsyncSSLDelegate.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/AsyncSSLDelegate.java Wed May 18 14:47:28 2016 +0000 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/AsyncSSLDelegate.java Wed May 18 16:39:08 2016 +0100 @@ -26,7 +26,6 @@ import java.io.Closeable; import java.io.IOException; import java.nio.ByteBuffer; -import java.util.Arrays; import java.util.LinkedList; import java.util.concurrent.ExecutorService; import java.util.function.Consumer; @@ -557,25 +556,37 @@ } static void logParams(SSLParameters p) { - if (!Log.ssl()) + if (!Log.ssl()) { return; + } + Log.logSSL("SSLParameters:"); if (p == null) { Log.logSSL("Null params"); return; } - for (String cipher : p.getCipherSuites()) { - Log.logSSL("cipher: {0}\n", cipher); + + if (p.getCipherSuites() != null) { + for (String cipher : p.getCipherSuites()) { + Log.logSSL("cipher: {0}\n", cipher); + } } + + // SSLParameters.getApplicationProtocols() can't return null for (String approto : p.getApplicationProtocols()) { Log.logSSL("application protocol: {0}\n", approto); } - for (String protocol : p.getProtocols()) { - Log.logSSL("protocol: {0}\n", protocol); + + if (p.getProtocols() != null) { + for (String protocol : p.getProtocols()) { + Log.logSSL("protocol: {0}\n", protocol); + } } - if (p.getServerNames() != null) + + if (p.getServerNames() != null) { for (SNIServerName sname : p.getServerNames()) { Log.logSSL("server name: {0}\n", sname.toString()); + } } } diff -r 24e6bb1a50ac -r a2105ea409ec jdk/src/java.httpclient/share/classes/java/net/http/Log.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/Log.java Wed May 18 14:47:28 2016 +0000 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/Log.java Wed May 18 16:39:08 2016 +0100 @@ -88,7 +88,7 @@ logging |= TRACE; break; case "all": - logging |= CONTENT|HEADERS|REQUESTS|FRAMES|ERRORS|TRACE; + logging |= CONTENT|HEADERS|REQUESTS|FRAMES|ERRORS|TRACE|SSL; break; } if (val.startsWith("frames")) { diff -r 24e6bb1a50ac -r a2105ea409ec jdk/test/java/net/httpclient/http2/TLSConnection.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/net/httpclient/http2/TLSConnection.java Wed May 18 16:39:08 2016 +0100 @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2016, 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.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.http.Http2Handler; +import java.net.http.Http2TestExchange; +import java.net.http.Http2TestServer; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLSession; + +/* + * @test + * @bug 8150769 8157107 + * @summary Checks that SSL parameters can be set for HTTP/2 connection + * @modules java.httpclient + * @compile/module=java.httpclient java/net/http/Http2Handler.java + * @compile/module=java.httpclient java/net/http/Http2TestExchange.java + * @compile/module=java.httpclient java/net/http/Http2TestServer.java + * @run main/othervm TLSConnection + */ +public class TLSConnection { + + private static final String KEYSTORE = System.getProperty("test.src") + + File.separator + "keystore.p12"; + private static final String PASSWORD = "password"; + + public static void main(String[] args) throws Exception { + + // enable all logging + System.setProperty("java.net.http.HttpClient.log", "all,frames:all"); + + // initialize JSSE + System.setProperty("javax.net.ssl.keyStore", KEYSTORE); + System.setProperty("javax.net.ssl.keyStorePassword", PASSWORD); + System.setProperty("javax.net.ssl.trustStore", KEYSTORE); + System.setProperty("javax.net.ssl.trustStorePassword", PASSWORD); + + Handler handler = new Handler(); + + try (Http2TestServer server = new Http2TestServer(true, 0, handler)) { + server.start(); + + int port = server.getAddress().getPort(); + String uriString = "https://127.0.0.1:" + Integer.toString(port); + + // run test cases + boolean success = true; + + SSLParameters parameters = null; + success &= expectFailure( + "Test #1: SSL parameters is null, expect NPE", + () -> connect(uriString, parameters), + NullPointerException.class); + + success &= expectSuccess( + "Test #2: default SSL parameters, " + + "expect successful connection", + () -> connect(uriString, new SSLParameters())); + success &= checkProtocol(handler.getSSLSession(), "TLSv1.2"); + + // set SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA cipher suite + // which has less priority in default cipher suite list + success &= expectSuccess( + "Test #3: SSL parameters with " + + "SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA cipher suite, " + + "expect successful connection", + () -> connect(uriString, new SSLParameters( + new String[] { "SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA" }))); + success &= checkProtocol(handler.getSSLSession(), "TLSv1.2"); + success &= checkCipherSuite(handler.getSSLSession(), + "SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA"); + + // set TLS_RSA_WITH_AES_128_CBC_SHA cipher suite + // which has less priority in default cipher suite list + // also set TLSv11 protocol + success &= expectSuccess( + "Test #4: SSL parameters with " + + "TLS_RSA_WITH_AES_128_CBC_SHA cipher suite," + + " expect successful connection", + () -> connect(uriString, new SSLParameters( + new String[] { "TLS_RSA_WITH_AES_128_CBC_SHA" }, + new String[] { "TLSv1.1" }))); + success &= checkProtocol(handler.getSSLSession(), "TLSv1.1"); + success &= checkCipherSuite(handler.getSSLSession(), + "TLS_RSA_WITH_AES_128_CBC_SHA"); + + if (success) { + System.out.println("Test passed"); + } else { + throw new RuntimeException("At least one test case failed"); + } + } + } + + private static interface Test { + + public void run() throws Exception; + } + + private static class Handler implements Http2Handler { + + private static final byte[] BODY = "Test response".getBytes(); + + private volatile SSLSession sslSession; + + @Override + public void handle(Http2TestExchange t) throws IOException { + System.out.println("Handler: received request to " + + t.getRequestURI()); + + try (InputStream is = t.getRequestBody()) { + byte[] body = is.readAllBytes(); + System.out.println("Handler: read " + body.length + + " bytes of body: "); + System.out.println(new String(body)); + } + + try (OutputStream os = t.getResponseBody()) { + t.sendResponseHeaders(200, BODY.length); + os.write(BODY); + } + + sslSession = t.getSSLSession(); + } + + SSLSession getSSLSession() { + return sslSession; + } + } + + private static void connect(String uriString, SSLParameters sslParameters) + throws URISyntaxException, IOException, InterruptedException { + + String body = HttpClient.create() + .sslParameters(sslParameters) + .version(HttpClient.Version.HTTP_2) + .build() + .request(new URI(uriString)) + .body(HttpRequest.fromString("body")) + .GET() + .response() + .body(HttpResponse.asString()); + + System.out.println("Response: " + body); + } + + private static boolean checkProtocol(SSLSession session, String protocol) { + if (session == null) { + System.out.println("Check protocol: no session provided"); + return false; + } + + System.out.println("Check protocol: negotiated protocol: " + + session.getProtocol()); + System.out.println("Check protocol: expected protocol: " + + protocol); + if (!protocol.equals(session.getProtocol())) { + System.out.println("Check protocol: unexpected negotiated protocol"); + return false; + } + + return true; + } + + private static boolean checkCipherSuite(SSLSession session, String ciphersuite) { + if (session == null) { + System.out.println("Check protocol: no session provided"); + return false; + } + + System.out.println("Check protocol: negotiated ciphersuite: " + + session.getCipherSuite()); + System.out.println("Check protocol: expected ciphersuite: " + + ciphersuite); + if (!ciphersuite.equals(session.getCipherSuite())) { + System.out.println("Check protocol: unexpected negotiated ciphersuite"); + return false; + } + + return true; + } + + private static boolean expectSuccess(String message, Test test) { + System.out.println(message); + try { + test.run(); + System.out.println("Passed"); + return true; + } catch (Exception e) { + System.out.println("Failed: unexpected exception:"); + e.printStackTrace(System.out); + return false; + } + } + + private static boolean expectFailure(String message, Test test, + Class expectedException) { + + System.out.println(message); + try { + test.run(); + System.out.println("Failed: unexpected successful connection"); + return false; + } catch (Exception e) { + System.out.println("Got an exception:"); + e.printStackTrace(System.out); + if (expectedException != null + && !expectedException.isAssignableFrom(e.getClass())) { + System.out.printf("Failed: expected %s, but got %s%n", + expectedException.getName(), + e.getClass().getName()); + return false; + } + System.out.println("Passed: expected exception"); + return true; + } + } + +} diff -r 24e6bb1a50ac -r a2105ea409ec jdk/test/java/net/httpclient/http2/java.httpclient/java/net/http/Http2TestServer.java --- a/jdk/test/java/net/httpclient/http2/java.httpclient/java/net/http/Http2TestServer.java Wed May 18 14:47:28 2016 +0000 +++ b/jdk/test/java/net/httpclient/http2/java.httpclient/java/net/http/Http2TestServer.java Wed May 18 16:39:08 2016 +0100 @@ -42,7 +42,7 @@ * Http2Handler on additional threads. All threads * obtained from the supplied ExecutorService. */ -public class Http2TestServer { +public class Http2TestServer implements AutoCloseable { final ServerSocket server; boolean secure; SettingsFrame serverSettings, clientSettings; @@ -156,4 +156,9 @@ }); } + @Override + public void close() throws Exception { + stop(); + } + }