Path: blob/master/test/jdk/sun/net/www/http/HttpClient/MultiThreadTest.java
41155 views
/*1* Copyright (c) 2002, 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 463662826* @summary HttpURLConnection duplicates HTTP GET requests when used with multiple threads27*/2829/*30* This tests keep-alive behavior using chunkedinputstreams31* It checks that keep-alive connections are used and also32* that requests are not being repeated (due to errors)33*34* It also checks that the keepalive connections are closed eventually35* because the test will not terminate if the connections36* are not closed by the keep-alive timer.37*/3839import java.net.*;40import java.io.*;41import java.time.Duration;42import java.util.Queue;43import java.util.concurrent.ConcurrentLinkedQueue;44import java.util.concurrent.atomic.AtomicInteger;4546public class MultiThreadTest extends Thread {4748/*49* Is debugging enabled - start with -d to enable.50*/51static boolean debug = true; // disable debug once stability proven5253static Object threadlock = new Object ();54static int threadCounter = 0;5556static Object getLock() { return threadlock; }5758static void debug(String msg) {59if (debug)60System.out.println(msg);61}6263static final AtomicInteger reqnum = new AtomicInteger();6465void doRequest(String uri) throws Exception {66URL url = new URL(uri + "?foo="+reqnum.getAndIncrement());67HttpURLConnection http = (HttpURLConnection)url.openConnection();68InputStream in = http.getInputStream();69byte b[] = new byte[100];70int total = 0;71int n;72do {73n = in.read(b);74if (n > 0) total += n;75} while (n > 0);76debug ("client: read " + total + " bytes");77in.close();78http.disconnect();79}8081String uri;82byte[] b;83int requests;8485MultiThreadTest(String authority, int requests) throws Exception {86uri = "http://" + authority + "/foo.html";8788b = new byte [256];89this.requests = requests;9091synchronized (threadlock) {92threadCounter ++;93}94}9596public void run() {97long start = System.nanoTime();9899try {100for (int i=0; i<requests; i++) {101doRequest (uri);102}103} catch (Exception e) {104throw new RuntimeException (e.getMessage());105} finally {106synchronized (threadlock) {107threadCounter --;108if (threadCounter == 0) {109threadlock.notifyAll();110}111}112}113debug("client: end - " + Duration.ofNanos(System.nanoTime() - start));114}115116static int threads=5;117118public static void main(String args[]) throws Exception {119long start = System.nanoTime();120121int x = 0, arg_len = args.length;122int requests = 20;123124if (arg_len > 0 && args[0].equals("-d")) {125debug = true;126x = 1;127arg_len --;128}129if (arg_len > 0) {130threads = Integer.parseInt (args[x]);131requests = Integer.parseInt (args[x+1]);132}133134/* start the server */135InetAddress loopback = InetAddress.getLoopbackAddress();136ServerSocket ss = new ServerSocket();137ss.bind(new InetSocketAddress(loopback, 0));138Server svr = new Server(ss);139svr.start();140141Object lock = MultiThreadTest.getLock();142synchronized (lock) {143for (int i=0; i<threads; i++) {144MultiThreadTest t = new MultiThreadTest(svr.getAuthority(), requests);145t.start ();146}147try {148lock.wait();149} catch (InterruptedException e) {}150}151152// shutdown server - we're done.153svr.shutdown();154155int cnt = svr.connectionCount();156MultiThreadTest.debug("Connections = " + cnt);157int reqs = Worker.getRequests ();158MultiThreadTest.debug("Requests = " + reqs);159System.out.println ("Connection count = " + cnt + " Request count = " + reqs);160161// We may have received traffic from something else than162// our client. We should only count those workers for which163// the expected header has been found.164int validConnections = 0;165for (Worker w : svr.workers()) {166if (w.headerFound) validConnections++;167}168169if (validConnections > threads + 1 || validConnections == 0) { // could be less170throw new RuntimeException ("Expected " + threads + " connections: used " + validConnections);171}172173// Sometimes the client drops a connection after a while and174// spawns a new one. Why this is happening is not clear,175// and JDK-8223783 is logged to follow up on this. For the sake176// of test stabilization we don't fail on `threads + 1` connections177// but log a warning instead.178if (validConnections == threads + 1) {179debug("WARNING: " + validConnections180+ " have been used, where only " + threads181+ " were expected!");182}183184if (validConnections != cnt) {185debug("WARNING: got " + (cnt - validConnections) + " unexpected connections!");186}187if (validConnections == cnt && reqs != threads*requests) {188throw new RuntimeException ("Expected "+ threads*requests+ " requests: got " +reqs);189}190191for (Thread worker : svr.workers()) {192worker.join(60_000);193}194195debug("main thread end - " + Duration.ofNanos(System.nanoTime() - start));196}197}198199/*200* Server thread to accept connection and create worker threads201* to service each connection.202*/203class Server extends Thread {204ServerSocket ss;205int connectionCount;206boolean shutdown = false;207private final Queue<Worker> workers = new ConcurrentLinkedQueue<>();208209Server(ServerSocket ss) {210this.ss = ss;211}212213public String getAuthority() {214InetAddress address = ss.getInetAddress();215String hostaddr = address.isAnyLocalAddress()216? "localhost" : address.getHostAddress();217if (hostaddr.indexOf(':') > -1) {218hostaddr = "[" + hostaddr + "]";219}220return hostaddr + ":" + ss.getLocalPort();221}222223public Queue<Worker> workers() {224return workers;225}226227public synchronized int connectionCount() {228return connectionCount;229}230231public synchronized void shutdown() {232shutdown = true;233}234235public void run() {236try {237ss.setSoTimeout(2000);238239for (;;) {240Socket s;241try {242MultiThreadTest.debug("server: calling accept.");243s = ss.accept();244MultiThreadTest.debug("server: return accept.");245} catch (SocketTimeoutException te) {246MultiThreadTest.debug("server: STE");247synchronized (this) {248if (shutdown) {249MultiThreadTest.debug("server: Shuting down.");250return;251}252}253continue;254}255256int id;257Worker w;258synchronized (this) {259id = connectionCount++;260w = new Worker(s, id);261workers.add(w);262}263w.start();264MultiThreadTest.debug("server: Started worker " + id);265}266267} catch (Exception e) {268e.printStackTrace();269} finally {270try {271ss.close();272} catch (Exception e) { }273}274}275}276277/*278* Worker thread to service single connection - can service279* multiple http requests on same connection.280*/281class Worker extends Thread {282Socket s;283int id;284volatile boolean headerFound;285286Worker(Socket s, int id) {287super("Worker-" + id);288this.s = s;289this.id = id;290}291292static int requests = 0;293static final Object rlock = new Object();294295public static int getRequests () {296synchronized (rlock) {297return requests;298}299}300301public static void incRequests () {302synchronized (rlock) {303requests++;304}305}306307int readUntil(InputStream in, StringBuilder headers, char[] seq) throws IOException {308int i=0, count=0;309while (true) {310int c = in.read();311if (c == -1)312return -1;313headers.append((char)c);314count++;315if (c == seq[i]) {316i++;317if (i == seq.length)318return count;319continue;320} else {321i = 0;322}323}324}325326public void run() {327long start = System.nanoTime();328329try {330int max = 400;331byte b[] = new byte[1000];332InputStream in = new BufferedInputStream(s.getInputStream());333// response to client334PrintStream out = new PrintStream(335new BufferedOutputStream(336s.getOutputStream() ));337338for (;;) {339340// read entire request from client341int n=0;342StringBuilder headers = new StringBuilder();343n = readUntil(in, headers, new char[] {'\r','\n', '\r','\n'});344if (n <= 0) {345MultiThreadTest.debug("worker: " + id + ": Shutdown");346s.close();347return;348}349if (headers.toString().contains("/foo.html?foo=")) {350headerFound = true;351} else {352MultiThreadTest.debug("worker: " + id + ": Unexpected request received: " + headers);353}354355MultiThreadTest.debug("worker " + id +356": Read request from client " +357"(" + n + " bytes).");358359incRequests();360out.print("HTTP/1.1 200 OK\r\n");361out.print("Transfer-Encoding: chunked\r\n");362out.print("Content-Type: text/html\r\n");363out.print("Connection: Keep-Alive\r\n");364out.print("Keep-Alive: timeout=15, max="+max+"\r\n");365out.print("\r\n");366out.print("6\r\nHello \r\n");367out.print("5\r\nWorld\r\n");368out.print("0\r\n\r\n");369out.flush();370371if (--max == 0) {372s.close();373return;374}375}376377} catch (Exception e) {378e.printStackTrace();379} finally {380try {381s.close();382} catch (Exception e) { }383MultiThreadTest.debug("worker: " + id + " end - " +384Duration.ofNanos(System.nanoTime() - start));385}386}387}388389390