diff options
author | SleekWeasel <sleekweasel@gmail.com> | 2013-09-14 19:54:34 +0100 |
---|---|---|
committer | SleekWeasel <sleekweasel@gmail.com> | 2013-09-14 20:26:15 +0100 |
commit | fe1961e39809c90a76422332e91af67a5c65c31c (patch) | |
tree | 7bbb9b94bb6070be3b708a68359d88500ef4f63b | |
parent | bf93f366554a38177e7ed73a53d9da7449aeedec (diff) | |
download | nanohttpd-fe1961e39809c90a76422332e91af67a5c65c31c.tar.gz |
Add chunked support from PipeInputStreams
-rw-r--r-- | core/src/main/java/fi/iki/elonen/NanoHTTPD.java | 59 | ||||
-rw-r--r-- | core/src/test/java/fi/iki/elonen/HttpChunkedResponseTest.java | 58 |
2 files changed, 100 insertions, 17 deletions
diff --git a/core/src/main/java/fi/iki/elonen/NanoHTTPD.java b/core/src/main/java/fi/iki/elonen/NanoHTTPD.java index bfe08a5..f058365 100644 --- a/core/src/main/java/fi/iki/elonen/NanoHTTPD.java +++ b/core/src/main/java/fi/iki/elonen/NanoHTTPD.java @@ -570,25 +570,12 @@ public abstract class NanoHTTPD { } } - int pending = data != null ? data.available() : 0; // This is to support partial sends, see serveFile() pw.print("Connection: keep-alive\r\n"); - pw.print("Content-Length: "+pending+"\r\n"); - pw.print("\r\n"); - pw.flush(); - - if (requestMethod != Method.HEAD && data != null) { - int BUFFER_SIZE = 16 * 1024; - byte[] buff = new byte[BUFFER_SIZE]; - while (pending > 0) { - int read = data.read(buff, 0, ((pending > BUFFER_SIZE) ? BUFFER_SIZE : pending)); - if (read <= 0) { - break; - } - outputStream.write(buff, 0, read); - - pending -= read; - } + if (requestMethod != Method.HEAD && data instanceof PipedInputStream) { + sendAsChunked(outputStream, pw); + } else { + sendAsFixedLength(outputStream, pw); } outputStream.flush(); safeClose(data); @@ -597,6 +584,44 @@ public abstract class NanoHTTPD { } } + private void sendAsChunked(OutputStream outputStream, PrintWriter pw) throws IOException { + pw.print("Transfer-Encoding: chunked\r\n"); + pw.print("\r\n"); + pw.flush(); + int BUFFER_SIZE = 16 * 1024; + byte[] CRLF = "\r\n".getBytes(); + byte[] buff = new byte[BUFFER_SIZE]; + int read; + while ((read = data.read(buff)) > 0) { + outputStream.write(String.format("%x\r\n", read).getBytes()); + outputStream.write(buff, 0, read); + outputStream.write(CRLF); + } + outputStream.write(String.format("0\r\n\r\n").getBytes()); + } + + private void sendAsFixedLength(OutputStream outputStream, PrintWriter pw) throws IOException { + int pending = data != null ? data.available() : 0; // This is to support partial sends, see serveFile() + pw.print("Content-Length: "+pending+"\r\n"); + + pw.print("\r\n"); + pw.flush(); + + if (requestMethod != Method.HEAD && data != null) { + int BUFFER_SIZE = 16 * 1024; + byte[] buff = new byte[BUFFER_SIZE]; + while (pending > 0) { + int read = data.read(buff, 0, ((pending > BUFFER_SIZE) ? BUFFER_SIZE : pending)); + if (read <= 0) { + break; + } + outputStream.write(buff, 0, read); + + pending -= read; + } + } + } + public Status getStatus() { return status; } diff --git a/core/src/test/java/fi/iki/elonen/HttpChunkedResponseTest.java b/core/src/test/java/fi/iki/elonen/HttpChunkedResponseTest.java new file mode 100644 index 0000000..04bb7b7 --- /dev/null +++ b/core/src/test/java/fi/iki/elonen/HttpChunkedResponseTest.java @@ -0,0 +1,58 @@ +package fi.iki.elonen; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PipedInputStream; + +import static fi.iki.elonen.NanoHTTPD.Response.Status.OK; + +public class HttpChunkedResponseTest extends HttpServerTest { + @org.junit.Test + public void thatChunkedContentIsChunked() throws Exception { + PipedInputStream pipedInputStream = new ChunkedInputStream(new String[]{ + "some", + "thing which is longer than sixteen characters", + "whee!", + "" + }); + String[] expected = { + "HTTP/1.1 200 OK", + "Content-Type: what/ever", + "Date: .*", + "Connection: keep-alive", + "Transfer-Encoding: chunked", + "", + "4", + "some", + "2d", + "thing which is longer than sixteen characters", + "5", + "whee!", + "0", + "" + }; + testServer.response = new NanoHTTPD.Response(OK, "what/ever", pipedInputStream); + + ByteArrayOutputStream byteArrayOutputStream = invokeServer("GET / HTTP/1.0"); + + assertResponse(byteArrayOutputStream, expected); + } + + private static class ChunkedInputStream extends PipedInputStream { + int chunk = 0; + String[] chunks; + + private ChunkedInputStream(String[] chunks) { + this.chunks = chunks; + } + + @Override + public synchronized int read(byte[] buffer) throws IOException { + // Too implementation-linked, but... + for (int i = 0; i < chunks[chunk].length(); ++i) { + buffer[i] = (byte) chunks[chunk].charAt(i); + } + return chunks[chunk++].length(); + } + } +} |