diff options
author | Paul Hawke <paul.hawke@gmail.com> | 2014-01-28 20:41:26 -0800 |
---|---|---|
committer | Paul Hawke <paul.hawke@gmail.com> | 2014-01-28 20:41:26 -0800 |
commit | 852318439539b54ee6b4ce048df63b6c12cf0417 (patch) | |
tree | 522706655026003bb993dd70a0cc0a270983986c | |
parent | d48c4379fc668ccc7540d8246f9b07db1ca87d4f (diff) | |
parent | da2a813333f77d6134a891cd19ad5f635c0b427b (diff) | |
download | nanohttpd-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.java | 14 | ||||
-rw-r--r-- | core/src/test/java/fi/iki/elonen/HttpKeepAliveTest.java | 77 |
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(); + } + } +} |