Path: blob/master/test/jdk/sun/net/www/http/KeepAliveCache/B5045306.java
41155 views
/*1* Copyright (c) 2005, 2019, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/*24* @test25* @bug 5045306 6356004 6993490 825512426* @modules java.base/sun.net.www27* java.management28* @library ../../httptest/29* @build HttpCallback TestHttpServer HttpTransaction30* @run main/othervm B504530631* @summary Http keep-alive implementation is not efficient32*/3334import java.net.*;35import java.io.*;36import java.lang.management.*;37import java.util.ArrayList;38import java.util.List;3940/* Part 1:41* The http client makes a connection to a URL whos content contains a lot of42* data, more than can fit in the socket buffer. The client only reads43* 1 byte of the data from the InputStream leaving behind more data than can44* fit in the socket buffer. The client then makes a second call to the http45* server. If the connection port used by the client is the same as for the46* first call then that means that the connection is being reused.47*48* Part 2:49* Test buggy webserver that sends less data than it specifies in its50* Content-length header.51*/5253public class B504530654{55static SimpleHttpTransaction httpTrans;56static TestHttpServer server;5758public static void main(String[] args) throws Exception {59startHttpServer();60clientHttpCalls();61}6263public static void startHttpServer() {64try {65httpTrans = new SimpleHttpTransaction();66server = new TestHttpServer(httpTrans, 1, 10, InetAddress.getLocalHost(), 0);67} catch (IOException e) {68e.printStackTrace();69}70}7172public static void clientHttpCalls() {73List<Throwable> uncaught = new ArrayList<>();74Thread.setDefaultUncaughtExceptionHandler((t, ex) -> {75uncaught.add(ex);76});77try {78System.out.println("http server listen on: " + server.getLocalPort());79String hostAddr = InetAddress.getLocalHost().getHostAddress();80if (hostAddr.indexOf(':') > -1) hostAddr = "[" + hostAddr + "]";81String baseURLStr = "http://" + hostAddr + ":" + server.getLocalPort() + "/";8283URL bigDataURL = new URL (baseURLStr + "firstCall");84URL smallDataURL = new URL (baseURLStr + "secondCall");8586HttpURLConnection uc = (HttpURLConnection)bigDataURL.openConnection(Proxy.NO_PROXY);8788//Only read 1 byte of response data and close the stream89InputStream is = uc.getInputStream();90byte[] ba = new byte[1];91is.read(ba);92is.close();9394// Allow the KeepAliveStreamCleaner thread to read the data left behind and cache the connection.95try { Thread.sleep(2000); } catch (Exception e) {}9697uc = (HttpURLConnection)smallDataURL.openConnection(Proxy.NO_PROXY);98uc.getResponseCode();99100if (SimpleHttpTransaction.failed)101throw new RuntimeException("Failed: Initial Keep Alive Connection is not being reused");102103// Part 2104URL part2Url = new URL (baseURLStr + "part2");105uc = (HttpURLConnection)part2Url.openConnection(Proxy.NO_PROXY);106is = uc.getInputStream();107is.close();108109// Allow the KeepAliveStreamCleaner thread to try and read the data left behind and cache the connection.110try { Thread.sleep(2000); } catch (Exception e) {}111112ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();113if (threadMXBean.isThreadCpuTimeSupported()) {114long[] threads = threadMXBean.getAllThreadIds();115ThreadInfo[] threadInfo = threadMXBean.getThreadInfo(threads);116for (int i=0; i<threadInfo.length; i++) {117if (threadInfo[i].getThreadName().equals("Keep-Alive-SocketCleaner")) {118System.out.println("Found Keep-Alive-SocketCleaner thread");119long threadID = threadInfo[i].getThreadId();120long before = threadMXBean.getThreadCpuTime(threadID);121try { Thread.sleep(2000); } catch (Exception e) {}122long after = threadMXBean.getThreadCpuTime(threadID);123124if (before ==-1 || after == -1)125break; // thread has died, OK126127// if Keep-Alive-SocketCleaner consumes more than 50% of cpu then we128// can assume a recursive loop.129long total = after - before;130if (total >= 1000000000) // 1 second, or 1 billion nanoseconds131throw new RuntimeException("Failed: possible recursive loop in Keep-Alive-SocketCleaner");132}133}134}135136} catch (IOException e) {137e.printStackTrace();138} finally {139server.terminate();140}141if (!uncaught.isEmpty()) {142throw new RuntimeException("Unhandled exception:", uncaught.get(0));143}144}145}146147class SimpleHttpTransaction implements HttpCallback148{149static boolean failed = false;150151// Need to have enough data here that is too large for the socket buffer to hold.152// Also http.KeepAlive.remainingData must be greater than this value, default is 256K.153static final int RESPONSE_DATA_LENGTH = 128 * 1024;154155int port1;156157public void request(HttpTransaction trans) {158try {159String path = trans.getRequestURI().getPath();160if (path.equals("/firstCall")) {161port1 = trans.channel().socket().getPort();162System.out.println("First connection on client port = " + port1);163164byte[] responseBody = new byte[RESPONSE_DATA_LENGTH];165for (int i=0; i<responseBody.length; i++)166responseBody[i] = 0x41;167trans.setResponseEntityBody (responseBody, responseBody.length);168trans.sendResponse(200, "OK");169} else if (path.equals("/secondCall")) {170int port2 = trans.channel().socket().getPort();171System.out.println("Second connection on client port = " + port2);172173if (port1 != port2)174failed = true;175176trans.setResponseHeader ("Content-length", Integer.toString(0));177178/* Force the server to not respond for more that the timeout179* set by the keepalive cleaner (5000 millis). This ensures the180* timeout is correctly resets the default read timeout,181* infinity. See 6993490. */182System.out.println("server sleeping...");183try {Thread.sleep(6000); } catch (InterruptedException e) {}184185trans.sendResponse(200, "OK");186} else if(path.equals("/part2")) {187System.out.println("Call to /part2");188byte[] responseBody = new byte[RESPONSE_DATA_LENGTH];189for (int i=0; i<responseBody.length; i++)190responseBody[i] = 0x41;191trans.setResponseEntityBody (responseBody, responseBody.length);192193// override the Content-length header to be greater than the actual response body194trans.setResponseHeader("Content-length", Integer.toString(responseBody.length+1));195trans.sendResponse(200, "OK");196197// now close the socket198trans.channel().socket().close();199}200} catch (Exception e) {201e.printStackTrace();202}203}204}205206207