You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2013/06/12 12:07:18 UTC
svn commit: r1492130 -
/tomcat/trunk/test/org/apache/catalina/nonblocking/TestNonBlockingAPI.java
Author: markt
Date: Wed Jun 12 10:07:18 2013
New Revision: 1492130
URL: http://svn.apache.org/r1492130
Log:
Re-write client to provide greater insight into why an invalid chunk is invalid. This helped track down the non-blocking write issues with the APR/native connector.
Modified:
tomcat/trunk/test/org/apache/catalina/nonblocking/TestNonBlockingAPI.java
Modified: tomcat/trunk/test/org/apache/catalina/nonblocking/TestNonBlockingAPI.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/nonblocking/TestNonBlockingAPI.java?rev=1492130&r1=1492129&r2=1492130&view=diff
==============================================================================
--- tomcat/trunk/test/org/apache/catalina/nonblocking/TestNonBlockingAPI.java (original)
+++ tomcat/trunk/test/org/apache/catalina/nonblocking/TestNonBlockingAPI.java Wed Jun 12 10:07:18 2013
@@ -17,14 +17,16 @@
package org.apache.catalina.nonblocking;
import java.io.IOException;
+import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
+import java.net.Socket;
import java.net.URL;
-import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import javax.net.SocketFactory;
import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
@@ -45,13 +47,24 @@ import org.apache.catalina.startup.Bytes
import org.apache.catalina.startup.TesterServlet;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
+import org.apache.tomcat.util.buf.B2CConverter;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.buf.ByteChunk.ByteOutputChannel;
public class TestNonBlockingAPI extends TomcatBaseTest {
- public static final long bytesToDownload = 1024 * 1024 * 5;
+ private static final byte[] CHUNK = new byte[1024 * 1024];
+ private static final long WRITE_SIZE = CHUNK.length * 5;
+ static {
+ byte[] seq = new byte[] {'0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+ int i = 0;
+ while (i < CHUNK.length) {
+ System.arraycopy(seq, 0, CHUNK, i, 16);
+ i += 16;
+ }
+ }
@Test
public void testNonBlockingRead() throws Exception {
@@ -89,40 +102,104 @@ public class TestNonBlockingAPI extends
tomcat.getConnector().setProperty("socket.txBufSize", "1024");
tomcat.start();
- Map<String, List<String>> resHeaders = new HashMap<>();
- ByteChunk slowReader = new ByteChunk();
- slowReader.setLimit(1); // FIXME BUFFER IS BROKEN, 0 doesn't work
- slowReader.setByteOutputChannel(new ByteOutputChannel() {
- long counter = 0;
- long delta = 0;
+ SocketFactory factory = SocketFactory.getDefault();
+ Socket s = factory.createSocket("localhost", getPort());
- @Override
- public void realWriteBytes(byte[] cbuf, int off, int len) throws IOException {
- try {
- if (len == 0)
- return;
- counter += len;
- delta += len;
- if (counter > bytesToDownload) {
- System.out.println("ERROR Downloaded more than expected ERROR");
- } else if (counter == bytesToDownload) {
- System.out.println("Download complete(" + bytesToDownload + " bytes)");
- // } else if (counter > (1966086)) {
- // System.out.println("Download almost complete, missing bytes ("+counter+")");
- } else if (delta > (bytesToDownload / 16)) {
- System.out.println("Read " + counter + " bytes.");
- delta = 0;
- Thread.sleep(500);
- }
- } catch (Exception x) {
- throw new IOException(x);
+ ByteChunk result = new ByteChunk();
+ OutputStream os = s.getOutputStream();
+ os.write(("GET / HTTP/1.1\r\n" +
+ "Host: localhost:" + getPort() + "\r\n" +
+ "Connection: close\r\n" +
+ "\r\n").getBytes(B2CConverter.ISO_8859_1));
+ os.flush();
+
+ InputStream is = s.getInputStream();
+ byte[] buffer = new byte[8192];
+
+ int read = 0;
+ int readSinceLastPause = 0;
+ while (read != -1) {
+ read = is.read(buffer);
+ if (read > 0) {
+ result.append(buffer, 0, read);
+ }
+ readSinceLastPause += read;
+ if (readSinceLastPause > WRITE_SIZE / 16) {
+ readSinceLastPause = 0;
+ Thread.sleep(500);
+ }
+ }
+
+ os.close();
+ is.close();
+ s.close();
+
+ // Validate the result.
+ // Response line
+ String resultString = result.toString();
+ System.out.println("Read " + resultString.length() + " bytes");
+ int lineStart = 0;
+ int lineEnd = resultString.indexOf('\n', 0);
+ String line = resultString.substring(lineStart, lineEnd + 1);
+ Assert.assertEquals("HTTP/1.1 200 OK\r\n", line);
+
+ // Check headers - looking to see if response is chunked (it should be)
+ boolean chunked = false;
+ while (line.length() > 2) {
+ lineStart = lineEnd + 1;
+ lineEnd = resultString.indexOf('\n', lineStart);
+ line = resultString.substring(lineStart, lineEnd + 1);
+ if (line.startsWith("Transfer-Encoding:")) {
+ Assert.assertEquals("Transfer-Encoding: chunked\r\n", line);
+ chunked = true;
+ }
+ }
+ Assert.assertTrue(chunked);
+
+ // Now check body size
+ int totalBodyRead = 0;
+ int chunkSize = -1;
+
+ while (chunkSize != 0) {
+ // Chunk size in hex
+ lineStart = lineEnd + 1;
+ lineEnd = resultString.indexOf('\n', lineStart);
+ line = resultString.substring(lineStart, lineEnd + 1);
+ Assert.assertTrue(line.endsWith("\r\n"));
+ line = line.substring(0, line.length() - 2);
+ System.out.println("[" + line + "]");
+ chunkSize = Integer.parseInt(line, 16);
+
+ // Read the chunk
+ lineStart = lineEnd + 1;
+ lineEnd = resultString.indexOf('\n', lineStart);
+ System.out.println("Start : " + lineStart + ", End: " + lineEnd);
+ line = resultString.substring(lineStart, lineEnd + 1);
+ if (line.length() > 40) {
+ System.out.println(line.substring(0, 32));
+ } else {
+ System.out.println(line);
}
+ Assert.assertTrue(line.endsWith("\r\n"));
+ if (chunkSize + 2 != line.length()) {
+ System.out.println("Chunk wrong length. Was " + line.length() +
+ " Expected " + (chunkSize + 2));
+ // look for failed position
+ int pos = 0;
+ String seq = "0123456789ABCDEF";
+ // Assume starts with 0
+ while (line.subSequence(pos, pos + seq.length()).equals(seq)) {
+ pos += seq.length();
+ }
+ System.out.println("Failed at position " + pos + " " +
+ line.substring(pos, pos + seq.length()));
}
- });
- int rc = postUrl(true, new DataWriter(0), "http://localhost:" + getPort() + "/", slowReader, resHeaders,
- null);
- slowReader.flushBuffer();
- Assert.assertEquals(HttpServletResponse.SC_OK, rc);
+ Assert.assertEquals(chunkSize + 2, line.length());
+
+ totalBodyRead += chunkSize;
+ }
+
+ Assert.assertEquals(WRITE_SIZE, totalBodyRead);
}
@@ -162,13 +239,13 @@ public class TestNonBlockingAPI extends
return;
counter += len;
delta += len;
- if (counter > bytesToDownload) {
+ if (counter > WRITE_SIZE) {
System.out.println("ERROR Downloaded more than expected ERROR");
- } else if (counter == bytesToDownload) {
- System.out.println("Download complete(" + bytesToDownload + " bytes)");
+ } else if (counter == WRITE_SIZE) {
+ System.out.println("Download complete(" + WRITE_SIZE + " bytes)");
// } else if (counter > (1966086)) {
// System.out.println("Download almost complete, missing bytes ("+counter+")");
- } else if (delta > (bytesToDownload / 16)) {
+ } else if (delta > (WRITE_SIZE / 16)) {
System.out.println("Read " + counter + " bytes.");
delta = 0;
Thread.sleep(500);
@@ -386,9 +463,8 @@ public class TestNonBlockingAPI extends
}
private class TestWriteListener implements WriteListener {
- long chunk = 1024 * 1024;
AsyncContext ctx;
- long bytesToDownload = TestNonBlockingAPI.bytesToDownload;
+ long bytesToDownload = TestNonBlockingAPI.WRITE_SIZE;
public volatile boolean onErrorInvoked = false;
public TestWriteListener(AsyncContext ctx) {
@@ -403,10 +479,8 @@ public class TestNonBlockingAPI extends
long end = System.currentTimeMillis();
long before = left;
while (left > 0 && ctx.getResponse().getOutputStream().isReady()) {
- byte[] b = new byte[(int) Math.min(chunk, bytesToDownload)];
- Arrays.fill(b, (byte) 'X');
- ctx.getResponse().getOutputStream().write(b);
- bytesToDownload -= b.length;
+ ctx.getResponse().getOutputStream().write(CHUNK);
+ bytesToDownload -= CHUNK.length;
left = Math.max(bytesToDownload, 0);
}
System.out.println("Write took:" + (end - start) + " ms. Bytes before=" + before + " after=" + left);
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org