aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Hawke <paul.hawke@gmail.com>2014-01-28 20:41:26 -0800
committerPaul Hawke <paul.hawke@gmail.com>2014-01-28 20:41:26 -0800
commit852318439539b54ee6b4ce048df63b6c12cf0417 (patch)
tree522706655026003bb993dd70a0cc0a270983986c
parentd48c4379fc668ccc7540d8246f9b07db1ca87d4f (diff)
parentda2a813333f77d6134a891cd19ad5f635c0b427b (diff)
downloadnanohttpd-852318439539b54ee6b4ce048df63b6c12cf0417.tar.gz
Merge pull request #96 from steventebrinke/master
Avoid stack overflow on long-living connections
-rw-r--r--core/src/main/java/fi/iki/elonen/NanoHTTPD.java14
-rw-r--r--core/src/test/java/fi/iki/elonen/HttpKeepAliveTest.java77
2 files changed, 83 insertions, 8 deletions
diff --git a/core/src/main/java/fi/iki/elonen/NanoHTTPD.java b/core/src/main/java/fi/iki/elonen/NanoHTTPD.java
index 5301c32..35690ed 100644
--- a/core/src/main/java/fi/iki/elonen/NanoHTTPD.java
+++ b/core/src/main/java/fi/iki/elonen/NanoHTTPD.java
@@ -12,7 +12,7 @@ import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.RandomAccessFile;
-import java.io.SequenceInputStream;
+import java.io.PushbackInputStream;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
@@ -814,7 +814,7 @@ public abstract class NanoHTTPD {
public static final int BUFSIZE = 8192;
private final TempFileManager tempFileManager;
private final OutputStream outputStream;
- private InputStream inputStream;
+ private PushbackInputStream inputStream;
private int splitbyte;
private int rlen;
private String uri;
@@ -826,13 +826,13 @@ public abstract class NanoHTTPD {
public HTTPSession(TempFileManager tempFileManager, InputStream inputStream, OutputStream outputStream) {
this.tempFileManager = tempFileManager;
- this.inputStream = inputStream;
+ this.inputStream = new PushbackInputStream(inputStream, BUFSIZE);
this.outputStream = outputStream;
}
public HTTPSession(TempFileManager tempFileManager, InputStream inputStream, OutputStream outputStream, InetAddress inetAddress) {
this.tempFileManager = tempFileManager;
- this.inputStream = inputStream;
+ this.inputStream = new PushbackInputStream(inputStream, BUFSIZE);
this.outputStream = outputStream;
String remoteIp = inetAddress.isLoopbackAddress() || inetAddress.isAnyLocalAddress() ? "127.0.0.1" : inetAddress.getHostAddress().toString();
headers = new HashMap<String, String>();
@@ -876,9 +876,7 @@ public abstract class NanoHTTPD {
}
if (splitbyte < rlen) {
- ByteArrayInputStream splitInputStream = new ByteArrayInputStream(buf, splitbyte, rlen - splitbyte);
- SequenceInputStream sequenceInputStream = new SequenceInputStream(splitInputStream, inputStream);
- inputStream = sequenceInputStream;
+ inputStream.unread(buf, splitbyte, rlen - splitbyte);
}
parms = new HashMap<String, String>();
@@ -949,7 +947,7 @@ public abstract class NanoHTTPD {
// Now read all the body and write it to f
byte[] buf = new byte[512];
while (rlen >= 0 && size > 0) {
- rlen = inputStream.read(buf, 0, 512);
+ rlen = inputStream.read(buf, 0, (int)Math.min(size, 512));
size -= rlen;
if (rlen > 0) {
randomAccessFile.write(buf, 0, rlen);
diff --git a/core/src/test/java/fi/iki/elonen/HttpKeepAliveTest.java b/core/src/test/java/fi/iki/elonen/HttpKeepAliveTest.java
new file mode 100644
index 0000000..f349ee5
--- /dev/null
+++ b/core/src/test/java/fi/iki/elonen/HttpKeepAliveTest.java
@@ -0,0 +1,77 @@
+package fi.iki.elonen;
+
+import static junit.framework.Assert.fail;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+
+import org.junit.Test;
+
+public class HttpKeepAliveTest extends HttpServerTest {
+
+ @Test
+ public void testManyGetRequests() throws Exception {
+ String request = "GET " + URI + " HTTP/1.1\r\n\r\n";
+ String[] expected = {
+ "HTTP/1.1 200 OK",
+ "Content-Type: text/html",
+ "Date: .*",
+ "Connection: keep-alive",
+ "Content-Length: 0",
+ ""
+ };
+ testManyRequests(request, expected);
+ }
+
+ @Test
+ public void testManyPutRequests() throws Exception {
+ String data = "BodyData 1\nLine 2";
+ String request = "PUT " + URI + " HTTP/1.1\r\nContent-Length: " + data.length() + "\r\n\r\n" + data;
+ String[] expected = {
+ "HTTP/1.1 200 OK",
+ "Content-Type: text/html",
+ "Date: .*",
+ "Connection: keep-alive",
+ "Content-Length: 0",
+ ""
+ };
+ testManyRequests(request, expected);
+ }
+
+ private Throwable error = null;
+
+ /**
+ * Issue the given request many times to check whether an error occurs.
+ * For this test, a small stack size is used, since a stack overflow is among the possible errors.
+ * @param request The request to issue
+ * @param expected The expected response
+ */
+ public void testManyRequests(final String request, final String[] expected) throws Exception {
+ Runnable r = new Runnable() {
+ public void run() {
+ try {
+ PipedOutputStream requestStream = new PipedOutputStream();
+ PipedInputStream inputStream = new PipedInputStream(requestStream);
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ NanoHTTPD.HTTPSession session = testServer.createSession(new TestTempFileManager(), inputStream, outputStream);
+ for (int i = 0; i < 2048; i++) {
+ requestStream.write(request.getBytes());
+ requestStream.flush();
+ session.execute();
+ assertResponse(outputStream, expected);
+ }
+ } catch (Throwable t) {
+ error = t;
+ }
+ }
+ };
+ Thread t = new Thread(null, r, "Request Thread", 1 << 17);
+ t.start();
+ t.join();
+ if (error != null) {
+ fail(""+error);
+ error.printStackTrace();
+ }
+ }
+}