Skip to content

Commit

Permalink
Merge pull request #58 from sleekweasel/chunking-support
Browse files Browse the repository at this point in the history
Add chunked support from PipeInputStreams
  • Loading branch information
psh committed Sep 16, 2013
2 parents 34bcf83 + 517987f commit 12b4973
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 17 deletions.
67 changes: 50 additions & 17 deletions core/src/main/java/fi/iki/elonen/NanoHTTPD.java
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,10 @@ public static class Response {
* The request method that spawned this response.
*/
private Method requestMethod;
/**
* Use chunkedTransfer
*/
private boolean chunkedTransfer;

/**
* Default constructor: response = HTTP_OK, mime = MIME_HTML and your supplied message
Expand Down Expand Up @@ -566,25 +570,12 @@ private void send(OutputStream outputStream) {
}
}

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 && chunkedTransfer) {
sendAsChunked(outputStream, pw);
} else {
sendAsFixedLength(outputStream, pw);
}
outputStream.flush();
safeClose(data);
Expand All @@ -593,6 +584,44 @@ private void send(OutputStream outputStream) {
}
}

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;
}
Expand Down Expand Up @@ -625,6 +654,10 @@ public void setRequestMethod(Method requestMethod) {
this.requestMethod = requestMethod;
}

public void setChunkedTransfer(boolean chunkedTransfer) {
this.chunkedTransfer = chunkedTransfer;
}

/**
* Some HTTP response status codes
*/
Expand Down
59 changes: 59 additions & 0 deletions core/src/test/java/fi/iki/elonen/HttpChunkedResponseTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
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);
testServer.response.setChunkedTransfer(true);

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();
}
}
}

0 comments on commit 12b4973

Please sign in to comment.