diff --git a/test/jdk/sun/net/www/http/KeepAliveCache/B5045306.java b/test/jdk/sun/net/www/http/KeepAliveCache/B5045306.java index 2d275ebfc9e..b9b9de57cdf 100644 --- a/test/jdk/sun/net/www/http/KeepAliveCache/B5045306.java +++ b/test/jdk/sun/net/www/http/KeepAliveCache/B5045306.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2024, 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 @@ -24,9 +24,9 @@ /* * @test * @bug 5045306 6356004 6993490 8255124 + * @summary Http keep-alive implementation is not efficient * @library /test/lib * @run main/othervm B5045306 - * @summary Http keep-alive implementation is not efficient */ import java.io.IOException; @@ -42,14 +42,17 @@ import java.net.Proxy; import java.net.URL; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer; +import jdk.test.lib.net.URIBuilder; + /* Part 1: - * The http client makes a connection to a URL whos content contains a lot of + * The http client makes a connection to a URL whose content contains a lot of * data, more than can fit in the socket buffer. The client only reads * 1 byte of the data from the InputStream leaving behind more data than can * fit in the socket buffer. The client then makes a second call to the http @@ -63,150 +66,166 @@ import com.sun.net.httpserver.HttpServer; public class B5045306 { static HttpServer server; - - public static void main(String[] args) { - startHttpServer(); - clientHttpCalls(); - } + static ExecutorService executor = Executors.newSingleThreadExecutor(); public static void startHttpServer() { try { - server = HttpServer.create(new InetSocketAddress(InetAddress.getLocalHost(), 0), 10, "/", new SimpleHttpTransactionHandler()); - server.setExecutor(Executors.newSingleThreadExecutor()); - server.start(); + server = HttpServer.create(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0), 10, "/", new SimpleHttpTransactionHandler()); } catch (IOException e) { - e.printStackTrace(); + throw new RuntimeException(e); } + server.setExecutor(executor); + server.start(); + System.out.println("http server listens on: " + server.getAddress()); } - public static void clientHttpCalls() { + public static void stopHttpServer() { + server.stop(1); + executor.shutdown(); + } + + public static void clientHttpCalls() throws Exception { List uncaught = new ArrayList<>(); Thread.setDefaultUncaughtExceptionHandler((t, ex) -> { uncaught.add(ex); }); - try { - System.out.println("http server listen on: " + server.getAddress().getPort()); - String hostAddr = InetAddress.getLocalHost().getHostAddress(); - if (hostAddr.indexOf(':') > -1) hostAddr = "[" + hostAddr + "]"; - String baseURLStr = "http://" + hostAddr + ":" + server.getAddress().getPort() + "/"; - URL bigDataURL = new URL (baseURLStr + "firstCall"); - URL smallDataURL = new URL (baseURLStr + "secondCall"); + URL bigDataURL = URIBuilder.newBuilder() + .scheme("http") + .loopback() + .port(server.getAddress().getPort()) + .path("/firstCall") + .toURL(); - HttpURLConnection uc = (HttpURLConnection)bigDataURL.openConnection(Proxy.NO_PROXY); + URL smallDataURL = URIBuilder.newBuilder() + .scheme("http") + .loopback() + .port(server.getAddress().getPort()) + .path("/secondCall") + .toURL(); - //Only read 1 byte of response data and close the stream - InputStream is = uc.getInputStream(); + HttpURLConnection uc = (HttpURLConnection)bigDataURL.openConnection(Proxy.NO_PROXY); + + // Only read 1 byte of response data and close the stream + try (InputStream is = uc.getInputStream()) { byte[] ba = new byte[1]; is.read(ba); - is.close(); + } - // Allow the KeepAliveStreamCleaner thread to read the data left behind and cache the connection. - try { Thread.sleep(2000); } catch (Exception e) {} + // Allow the KeepAliveStreamCleaner thread to read the data left behind and cache the connection. + try { Thread.sleep(2000); } catch (Exception e) {} - uc = (HttpURLConnection)smallDataURL.openConnection(Proxy.NO_PROXY); - uc.getResponseCode(); + uc = (HttpURLConnection)smallDataURL.openConnection(Proxy.NO_PROXY); + uc.getResponseCode(); - if (SimpleHttpTransactionHandler.failed) - throw new RuntimeException("Failed: Initial Keep Alive Connection is not being reused"); + if (SimpleHttpTransactionHandler.failed) + throw new RuntimeException("Failed: Initial Keep Alive Connection is not being reused"); - // Part 2 - URL part2Url = new URL (baseURLStr + "part2"); - uc = (HttpURLConnection)part2Url.openConnection(Proxy.NO_PROXY); - is = uc.getInputStream(); - is.close(); + // Part 2 + URL part2Url = URIBuilder.newBuilder() + .scheme("http") + .loopback() + .port(server.getAddress().getPort()) + .path("/part2") + .toURL(); - // Allow the KeepAliveStreamCleaner thread to try and read the data left behind and cache the connection. - try { Thread.sleep(2000); } catch (Exception e) {} + uc = (HttpURLConnection)part2Url.openConnection(Proxy.NO_PROXY); + try (InputStream is = uc.getInputStream()) {} - ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); - if (threadMXBean.isThreadCpuTimeSupported()) { - long[] threads = threadMXBean.getAllThreadIds(); - ThreadInfo[] threadInfo = threadMXBean.getThreadInfo(threads); - for (int i=0; i= 1000000000) // 1 second, or 1 billion nanoseconds - throw new RuntimeException("Failed: possible recursive loop in Keep-Alive-SocketCleaner"); - } + if (before ==-1 || after == -1) + break; // thread has died, OK + + // if Keep-Alive-SocketCleaner consumes more than 50% of cpu then we + // can assume a recursive loop. + long total = after - before; + if (total >= 1000000000) // 1 second, or 1 billion nanoseconds + throw new RuntimeException("Failed: possible recursive loop in Keep-Alive-SocketCleaner"); } } - - } catch (IOException e) { - e.printStackTrace(); - } finally { - server.stop(1); } if (!uncaught.isEmpty()) { throw new RuntimeException("Unhandled exception:", uncaught.get(0)); } } -} -class SimpleHttpTransactionHandler implements HttpHandler -{ - static volatile boolean failed = false; + static class SimpleHttpTransactionHandler implements HttpHandler { + static volatile boolean failed = false; - // Need to have enough data here that is too large for the socket buffer to hold. - // Also http.KeepAlive.remainingData must be greater than this value, default is 256K. - static final int RESPONSE_DATA_LENGTH = 128 * 1024; + // Need to have enough data here that is too large for the socket buffer to hold. + // Also http.KeepAlive.remainingData must be greater than this value, default is 256K. + static final int RESPONSE_DATA_LENGTH = 128 * 1024; - int port1; + int port1; - public void handle(HttpExchange trans) { - try { - String path = trans.getRequestURI().getPath(); - if (path.equals("/firstCall")) { - port1 = trans.getRemoteAddress().getPort(); - System.out.println("First connection on client port = " + port1); + public void handle(HttpExchange trans) { + try { + String path = trans.getRequestURI().getPath(); + if (path.equals("/firstCall")) { + port1 = trans.getRemoteAddress().getPort(); + System.out.println("First connection on client port = " + port1); - byte[] responseBody = new byte[RESPONSE_DATA_LENGTH]; - for (int i=0; i passed = new CompletableFuture<>(); - static class Server extends Thread { - final ServerSocket serverSocket; - final int port; + static class Server extends Thread implements AutoCloseable { final String param; // the parameter to test "max" or "timeout" + final ServerSocket serverSocket = new ServerSocket(0); + final int port; volatile Socket s; public Server(String param) throws IOException { - serverSocket = new ServerSocket(0); + this.param = param; port = serverSocket.getLocalPort(); setDaemon(true); - this.param = param; } public int getPort() { return port; } - public void close() { - try { - serverSocket.close(); - if (s != null) - s.close(); - } catch (IOException e) {} + public void close() throws IOException { + serverSocket.close(); + if (s != null) + s.close(); } static final byte[] requestEnd = new byte[] {'\r', '\n', '\r', '\n' }; @@ -125,28 +127,29 @@ public class B8291637 { } } - public static void main(String[] args) throws Exception { - Server server = new Server(args[0]); - int port = server.getPort(); - server.start(); - URL url = new URL("http://127.0.0.1:" + Integer.toString(port) + "/"); - HttpURLConnection urlc = (HttpURLConnection) url.openConnection(); - InputStream i = urlc.getInputStream(); - int c,count=0; - byte[] buf = new byte[256]; - while ((c=i.read(buf)) != -1) { - count+=c; - } - i.close(); - System.out.println("Read " + count ); - try { + public static void runTest(String param) throws Exception { + try (Server server = new Server(param)) { + server.start(); + URL url = URIBuilder.newBuilder() + .scheme("http") + .loopback() + .port(server.getPort()) + .path("/") + .toURL(); + HttpURLConnection urlc = (HttpURLConnection) url.openConnection(); + try (InputStream i = urlc.getInputStream()) { + System.out.println("Read " + i.readAllBytes().length); + } if (!passed.get()) { throw new RuntimeException("Test failed"); } else { System.out.println("Test passed"); } - } finally { - server.close(); } } + + public static void main(String[] args) throws Exception { + runTest("timeout"); + runTest("max"); + } } diff --git a/test/jdk/sun/net/www/http/KeepAliveCache/B8293562.java b/test/jdk/sun/net/www/http/KeepAliveCache/B8293562.java index 0a6e7ef1416..c7202af70dd 100644 --- a/test/jdk/sun/net/www/http/KeepAliveCache/B8293562.java +++ b/test/jdk/sun/net/www/http/KeepAliveCache/B8293562.java @@ -24,20 +24,11 @@ /* * @test * @bug 8293562 + * @summary Http keep-alive thread should close sockets without holding a lock * @library /test/lib * @run main/othervm -Dhttp.keepAlive.time.server=1 B8293562 - * @summary Http keep-alive thread should close sockets without holding a lock */ -import com.sun.net.httpserver.HttpExchange; -import com.sun.net.httpserver.HttpHandler; -import com.sun.net.httpserver.HttpServer; - -import javax.net.ssl.HandshakeCompletedListener; -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.SSLSession; -import javax.net.ssl.SSLSocket; -import javax.net.ssl.SSLSocketFactory; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; @@ -53,6 +44,18 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; +import javax.net.ssl.HandshakeCompletedListener; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; + +import jdk.test.lib.net.URIBuilder; + public class B8293562 { static HttpServer server; static CountDownLatch closing = new CountDownLatch(1); @@ -73,12 +76,13 @@ public class B8293562 { public static void clientHttpCalls() throws Exception { try { - System.out.println("http server listen on: " + server.getAddress().getPort()); - String hostAddr = InetAddress.getLoopbackAddress().getHostAddress(); - if (hostAddr.indexOf(':') > -1) hostAddr = "[" + hostAddr + "]"; - String baseURLStr = "https://" + hostAddr + ":" + server.getAddress().getPort() + "/"; + System.out.println("http server listens on: " + server.getAddress().getPort()); - URL testUrl = new URL (baseURLStr); + URL testUrl = URIBuilder.newBuilder() + .scheme("https") + .loopback() + .port(server.getAddress().getPort()) + .toURL(); // SlowCloseSocketFactory is not a real SSLSocketFactory; // it produces regular non-SSL sockets. Effectively, the request diff --git a/test/jdk/sun/net/www/http/KeepAliveCache/KeepAliveProperty.java b/test/jdk/sun/net/www/http/KeepAliveCache/KeepAliveProperty.java index 83491ad4e38..0e065b1224c 100644 --- a/test/jdk/sun/net/www/http/KeepAliveCache/KeepAliveProperty.java +++ b/test/jdk/sun/net/www/http/KeepAliveCache/KeepAliveProperty.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, 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 @@ -23,18 +23,29 @@ /* * @test - * @library /test/lib * @bug 8278067 + * @library /test/lib * @run main/othervm -Dhttp.keepAlive.time.server=30 KeepAliveProperty long * @run main/othervm -Dhttp.keepAlive.time.server=1 KeepAliveProperty short * @run main/othervm -ea -Dhttp.keepAlive.time.server=0 KeepAliveProperty short */ -import java.net.*; -import java.io.*; -import java.nio.charset.*; -import java.util.logging.*; +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.logging.ConsoleHandler; +import java.util.logging.Level; +import java.util.logging.Logger; + import jdk.test.lib.net.URIBuilder; + import static java.net.Proxy.NO_PROXY; public class KeepAliveProperty { diff --git a/test/jdk/sun/net/www/http/KeepAliveCache/KeepAliveTimerThread.java b/test/jdk/sun/net/www/http/KeepAliveCache/KeepAliveTimerThread.java index 5568f0c54a9..44130b2a820 100644 --- a/test/jdk/sun/net/www/http/KeepAliveCache/KeepAliveTimerThread.java +++ b/test/jdk/sun/net/www/http/KeepAliveCache/KeepAliveTimerThread.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2024, 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 @@ -23,16 +23,26 @@ /* * @test - * @library /test/lib * @bug 4701299 * @summary Keep-Alive-Timer thread management in KeepAliveCache causes memory leak + * @library /test/lib * @run main KeepAliveTimerThread * @run main/othervm -Djava.net.preferIPv6Addresses=true KeepAliveTimerThread */ -import java.net.*; -import java.io.*; +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketTimeoutException; +import java.net.URL; + import jdk.test.lib.net.URIBuilder; + import static java.net.Proxy.NO_PROXY; public class KeepAliveTimerThread { @@ -131,8 +141,5 @@ public class KeepAliveTimerThread { if (grp.activeCount() > 0) { throw new RuntimeException("Keep-alive thread started in wrong thread group"); } - - grp.destroy(); } - }