/* * Copyright (c) 2015, 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. */ /* * @test * @bug 8087112 * @modules jdk.incubator.httpclient * java.logging * jdk.httpserver * @library /lib/testlibrary/ / * @build jdk.testlibrary.SimpleSSLContext ProxyServer EchoHandler * @compile ../../../com/sun/net/httpserver/LogFilter.java * @compile ../../../com/sun/net/httpserver/FileServerHandler.java * @run main/othervm -Djdk.httpclient.HttpClient.log=errors,trace SmokeTest */ import com.sun.net.httpserver.Headers; import com.sun.net.httpserver.HttpContext; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer; import com.sun.net.httpserver.HttpsConfigurator; import com.sun.net.httpserver.HttpsParameters; import com.sun.net.httpserver.HttpsServer; import java.util.concurrent.atomic.AtomicInteger; import java.net.InetSocketAddress; import java.net.PasswordAuthentication; import java.net.ProxySelector; import java.net.ServerSocket; import java.net.Socket; import java.net.URI; import jdk.incubator.http.HttpClient; import jdk.incubator.http.HttpRequest; import jdk.incubator.http.HttpResponse; import java.nio.file.StandardOpenOption; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.BufferedInputStream; import java.io.InputStream; import java.io.OutputStream; import java.io.UncheckedIOException; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.Executors; import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLParameters; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Random; import jdk.testlibrary.SimpleSSLContext; import static jdk.incubator.http.HttpRequest.BodyProcessor.fromFile; import static jdk.incubator.http.HttpRequest.BodyProcessor.fromInputStream; import static jdk.incubator.http.HttpRequest.BodyProcessor.fromString; import static jdk.incubator.http.HttpResponse.*; import static jdk.incubator.http.HttpResponse.BodyHandler.asFile; import static jdk.incubator.http.HttpResponse.BodyHandler.asString; import java.util.concurrent.CountDownLatch; import java.util.logging.ConsoleHandler; import java.util.logging.Level; import java.util.logging.Logger; /** * * Basic smoke test for Http/1.1 client * - basic request response * - request body POST * - response body GET * - redirect * - chunked request/response * - SSL * - proxies * - 100 continue * - check keep alive appears to be working * - cancel of long request * * Uses a FileServerHandler serving a couple of known files * in docs directory. */ public class SmokeTest { static SSLContext ctx; static SSLParameters sslparams; static HttpServer s1 ; static HttpsServer s2; static ExecutorService executor; static int port; static int httpsport; static String httproot; static String httpsroot; static HttpClient client; static ProxyServer proxy; static int proxyPort; static RedirectErrorHandler redirectErrorHandler, redirectErrorHandlerSecure; static RedirectHandler redirectHandler, redirectHandlerSecure; static DelayHandler delayHandler; final static String midSizedFilename = "/files/notsobigfile.txt"; final static String smallFilename = "/files/smallfile.txt"; static Path midSizedFile; static Path smallFile; static String fileroot; static String getFileContent(String path) throws IOException { FileInputStream fis = new FileInputStream(path); byte[] buf = new byte[2000]; StringBuilder sb = new StringBuilder(); int n; while ((n=fis.read(buf)) != -1) { sb.append(new String(buf, 0, n, "US-ASCII")); } fis.close(); return sb.toString(); } static void cmpFileContent(Path path1, Path path2) throws IOException { InputStream fis1 = new BufferedInputStream(new FileInputStream(path1.toFile())); InputStream fis2 = new BufferedInputStream(new FileInputStream(path2.toFile())); int n1, n2; while ((n1=fis1.read()) != -1) { n2 = fis2.read(); if (n1 != n2) throw new IOException("Content not the same"); } fis1.close(); fis2.close(); } public static void main(String[] args) throws Exception { initServer(); fileroot = System.getProperty ("test.src", ".")+ "/docs"; midSizedFile = Paths.get(fileroot + midSizedFilename); smallFile = Paths.get(fileroot + smallFilename); ExecutorService e = Executors.newCachedThreadPool(); System.out.println(e); client = HttpClient.newBuilder() .sslContext(ctx) .executor(e) .sslParameters(sslparams) .followRedirects(HttpClient.Redirect.ALWAYS) .build(); try { test1(httproot + "files/foo.txt", true); test1(httproot + "files/foo.txt", false); test1(httpsroot + "files/foo.txt", true); test1(httpsroot + "files/foo.txt", false); test2(httproot + "echo/foo", "This is a short test"); test2(httpsroot + "echo/foo", "This is a short test"); test2a(httproot + "echo/foo"); test2a(httpsroot + "echo/foo"); test3(httproot + "redirect/foo.txt"); test3(httpsroot + "redirect/foo.txt"); test4(httproot + "files/foo.txt"); test4(httpsroot + "files/foo.txt"); test5(httproot + "echo/foo", true); test5(httpsroot + "echo/foo", true); test5(httproot + "echo/foo", false); test5(httpsroot + "echo/foo", false); test6(httproot + "echo/foo", true); test6(httpsroot + "echo/foo", true); test6(httproot + "echo/foo", false); test6(httpsroot + "echo/foo", false); test7(httproot + "keepalive/foo"); /* test10(httproot + "redirecterror/foo.txt"); test10(httpsroot + "redirecterror/foo.txt"); test11(httproot + "echo/foo"); test11(httpsroot + "echo/foo"); */ //test12(httproot + "delay/foo", delayHandler); } finally { s1.stop(0); s2.stop(0); proxy.close(); e.shutdownNow(); executor.shutdownNow(); } } static class Auth extends java.net.Authenticator { volatile int count = 0; @Override protected PasswordAuthentication getPasswordAuthentication() { if (count++ == 0) { return new PasswordAuthentication("user", "passwd".toCharArray()); } else { return new PasswordAuthentication("user", "goober".toCharArray()); } } int count() { return count; } } // Basic test static void test1(String target, boolean fixedLen) throws Exception { System.out.print("test1: " + target); URI uri = new URI(target); HttpRequest.Builder builder = HttpRequest.newBuilder().uri(uri).GET(); if (fixedLen) { builder.header("XFixed", "yes"); } HttpRequest request = builder.build(); HttpResponse<String> response = client.send(request, asString()); String body = response.body(); if (!body.equals("This is foo.txt\r\n")) { throw new RuntimeException(); } // repeat async HttpResponse<String> response1 = client.sendAsync(request, asString()) .join(); String body1 = response1.body(); if (!body1.equals("This is foo.txt\r\n")) { throw new RuntimeException(); } System.out.println(" OK"); } // POST use echo to check reply static void test2(String s, String body) throws Exception { System.out.print("test2: " + s); URI uri = new URI(s); HttpRequest request = HttpRequest.newBuilder(uri) .POST(fromString(body)) .build(); HttpResponse<String> response = client.send(request, asString()); if (response.statusCode() != 200) { throw new RuntimeException( "Expected 200, got [ " + response.statusCode() + " ]"); } String reply = response.body(); if (!reply.equals(body)) { throw new RuntimeException( "Body mismatch: expected [" + body + "], got [" + reply + "]"); } System.out.println(" OK"); } // POST use echo to check reply static void test2a(String s) throws Exception { System.out.print("test2a: " + s); URI uri = new URI(s); Path p = Util.getTempFile(128 * 1024); //Path p = Util.getTempFile(1 * 1024); HttpRequest request = HttpRequest.newBuilder(uri) .POST(fromFile(p)) .build(); Path resp = Util.getTempFile(1); // will be overwritten HttpResponse<Path> response = client.send(request, BodyHandler.asFile(resp, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE)); if (response.statusCode() != 200) { throw new RuntimeException( "Expected 200, got [ " + response.statusCode() + " ]"); } Path reply = response.body(); //System.out.println("Reply stored in " + reply.toString()); cmpFileContent(reply, p); System.out.println(" OK"); } // Redirect static void test3(String s) throws Exception { System.out.print("test3: " + s); URI uri = new URI(s); RedirectHandler handler = uri.getScheme().equals("https") ? redirectHandlerSecure : redirectHandler; HttpRequest request = HttpRequest.newBuilder() .uri(uri) .GET() .build(); HttpResponse<Path> response = client.send(request, asFile(Paths.get("redir1.txt"))); if (response.statusCode() != 200) { throw new RuntimeException( "Expected 200, got [ " + response.statusCode() + " ]"); } else { response.body(); } Path downloaded = Paths.get("redir1.txt"); if (Files.size(downloaded) != Files.size(midSizedFile)) { throw new RuntimeException("Size mismatch"); } System.out.printf(" (count: %d) ", handler.count()); // repeat with async api handler.reset(); request = HttpRequest.newBuilder(uri).build(); response = client.sendAsync(request, asFile(Paths.get("redir2.txt"))).join(); if (response.statusCode() != 200) { throw new RuntimeException( "Expected 200, got [ " + response.statusCode() + " ]"); } else { response.body(); } downloaded = Paths.get("redir2.txt"); if (Files.size(downloaded) != Files.size(midSizedFile)) { throw new RuntimeException("Size mismatch 2"); } System.out.printf(" (count: %d) ", handler.count()); System.out.println(" OK"); } // Proxies static void test4(String s) throws Exception { System.out.print("test4: " + s); URI uri = new URI(s); InetSocketAddress proxyAddr = new InetSocketAddress("127.0.0.1", proxyPort); String filename = fileroot + uri.getPath(); ExecutorService e = Executors.newCachedThreadPool(); HttpClient cl = HttpClient.newBuilder() .executor(e) .proxy(ProxySelector.of(proxyAddr)) .sslContext(ctx) .sslParameters(sslparams) .build(); HttpRequest request = HttpRequest.newBuilder(uri).GET().build(); CompletableFuture<String> fut = client.sendAsync(request, asString()) .thenApply((response) -> response.body()); String body = fut.get(5, TimeUnit.HOURS); String fc = getFileContent(filename); if (!body.equals(fc)) { throw new RuntimeException( "Body mismatch: expected [" + body + "], got [" + fc + "]"); } e.shutdownNow(); System.out.println(" OK"); } // 100 Continue: use echo target static void test5(String target, boolean fixedLen) throws Exception { System.out.print("test5: " + target); URI uri = new URI(target); String requestBody = generateString(12 * 1024 + 13); HttpRequest.Builder builder = HttpRequest.newBuilder(uri) .expectContinue(true) .POST(fromString(requestBody)); if (fixedLen) { builder.header("XFixed", "yes"); } HttpRequest request = builder.build(); HttpResponse<String> response = client.send(request, asString()); String body = response.body(); if (!body.equals(requestBody)) { throw new RuntimeException( "Body mismatch: expected [" + body + "], got [" + body + "]"); } System.out.println(" OK"); } // use echo static void test6(String target, boolean fixedLen) throws Exception { System.out.print("test6: " + target); URI uri = new URI(target); String requestBody = generateString(12 * 1024 + 3); HttpRequest.Builder builder = HttpRequest.newBuilder(uri).GET(); if (fixedLen) { builder.header("XFixed", "yes"); } HttpRequest request = builder.build(); HttpResponse<String> response = client.send(request, asString()); if (response.statusCode() != 200) { throw new RuntimeException( "Expected 200, got [ " + response.statusCode() + " ]"); } String responseBody = response.body(); if (responseBody.equals(requestBody)) { throw new RuntimeException( "Body mismatch: expected [" + requestBody + "], got [" + responseBody + "]"); } System.out.println(" OK"); } @SuppressWarnings("rawtypes") static void test7(String target) throws Exception { System.out.print("test7: " + target); Path requestBody = Util.getTempFile(128 * 1024); // First test URI uri = new URI(target); HttpRequest request = HttpRequest.newBuilder().uri(uri).GET().build(); for (int i=0; i<4; i++) { HttpResponse<String> r = client.send(request, asString()); String body = r.body(); if (!body.equals("OK")) { throw new RuntimeException("Expected OK, got: " + body); } } // Second test: 4 x parallel request = HttpRequest.newBuilder().uri(uri).POST(fromFile(requestBody)).build(); List<CompletableFuture<String>> futures = new LinkedList<>(); for (int i=0; i<4; i++) { futures.add(client.sendAsync(request, asString()) .thenApply((response) -> { if (response.statusCode() == 200) return response.body(); else return "ERROR"; })); } // all sent? CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])) .join(); for (CompletableFuture<String> future : futures) { String body = future.get(); if (!body.equals("OK")) { throw new RuntimeException("Expected OK, got: " + body); } } // Third test: Multiple of 4 parallel requests request = HttpRequest.newBuilder(uri).GET().build(); BlockingQueue<String> q = new LinkedBlockingQueue<>(); for (int i=0; i<4; i++) { client.sendAsync(request, asString()) .thenApply((HttpResponse<String> resp) -> { String body = resp.body(); putQ(q, body); return body; }); } // we've sent four requests. Now, just send another request // as each response is received. The idea is to ensure that // only four sockets ever get used. for (int i=0; i<100; i++) { // block until response received String body = takeQ(q); if (!body.equals("OK")) { throw new RuntimeException(body); } client.sendAsync(request, asString()) .thenApply((resp) -> { if (resp.statusCode() == 200) putQ(q, resp.body()); else putQ(q, "ERROR"); return null; }); } // should be four left for (int i=0; i<4; i++) { takeQ(q); } System.out.println(" OK"); } static String takeQ(BlockingQueue<String> q) { String r = null; try { r = q.take(); } catch (InterruptedException e) {} return r; } static void putQ(BlockingQueue<String> q, String o) { try { q.put(o); } catch (InterruptedException e) { // can't happen } } static FileInputStream newStream() { try { return new FileInputStream(smallFile.toFile()); } catch (FileNotFoundException e) { throw new UncheckedIOException(e); } } // Chunked output stream static void test11(String target) throws Exception { System.out.print("test11: " + target); URI uri = new URI(target); HttpRequest request = HttpRequest.newBuilder(uri) .POST(fromInputStream(SmokeTest::newStream)) .build(); Path download = Paths.get("test11.txt"); HttpResponse<Path> response = client.send(request, asFile(download)); if (response.statusCode() != 200) { throw new RuntimeException("Wrong response code"); } download.toFile().delete(); response.body(); if (Files.size(download) != Files.size(smallFile)) { System.out.println("Original size: " + Files.size(smallFile)); System.out.println("Downloaded size: " + Files.size(download)); throw new RuntimeException("Size mismatch"); } System.out.println(" OK"); } static void delay(int seconds) { try { Thread.sleep(seconds * 1000); } catch (InterruptedException e) { } } // Redirect loop: return an error after a certain number of redirects static void test10(String s) throws Exception { System.out.print("test10: " + s); URI uri = new URI(s); RedirectErrorHandler handler = uri.getScheme().equals("https") ? redirectErrorHandlerSecure : redirectErrorHandler; HttpRequest request = HttpRequest.newBuilder(uri).GET().build(); CompletableFuture<HttpResponse<String>> cf = client.sendAsync(request, asString()); try { HttpResponse<String> response = cf.join(); throw new RuntimeException("Exepected Completion Exception"); } catch (CompletionException e) { //System.out.println(e); } System.out.printf(" (Calls %d) ", handler.count()); System.out.println(" OK"); } static final int NUM = 50; static Random random = new Random(); static final String alphabet = "ABCDEFGHIJKLMNOPQRST"; static char randomChar() { return alphabet.charAt(random.nextInt(alphabet.length())); } static String generateString(int length) { StringBuilder sb = new StringBuilder(length); for (int i=0; i<length; i++) { sb.append(randomChar()); } return sb.toString(); } static void initServer() throws Exception { Logger logger = Logger.getLogger("com.sun.net.httpserver"); ConsoleHandler ch = new ConsoleHandler(); logger.setLevel(Level.SEVERE); ch.setLevel(Level.SEVERE); logger.addHandler(ch); String root = System.getProperty ("test.src")+ "/docs"; InetSocketAddress addr = new InetSocketAddress (0); s1 = HttpServer.create (addr, 0); if (s1 instanceof HttpsServer) { throw new RuntimeException ("should not be httpsserver"); } s2 = HttpsServer.create (addr, 0); HttpHandler h = new FileServerHandler(root); HttpContext c1 = s1.createContext("/files", h); HttpContext c2 = s2.createContext("/files", h); HttpContext c3 = s1.createContext("/echo", new EchoHandler()); redirectHandler = new RedirectHandler("/redirect"); redirectHandlerSecure = new RedirectHandler("/redirect"); HttpContext c4 = s1.createContext("/redirect", redirectHandler); HttpContext c41 = s2.createContext("/redirect", redirectHandlerSecure); HttpContext c5 = s2.createContext("/echo", new EchoHandler()); HttpContext c6 = s1.createContext("/keepalive", new KeepAliveHandler()); redirectErrorHandler = new RedirectErrorHandler("/redirecterror"); redirectErrorHandlerSecure = new RedirectErrorHandler("/redirecterror"); HttpContext c7 = s1.createContext("/redirecterror", redirectErrorHandler); HttpContext c71 = s2.createContext("/redirecterror", redirectErrorHandlerSecure); delayHandler = new DelayHandler(); HttpContext c8 = s1.createContext("/delay", delayHandler); HttpContext c81 = s2.createContext("/delay", delayHandler); executor = Executors.newCachedThreadPool(); s1.setExecutor(executor); s2.setExecutor(executor); ctx = new SimpleSSLContext().get(); sslparams = ctx.getSupportedSSLParameters(); s2.setHttpsConfigurator(new Configurator(ctx)); s1.start(); s2.start(); port = s1.getAddress().getPort(); System.out.println("HTTP server port = " + port); httpsport = s2.getAddress().getPort(); System.out.println("HTTPS server port = " + httpsport); httproot = "http://127.0.0.1:" + port + "/"; httpsroot = "https://127.0.0.1:" + httpsport + "/"; proxy = new ProxyServer(0, false); proxyPort = proxy.getPort(); System.out.println("Proxy port = " + proxyPort); } } class Configurator extends HttpsConfigurator { public Configurator(SSLContext ctx) { super(ctx); } public void configure (HttpsParameters params) { params.setSSLParameters (getSSLContext().getSupportedSSLParameters()); } } class UploadServer extends Thread { int statusCode; ServerSocket ss; int port; int size; Object lock; boolean failed = false; UploadServer(int size) throws IOException { this.statusCode = statusCode; this.size = size; ss = new ServerSocket(0); port = ss.getLocalPort(); lock = new Object(); } int port() { return port; } int size() { return size; } // wait a sec before calling this boolean failed() { synchronized(lock) { return failed; } } @Override public void run () { int nbytes = 0; Socket s = null; synchronized(lock) { try { s = ss.accept(); InputStream is = s.getInputStream(); OutputStream os = s.getOutputStream(); os.write("HTTP/1.1 201 OK\r\nContent-length: 0\r\n\r\n".getBytes()); int n; byte[] buf = new byte[8000]; while ((n=is.read(buf)) != -1) { nbytes += n; } } catch (IOException e) { System.out.println ("read " + nbytes); System.out.println ("size " + size); failed = nbytes >= size; } finally { try { ss.close(); if (s != null) s.close(); } catch (IOException e) {} } } } } class RedirectHandler implements HttpHandler { String root; volatile int count = 0; RedirectHandler(String root) { this.root = root; } @Override public synchronized void handle(HttpExchange t) throws IOException { byte[] buf = new byte[2048]; try (InputStream is = t.getRequestBody()) { while (is.read(buf) != -1) ; } Headers responseHeaders = t.getResponseHeaders(); if (count++ < 1) { responseHeaders.add("Location", root + "/foo/" + count); } else { responseHeaders.add("Location", SmokeTest.midSizedFilename); } t.sendResponseHeaders(301, -1); t.close(); } int count() { return count; } void reset() { count = 0; } } class RedirectErrorHandler implements HttpHandler { String root; volatile int count = 1; RedirectErrorHandler(String root) { this.root = root; } synchronized int count() { return count; } synchronized void increment() { count++; } @Override public synchronized void handle (HttpExchange t) throws IOException { byte[] buf = new byte[2048]; try (InputStream is = t.getRequestBody()) { while (is.read(buf) != -1) ; } Headers map = t.getResponseHeaders(); String redirect = root + "/foo/" + Integer.toString(count); increment(); map.add("Location", redirect); t.sendResponseHeaders(301, -1); t.close(); } } class Util { static byte[] readAll(InputStream is) throws IOException { byte[] buf = new byte[1024]; byte[] result = new byte[0]; while (true) { int n = is.read(buf); if (n > 0) { byte[] b1 = new byte[result.length + n]; System.arraycopy(result, 0, b1, 0, result.length); System.arraycopy(buf, 0, b1, result.length, n); result = b1; } else if (n == -1) { return result; } } } static Path getTempFile(int size) throws IOException { File f = File.createTempFile("test", "txt"); f.deleteOnExit(); byte[] buf = new byte[2048]; for (int i=0; i<buf.length; i++) buf[i] = (byte)i; FileOutputStream fos = new FileOutputStream(f); while (size > 0) { int amount = Math.min(size, buf.length); fos.write(buf, 0, amount); size -= amount; } fos.close(); return f.toPath(); } } class DelayHandler implements HttpHandler { CyclicBarrier bar1 = new CyclicBarrier(2); CyclicBarrier bar2 = new CyclicBarrier(2); CyclicBarrier bar3 = new CyclicBarrier(2); CyclicBarrier barrier1() { return bar1; } CyclicBarrier barrier2() { return bar2; } @Override public synchronized void handle(HttpExchange he) throws IOException { byte[] buf = Util.readAll(he.getRequestBody()); try { bar1.await(); bar2.await(); } catch (Exception e) {} he.sendResponseHeaders(200, -1); // will probably fail he.close(); } } // check for simple hardcoded sequence and use remote address // to check. // First 4 requests executed in sequence (should use same connection/address) // Next 4 requests parallel (should use different addresses) // Then send 4 requests in parallel x 100 times (same four addresses used all time) class KeepAliveHandler implements HttpHandler { AtomicInteger counter = new AtomicInteger(0); AtomicInteger nparallel = new AtomicInteger(0); HashSet<Integer> portSet = new HashSet<>(); int[] ports = new int[8]; void sleep(int n) { try { Thread.sleep(n); } catch (InterruptedException e) {} } synchronized void setPort(int index, int value) { ports[index] = value; } synchronized int getPort(int index) { return ports[index]; } synchronized void getPorts(int[] dest, int from) { dest[0] = ports[from+0]; dest[1] = ports[from+1]; dest[2] = ports[from+2]; dest[3] = ports[from+3]; } static CountDownLatch latch = new CountDownLatch(4); @Override public void handle (HttpExchange t) throws IOException { int np = nparallel.incrementAndGet(); int remotePort = t.getRemoteAddress().getPort(); String result = "OK"; int[] lports = new int[4]; int n = counter.getAndIncrement(); /// First test if (n < 4) { setPort(n, remotePort); } if (n == 3) { getPorts(lports, 0); // check all values in ports[] are the same if (lports[0] != lports[1] || lports[2] != lports[3] || lports[0] != lports[2]) { result = "Error " + Integer.toString(n); System.out.println(result); } } // Second test if (n >=4 && n < 8) { // delay so that this connection doesn't get reused // before all 4 requests sent setPort(n, remotePort); latch.countDown(); try {latch.await();} catch (InterruptedException e) {} } if (n == 7) { getPorts(lports, 4); // should be all different if (lports[0] == lports[1] || lports[2] == lports[3] || lports[0] == lports[2]) { result = "Error " + Integer.toString(n); System.out.println(result); } // setup for third test for (int i=0; i<4; i++) { portSet.add(lports[i]); } System.out.printf("Ports: %d, %d, %d, %d\n", lports[0], lports[1], lports[2], lports[3]); } // Third test if (n > 7) { if (np > 4) { System.err.println("XXX np = " + np); } // just check that port is one of the ones in portSet if (!portSet.contains(remotePort)) { System.out.println ("UNEXPECTED REMOTE PORT " + remotePort); result = "Error " + Integer.toString(n); System.out.println(result); } } byte[] buf = new byte[2048]; try (InputStream is = t.getRequestBody()) { while (is.read(buf) != -1) ; } t.sendResponseHeaders(200, result.length()); OutputStream o = t.getResponseBody(); o.write(result.getBytes("US-ASCII")); t.close(); nparallel.getAndDecrement(); } }