1 /* 2 * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* 25 * @test 26 * @summary Tests Exception detail message when too few response bytes are 27 * received before a socket exception or eof. 28 * @library /lib/testlibrary 29 * @build jdk.testlibrary.SimpleSSLContext 30 * @run testng/othervm 31 * -Djdk.httpclient.HttpClient.log=headers,errors,channel 32 * ShortResponseBody 33 */ 34 35 import java.io.IOException; 36 import java.io.InputStream; 37 import java.io.OutputStream; 38 import java.io.UncheckedIOException; 39 import java.net.InetAddress; 40 import java.net.InetSocketAddress; 41 import java.net.ServerSocket; 42 import java.net.Socket; 43 import java.net.URI; 44 import java.net.http.HttpClient; 45 import java.net.http.HttpRequest; 46 import java.net.http.HttpRequest.BodyPublishers; 47 import java.net.http.HttpResponse; 48 import java.util.ArrayList; 49 import java.util.Arrays; 50 import java.util.List; 51 import java.util.concurrent.ExecutionException; 52 import java.util.concurrent.Executor; 53 import java.util.concurrent.ExecutorService; 54 import java.util.concurrent.Executors; 55 import java.util.concurrent.ThreadFactory; 56 import java.util.concurrent.atomic.AtomicLong; 57 import java.util.stream.Stream; 58 import jdk.testlibrary.SimpleSSLContext; 59 import org.testng.annotations.AfterTest; 60 import org.testng.annotations.BeforeTest; 61 import org.testng.annotations.DataProvider; 62 import org.testng.annotations.Test; 63 import javax.net.ssl.SSLContext; 64 import javax.net.ssl.SSLServerSocketFactory; 65 import javax.net.ssl.SSLParameters; 66 import javax.net.ssl.SSLSocket; 67 import static java.lang.System.out; 68 import static java.net.http.HttpClient.Builder.NO_PROXY; 69 import static java.net.http.HttpResponse.BodyHandlers.ofString; 70 import static java.nio.charset.StandardCharsets.US_ASCII; 71 import static java.util.stream.Collectors.toList; 72 import static org.testng.Assert.assertTrue; 73 import static org.testng.Assert.assertEquals; 74 import static org.testng.Assert.fail; 75 76 public class ShortResponseBody { 77 78 Server closeImmediatelyServer; 79 Server closeImmediatelyHttpsServer; 80 Server variableLengthServer; 81 Server variableLengthHttpsServer; 82 Server fixedLengthServer; 83 84 String httpURIClsImed; 85 String httpsURIClsImed; 86 String httpURIVarLen; 87 String httpsURIVarLen; 88 String httpURIFixLen; 89 90 SSLContext sslContext; 91 SSLParameters sslParameters; 92 93 static final String EXPECTED_RESPONSE_BODY = 94 "<html><body><h1>Heading</h1><p>Some Text</p></body></html>"; 95 96 final static AtomicLong ids = new AtomicLong(); 97 final ThreadFactory factory = new ThreadFactory() { 98 @Override 99 public Thread newThread(Runnable r) { 100 Thread thread = new Thread(r, "HttpClient-Worker-" + ids.incrementAndGet()); 101 thread.setDaemon(true); 102 return thread; 103 } 104 }; 105 final ExecutorService service = Executors.newCachedThreadPool(factory); 106 107 @DataProvider(name = "sanity") sanity()108 public Object[][] sanity() { 109 return new Object[][]{ 110 { httpURIVarLen + "?length=all" }, 111 { httpsURIVarLen + "?length=all" }, 112 { httpURIFixLen + "?length=all" }, 113 }; 114 } 115 116 @Test(dataProvider = "sanity") sanity(String url)117 void sanity(String url) throws Exception { 118 HttpClient client = newHttpClient(); 119 HttpRequest request = HttpRequest.newBuilder(URI.create(url)).build(); 120 HttpResponse<String> response = client.send(request, ofString()); 121 String body = response.body(); 122 assertEquals(body, EXPECTED_RESPONSE_BODY); 123 client.sendAsync(request, ofString()) 124 .thenApply(resp -> resp.body()) 125 .thenAccept(b -> assertEquals(b, EXPECTED_RESPONSE_BODY)) 126 .join(); 127 } 128 129 @DataProvider(name = "uris") variants()130 public Object[][] variants() { 131 String[][] cases = new String[][] { 132 // The length query string is the total number of bytes in the reply, 133 // including headers, before the server closes the connection. The 134 // second arg is a partial-expected-detail message in the exception. 135 { httpURIVarLen + "?length=0", "no bytes" }, // EOF without receiving anything 136 { httpURIVarLen + "?length=1", "status line" }, // EOF during status-line 137 { httpURIVarLen + "?length=2", "status line" }, 138 { httpURIVarLen + "?length=10", "status line" }, 139 { httpURIVarLen + "?length=19", "header" }, // EOF during Content-Type header 140 { httpURIVarLen + "?length=30", "header" }, 141 { httpURIVarLen + "?length=45", "header" }, 142 { httpURIVarLen + "?length=48", "header" }, 143 { httpURIVarLen + "?length=51", "header" }, 144 { httpURIVarLen + "?length=98", "header" }, // EOF during Connection header 145 { httpURIVarLen + "?length=100", "header" }, 146 { httpURIVarLen + "?length=101", "header" }, 147 { httpURIVarLen + "?length=104", "header" }, 148 { httpURIVarLen + "?length=106", "chunked transfer encoding" }, // EOF during chunk header ( length ) 149 { httpURIVarLen + "?length=110", "chunked transfer encoding" }, // EOF during chunk response body data 150 151 { httpsURIVarLen + "?length=0", "no bytes" }, 152 { httpsURIVarLen + "?length=1", "status line" }, 153 { httpsURIVarLen + "?length=2", "status line" }, 154 { httpsURIVarLen + "?length=10", "status line" }, 155 { httpsURIVarLen + "?length=19", "header" }, 156 { httpsURIVarLen + "?length=30", "header" }, 157 { httpsURIVarLen + "?length=45", "header" }, 158 { httpsURIVarLen + "?length=48", "header" }, 159 { httpsURIVarLen + "?length=51", "header" }, 160 { httpsURIVarLen + "?length=98", "header" }, 161 { httpsURIVarLen + "?length=100", "header" }, 162 { httpsURIVarLen + "?length=101", "header" }, 163 { httpsURIVarLen + "?length=104", "header" }, 164 { httpsURIVarLen + "?length=106", "chunked transfer encoding" }, 165 { httpsURIVarLen + "?length=110", "chunked transfer encoding" }, 166 167 { httpURIFixLen + "?length=0", "no bytes" }, // EOF without receiving anything 168 { httpURIFixLen + "?length=1", "status line" }, // EOF during status-line 169 { httpURIFixLen + "?length=2", "status line" }, 170 { httpURIFixLen + "?length=10", "status line" }, 171 { httpURIFixLen + "?length=19", "header" }, // EOF during Content-Type header 172 { httpURIFixLen + "?length=30", "header" }, 173 { httpURIFixLen + "?length=45", "header" }, 174 { httpURIFixLen + "?length=48", "header" }, 175 { httpURIFixLen + "?length=51", "header" }, 176 { httpURIFixLen + "?length=78", "header" }, // EOF during Connection header 177 { httpURIFixLen + "?length=79", "header" }, 178 { httpURIFixLen + "?length=86", "header" }, 179 { httpURIFixLen + "?length=104", "fixed content-length" }, // EOF during body 180 { httpURIFixLen + "?length=106", "fixed content-length" }, 181 { httpURIFixLen + "?length=110", "fixed content-length" }, 182 183 // ## ADD https fixed 184 185 { httpURIClsImed, "no bytes"}, 186 { httpsURIClsImed, "no bytes"}, 187 }; 188 189 List<Object[]> list = new ArrayList<>(); 190 Arrays.asList(cases).stream() 191 .map(e -> new Object[] {e[0], e[1], true}) // reuse client 192 .forEach(list::add); 193 Arrays.asList(cases).stream() 194 .map(e -> new Object[] {e[0], e[1], false}) // do not reuse client 195 .forEach(list::add); 196 return list.stream().toArray(Object[][]::new); 197 } 198 199 static final int ITERATION_COUNT = 3; 200 newHttpClient()201 HttpClient newHttpClient() { 202 return HttpClient.newBuilder() 203 .proxy(NO_PROXY) 204 .sslContext(sslContext) 205 .sslParameters(sslParameters) 206 .executor(service) 207 .build(); 208 } 209 210 @Test(dataProvider = "uris") testSynchronousGET(String url, String expectedMsg, boolean sameClient)211 void testSynchronousGET(String url, String expectedMsg, boolean sameClient) 212 throws Exception 213 { 214 out.print("---\n"); 215 HttpClient client = null; 216 for (int i=0; i< ITERATION_COUNT; i++) { 217 if (!sameClient || client == null) 218 client = newHttpClient(); 219 HttpRequest request = HttpRequest.newBuilder(URI.create(url)).build(); 220 try { 221 HttpResponse<String> response = client.send(request, ofString()); 222 String body = response.body(); 223 out.println(response + ": " + body); 224 fail("UNEXPECTED RESPONSE: " + response); 225 } catch (IOException ioe) { 226 out.println("Caught expected exception:" + ioe); 227 String msg = ioe.getMessage(); 228 assertTrue(msg.contains(expectedMsg), "exception msg:[" + msg + "]"); 229 // synchronous API must have the send method on the stack 230 assertSendMethodOnStack(ioe); 231 assertNoConnectionExpiredException(ioe); 232 } 233 } 234 } 235 236 @Test(dataProvider = "uris") testAsynchronousGET(String url, String expectedMsg, boolean sameClient)237 void testAsynchronousGET(String url, String expectedMsg, boolean sameClient) 238 throws Exception 239 { 240 out.print("---\n"); 241 HttpClient client = null; 242 for (int i=0; i< ITERATION_COUNT; i++) { 243 if (!sameClient || client == null) 244 client = newHttpClient(); 245 HttpRequest request = HttpRequest.newBuilder(URI.create(url)).build(); 246 try { 247 HttpResponse<String> response = client.sendAsync(request, ofString()).get(); 248 String body = response.body(); 249 out.println(response + ": " + body); 250 fail("UNEXPECTED RESPONSE: " + response); 251 } catch (ExecutionException ee) { 252 if (ee.getCause() instanceof IOException) { 253 IOException ioe = (IOException) ee.getCause(); 254 out.println("Caught expected exception:" + ioe); 255 String msg = ioe.getMessage(); 256 assertTrue(msg.contains(expectedMsg), "exception msg:[" + msg + "]"); 257 assertNoConnectionExpiredException(ioe); 258 } else { 259 throw ee; 260 } 261 } 262 } 263 } 264 265 // can be used to prolong request body publication 266 static final class InfiniteInputStream extends InputStream { 267 int count = 0; 268 int k16 = 0; 269 @Override read()270 public int read() throws IOException { 271 if (++count == 1) { 272 System.out.println("Start sending 1 byte"); 273 } 274 if (count > 16 * 1024) { 275 k16++; 276 System.out.println("... 16K sent."); 277 count = count % (16 * 1024); 278 } 279 if (k16 > 128) { 280 System.out.println("WARNING: InfiniteInputStream: " + 281 "more than 128 16k buffers generated: returning EOF"); 282 return -1; 283 } 284 return 1; 285 } 286 287 @Override read(byte[] buf, int offset, int length)288 public int read(byte[] buf, int offset, int length) { 289 //int count = offset; 290 length = Math.max(0, Math.min(buf.length - offset, length)); 291 //for (; count < length; count++) 292 // buf[offset++] = 0x01; 293 //return count; 294 if (count == 0) { 295 System.out.println("Start sending " + length); 296 } else if (count > 16 * 1024) { 297 k16++; 298 System.out.println("... 16K sent."); 299 count = count % (16 * 1024); 300 } 301 if (k16 > 128) { 302 System.out.println("WARNING: InfiniteInputStream: " + 303 "more than 128 16k buffers generated: returning EOF"); 304 return -1; 305 } 306 count += length; 307 return length; 308 } 309 } 310 311 // POST tests are racy in what may be received before writing may cause a 312 // broken pipe or reset exception, before all the received data can be read. 313 // Any message up to, and including, the "expected" error message can occur. 314 // Strictly ordered list, in order of possible occurrence. 315 static final List<String> MSGS_ORDER = 316 List.of("no bytes", "status line", "header"); 317 318 319 @Test(dataProvider = "uris") testSynchronousPOST(String url, String expectedMsg, boolean sameClient)320 void testSynchronousPOST(String url, String expectedMsg, boolean sameClient) 321 throws Exception 322 { 323 out.print("---\n"); 324 HttpClient client = null; 325 for (int i=0; i< ITERATION_COUNT; i++) { 326 if (!sameClient || client == null) 327 client = newHttpClient(); 328 HttpRequest request = HttpRequest.newBuilder(URI.create(url)) 329 .POST(BodyPublishers.ofInputStream(() -> new InfiniteInputStream())) 330 .build(); 331 try { 332 HttpResponse<String> response = client.send(request, ofString()); 333 String body = response.body(); 334 out.println(response + ": " + body); 335 fail("UNEXPECTED RESPONSE: " + response); 336 } catch (IOException ioe) { 337 out.println("Caught expected exception:" + ioe); 338 String msg = ioe.getMessage(); 339 340 List<String> expectedMessages = new ArrayList<>(); 341 expectedMessages.add(expectedMsg); 342 MSGS_ORDER.stream().takeWhile(s -> !s.equals(expectedMsg)) 343 .forEach(expectedMessages::add); 344 345 assertTrue(expectedMessages.stream().anyMatch(s -> msg.indexOf(s) != -1), 346 "exception msg:[" + msg + "], not in [" + expectedMessages); 347 // synchronous API must have the send method on the stack 348 assertSendMethodOnStack(ioe); 349 assertNoConnectionExpiredException(ioe); 350 } 351 } 352 } 353 354 @Test(dataProvider = "uris") testAsynchronousPOST(String url, String expectedMsg, boolean sameClient)355 void testAsynchronousPOST(String url, String expectedMsg, boolean sameClient) 356 throws Exception 357 { 358 out.print("---\n"); 359 HttpClient client = null; 360 for (int i=0; i< ITERATION_COUNT; i++) { 361 if (!sameClient || client == null) 362 client = newHttpClient(); 363 HttpRequest request = HttpRequest.newBuilder(URI.create(url)) 364 .POST(BodyPublishers.ofInputStream(() -> new InfiniteInputStream())) 365 .build(); 366 try { 367 HttpResponse<String> response = client.sendAsync(request, ofString()).get(); 368 String body = response.body(); 369 out.println(response + ": " + body); 370 fail("UNEXPECTED RESPONSE: " + response); 371 } catch (ExecutionException ee) { 372 if (ee.getCause() instanceof IOException) { 373 IOException ioe = (IOException) ee.getCause(); 374 out.println("Caught expected exception:" + ioe); 375 String msg = ioe.getMessage(); 376 377 List<String> expectedMessages = new ArrayList<>(); 378 expectedMessages.add(expectedMsg); 379 MSGS_ORDER.stream().takeWhile(s -> !s.equals(expectedMsg)) 380 .forEach(expectedMessages::add); 381 382 assertTrue(expectedMessages.stream().anyMatch(s -> msg.indexOf(s) != -1), 383 "exception msg:[" + msg + "], not in [" + expectedMessages); 384 assertNoConnectionExpiredException(ioe); 385 } else { 386 throw ee; 387 } 388 } 389 } 390 } 391 392 // Asserts that the "send" method appears in the stack of the given 393 // exception. The synchronous API must contain the send method on the stack. assertSendMethodOnStack(IOException ioe)394 static void assertSendMethodOnStack(IOException ioe) { 395 final String cn = "jdk.internal.net.http.HttpClientImpl"; 396 List<StackTraceElement> list = Stream.of(ioe.getStackTrace()) 397 .filter(ste -> ste.getClassName().equals(cn) 398 && ste.getMethodName().equals("send")) 399 .collect(toList()); 400 if (list.size() != 1) { 401 ioe.printStackTrace(out); 402 fail(cn + ".send method not found in stack."); 403 } 404 } 405 406 // Asserts that the implementation-specific ConnectionExpiredException does 407 // NOT appear anywhere in the exception or its causal chain. assertNoConnectionExpiredException(IOException ioe)408 static void assertNoConnectionExpiredException(IOException ioe) { 409 Throwable throwable = ioe; 410 do { 411 String cn = throwable.getClass().getSimpleName(); 412 if (cn.equals("ConnectionExpiredException")) { 413 ioe.printStackTrace(out); 414 fail("UNEXPECTED ConnectionExpiredException in:[" + ioe + "]"); 415 } 416 } while ((throwable = throwable.getCause()) != null); 417 } 418 419 // -- infra 420 421 /** 422 * A server that, listens on a port, accepts new connections, and can be 423 * closed. 424 */ 425 static abstract class Server extends Thread implements AutoCloseable { 426 protected final ServerSocket ss; 427 protected volatile boolean closed; 428 Server(String name)429 Server(String name) throws IOException { 430 super(name); 431 ss = newServerSocket(); 432 ss.bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0)); 433 this.start(); 434 } 435 newServerSocket()436 protected ServerSocket newServerSocket() throws IOException { 437 return new ServerSocket(); 438 } 439 getPort()440 public int getPort() { return ss.getLocalPort(); } 441 442 @Override close()443 public void close() { 444 if (closed) 445 return; 446 closed = true; 447 try { 448 ss.close(); 449 } catch (IOException e) { 450 throw new UncheckedIOException("Unexpected", e); 451 } 452 } 453 } 454 455 /** 456 * A server that closes the connection immediately, without reading or writing. 457 */ 458 static class PlainCloseImmediatelyServer extends Server { PlainCloseImmediatelyServer()459 PlainCloseImmediatelyServer() throws IOException { 460 super("PlainCloseImmediatelyServer"); 461 } 462 PlainCloseImmediatelyServer(String name)463 protected PlainCloseImmediatelyServer(String name) throws IOException { 464 super(name); 465 } 466 467 @Override run()468 public void run() { 469 while (!closed) { 470 try (Socket s = ss.accept()) { 471 if (s instanceof SSLSocket) { 472 ((SSLSocket)s).startHandshake(); 473 } 474 out.println("Server: got connection, closing immediately "); 475 } catch (IOException e) { 476 if (!closed) 477 throw new UncheckedIOException("Unexpected", e); 478 } 479 } 480 } 481 } 482 483 /** 484 * A server that closes the connection immediately, without reading or writing, 485 * after completing the SSL handshake. 486 */ 487 static final class SSLCloseImmediatelyServer extends PlainCloseImmediatelyServer { SSLCloseImmediatelyServer()488 SSLCloseImmediatelyServer() throws IOException { 489 super("SSLCloseImmediatelyServer"); 490 } 491 @Override newServerSocket()492 public ServerSocket newServerSocket() throws IOException { 493 return SSLServerSocketFactory.getDefault().createServerSocket(); 494 } 495 } 496 497 /** 498 * A server that replies with headers and a, possibly partial, reply, before 499 * closing the connection. The number of bytes of written ( header + body), 500 * is controllable through the "length" query string param in the requested 501 * URI. 502 */ 503 static abstract class ReplyingServer extends Server { 504 505 private final String name; 506 ReplyingServer(String name)507 ReplyingServer(String name) throws IOException { 508 super(name); 509 this.name = name; 510 } 511 response()512 abstract String response(); 513 514 @Override run()515 public void run() { 516 while (!closed) { 517 try (Socket s = ss.accept()) { 518 out.print(name + ": got connection "); 519 InputStream is = s.getInputStream(); 520 URI requestMethod = readRequestMethod(is); 521 out.print(requestMethod + " "); 522 URI uriPath = readRequestPath(is); 523 out.println(uriPath); 524 String headers = readRequestHeaders(is); 525 526 String query = uriPath.getRawQuery(); 527 if (query == null) { 528 out.println("Request headers: [" + headers + "]"); 529 } 530 assert query != null : "null query for uriPath: " + uriPath; 531 String qv = query.split("=")[1]; 532 int len; 533 if (qv.equals("all")) { 534 len = response().getBytes(US_ASCII).length; 535 } else { 536 len = Integer.parseInt(query.split("=")[1]); 537 } 538 539 OutputStream os = s.getOutputStream(); 540 out.println(name + ": writing " + len + " bytes"); 541 byte[] responseBytes = response().getBytes(US_ASCII); 542 for (int i = 0; i< len; i++) { 543 os.write(responseBytes[i]); 544 os.flush(); 545 } 546 } catch (IOException e) { 547 if (!closed) 548 throw new UncheckedIOException("Unexpected", e); 549 } 550 } 551 } 552 553 static final byte[] requestEnd = new byte[] { '\r', '\n', '\r', '\n' }; 554 555 // Read the request method readRequestMethod(InputStream is)556 static URI readRequestMethod(InputStream is) throws IOException { 557 StringBuilder sb = new StringBuilder(); 558 int r; 559 while ((r = is.read()) != -1 && r != 0x20) { 560 sb.append((char)r); 561 } 562 return URI.create(sb.toString()); 563 } 564 565 // Read the request URI path readRequestPath(InputStream is)566 static URI readRequestPath(InputStream is) throws IOException { 567 StringBuilder sb = new StringBuilder(); 568 int r; 569 while ((r = is.read()) != -1 && r != 0x20) { 570 sb.append((char)r); 571 } 572 return URI.create(sb.toString()); 573 } 574 575 // Read until the end of a HTTP request headers readRequestHeaders(InputStream is)576 static String readRequestHeaders(InputStream is) throws IOException { 577 int requestEndCount = 0, r; 578 StringBuilder sb = new StringBuilder(); 579 while ((r = is.read()) != -1) { 580 sb.append((char) r); 581 if (r == requestEnd[requestEndCount]) { 582 requestEndCount++; 583 if (requestEndCount == 4) { 584 break; 585 } 586 } else { 587 requestEndCount = 0; 588 } 589 } 590 return sb.toString(); 591 } 592 } 593 594 /** A server that issues a, possibly-partial, chunked reply. */ 595 static class PlainVariableLengthServer extends ReplyingServer { 596 597 static final String CHUNKED_RESPONSE_BODY = 598 "6\r\n"+ "<html>\r\n" + 599 "6\r\n"+ "<body>\r\n" + 600 "10\r\n"+ "<h1>Heading</h1>\r\n" + 601 "10\r\n"+ "<p>Some Text</p>\r\n" + 602 "7\r\n"+ "</body>\r\n" + 603 "7\r\n"+ "</html>\r\n" + 604 "0\r\n"+ "\r\n"; 605 606 static final String RESPONSE_HEADERS = 607 "HTTP/1.1 200 OK\r\n" + 608 "Content-Type: text/html; charset=utf-8\r\n" + 609 "Transfer-Encoding: chunked\r\n" + 610 "Connection: close\r\n\r\n"; 611 612 static final String RESPONSE = RESPONSE_HEADERS + CHUNKED_RESPONSE_BODY; 613 PlainVariableLengthServer()614 PlainVariableLengthServer() throws IOException { 615 super("PlainVariableLengthServer"); 616 } 617 PlainVariableLengthServer(String name)618 protected PlainVariableLengthServer(String name) throws IOException { 619 super(name); 620 } 621 622 @Override response( )623 String response( ) { return RESPONSE; } 624 } 625 626 /** A server that issues a, possibly-partial, chunked reply over SSL. */ 627 static final class SSLVariableLengthServer extends PlainVariableLengthServer { SSLVariableLengthServer()628 SSLVariableLengthServer() throws IOException { 629 super("SSLVariableLengthServer"); 630 } 631 @Override newServerSocket()632 public ServerSocket newServerSocket() throws IOException { 633 return SSLServerSocketFactory.getDefault().createServerSocket(); 634 } 635 } 636 637 /** A server that issues a fixed-length reply. */ 638 static final class FixedLengthServer extends ReplyingServer { 639 640 static final String RESPONSE_BODY = EXPECTED_RESPONSE_BODY; 641 642 static final String RESPONSE_HEADERS = 643 "HTTP/1.1 200 OK\r\n" + 644 "Content-Type: text/html; charset=utf-8\r\n" + 645 "Content-Length: " + RESPONSE_BODY.length() + "\r\n" + 646 "Connection: close\r\n\r\n"; 647 648 static final String RESPONSE = RESPONSE_HEADERS + RESPONSE_BODY; 649 FixedLengthServer()650 FixedLengthServer() throws IOException { 651 super("FixedLengthServer"); 652 } 653 654 @Override response( )655 String response( ) { return RESPONSE; } 656 } 657 serverAuthority(Server server)658 static String serverAuthority(Server server) { 659 return InetAddress.getLoopbackAddress().getHostName() + ":" 660 + server.getPort(); 661 } 662 663 @BeforeTest setup()664 public void setup() throws Exception { 665 sslContext = new SimpleSSLContext().get(); 666 if (sslContext == null) 667 throw new AssertionError("Unexpected null sslContext"); 668 SSLContext.setDefault(sslContext); 669 670 sslParameters = new SSLParameters(); 671 sslParameters.setProtocols(new String[] {"TLSv1.2"}); 672 673 closeImmediatelyServer = new PlainCloseImmediatelyServer(); 674 httpURIClsImed = "http://" + serverAuthority(closeImmediatelyServer) 675 + "/http1/closeImmediately/foo"; 676 677 closeImmediatelyHttpsServer = new SSLCloseImmediatelyServer(); 678 httpsURIClsImed = "https://" + serverAuthority(closeImmediatelyHttpsServer) 679 + "/https1/closeImmediately/foo"; 680 681 variableLengthServer = new PlainVariableLengthServer(); 682 httpURIVarLen = "http://" + serverAuthority(variableLengthServer) 683 + "/http1/variable/bar"; 684 685 variableLengthHttpsServer = new SSLVariableLengthServer(); 686 httpsURIVarLen = "https://" + serverAuthority(variableLengthHttpsServer) 687 + "/https1/variable/bar"; 688 689 fixedLengthServer = new FixedLengthServer(); 690 httpURIFixLen = "http://" + serverAuthority(fixedLengthServer) 691 + "/http1/fixed/baz"; 692 } 693 694 @AfterTest teardown()695 public void teardown() throws Exception { 696 closeImmediatelyServer.close(); 697 closeImmediatelyHttpsServer.close(); 698 variableLengthServer.close(); 699 variableLengthHttpsServer.close(); 700 fixedLengthServer.close(); 701 } 702 } 703