@Test public void testPartialReadThenClose() throws Exception { server.setHandler(new PartialReaderHandler()); server.start(); try (final Socket socket = new Socket("localhost", connector.getLocalPort())) { socket.setSoTimeout(1000); byte[] content = new byte[32 * 4096]; Arrays.fill(content, (byte) 88); OutputStream out = socket.getOutputStream(); String header = "POST /?read=10 HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Length: " + content.length + "\r\n" + "Content-Type: bytes\r\n" + "\r\n"; byte[] h = header.getBytes(StandardCharsets.ISO_8859_1); out.write(h); out.write(content, 0, 4096); out.flush(); BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); assertThat(in.readLine(), containsString("HTTP/1.1 200 OK")); assertThat(in.readLine(), containsString("Content-Length:")); assertThat(in.readLine(), containsString("Server:")); in.readLine(); assertThat(in.readLine(), containsString("XXXXXXX")); socket.close(); } }
@Test public void testPipelined() throws Exception { server.setHandler(new AsyncStreamHandler()); server.start(); try (final Socket socket = new Socket("localhost", connector.getLocalPort())) { socket.setSoTimeout(1000); byte[] content = new byte[32 * 4096]; Arrays.fill(content, (byte) 120); OutputStream out = socket.getOutputStream(); String header = "POST / HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Length: " + content.length + "\r\n" + "Content-Type: bytes\r\n" + "\r\n"; byte[] h = header.getBytes(StandardCharsets.ISO_8859_1); out.write(h); out.write(content); header = "POST / HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Length: " + content.length + "\r\n" + "Content-Type: bytes\r\n" + "Connection: close\r\n" + "\r\n"; h = header.getBytes(StandardCharsets.ISO_8859_1); out.write(h); out.write(content); out.flush(); InputStream in = socket.getInputStream(); String response = IO.toString(in); assertTrue(response.indexOf("200 OK") > 0); long total = __total.poll(5, TimeUnit.SECONDS); assertEquals(content.length, total); total = __total.poll(5, TimeUnit.SECONDS); assertEquals(content.length, total); } }
public void asyncReadTest(int contentSize, int chunkSize, int chunks, int delayMS) throws Exception { String tst = contentSize + "," + chunkSize + "," + chunks + "," + delayMS; // System.err.println(tst); try (final Socket socket = new Socket("localhost", connector.getLocalPort())) { byte[] content = new byte[contentSize]; Arrays.fill(content, (byte) 120); OutputStream out = socket.getOutputStream(); out.write("POST / HTTP/1.1\r\n".getBytes()); out.write("Host: localhost\r\n".getBytes()); out.write(("Content-Length: " + content.length + "\r\n").getBytes()); out.write("Content-Type: bytes\r\n".getBytes()); out.write("Connection: close\r\n".getBytes()); out.write("\r\n".getBytes()); out.flush(); int offset = 0; for (int i = 0; i < chunks; i++) { out.write(content, offset, chunkSize); offset += chunkSize; Thread.sleep(delayMS); } out.write(content, offset, content.length - offset); out.flush(); InputStream in = socket.getInputStream(); String response = IO.toString(in); assertTrue(tst, response.indexOf("200 OK") > 0); long total = __total.poll(30, TimeUnit.SECONDS); assertEquals(tst, content.length, total); } }
@Test public void testSlowClientWithPipelinedRequest() throws Exception { final int contentLength = 512 * 1024; startServer( new AbstractHandler() { @Override public void handle( String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); if ("/content".equals(target)) { // We simulate what the DefaultServlet does, bypassing the blocking // write mechanism otherwise the test does not reproduce the bug OutputStream outputStream = response.getOutputStream(); HttpOutput output = (HttpOutput) outputStream; // Since the test is via localhost, we need a really big buffer to stall the write byte[] bytes = new byte[contentLength]; Arrays.fill(bytes, (byte) '9'); ByteBuffer buffer = ByteBuffer.wrap(bytes); // Do a non blocking write output.sendContent(buffer); } } }); Socket client = new Socket("localhost", connector.getLocalPort()); OutputStream output = client.getOutputStream(); output.write( ("" + "GET /content HTTP/1.1\r\n" + "Host: localhost:" + connector.getLocalPort() + "\r\n" + "\r\n" + "") .getBytes(StandardCharsets.UTF_8)); output.flush(); InputStream input = client.getInputStream(); int read = input.read(); Assert.assertTrue(read >= 0); // As soon as we can read the response, send a pipelined request // so it is a different read for the server and it will trigger NIO output.write( ("" + "GET /pipelined HTTP/1.1\r\n" + "Host: localhost:" + connector.getLocalPort() + "\r\n" + "\r\n" + "") .getBytes(StandardCharsets.UTF_8)); output.flush(); // Simulate a slow reader Thread.sleep(1000); Assert.assertThat(handles.get(), lessThan(10)); // We are sure we are not spinning, read the content StringBuilder lines = new StringBuilder().append((char) read); int crlfs = 0; while (true) { read = input.read(); lines.append((char) read); if (read == '\r' || read == '\n') ++crlfs; else crlfs = 0; if (crlfs == 4) break; } Assert.assertTrue(lines.toString().contains(" 200 ")); // Read the body for (int i = 0; i < contentLength; ++i) input.read(); // Read the pipelined response lines.setLength(0); crlfs = 0; while (true) { read = input.read(); lines.append((char) read); if (read == '\r' || read == '\n') ++crlfs; else crlfs = 0; if (crlfs == 4) break; } Assert.assertTrue(lines.toString().contains(" 200 ")); client.close(); }