package org.webpieces.httpparser.api; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.util.List; import org.junit.Assert; import org.junit.Test; import org.webpieces.data.api.BufferCreationPool; import org.webpieces.data.api.DataWrapper; import org.webpieces.data.api.DataWrapperGenerator; import org.webpieces.data.api.DataWrapperGeneratorFactory; import org.webpieces.httpparser.api.HttpParser; import org.webpieces.httpparser.api.HttpParserFactory; import org.webpieces.httpparser.api.Memento; import org.webpieces.httpparser.api.common.Header; import org.webpieces.httpparser.api.common.KnownHeaderName; import org.webpieces.httpparser.api.dto.HttpChunk; import org.webpieces.httpparser.api.dto.HttpChunkExtension; import org.webpieces.httpparser.api.dto.HttpLastChunk; import org.webpieces.httpparser.api.dto.HttpPayload; import org.webpieces.httpparser.api.dto.HttpResponse; import org.webpieces.httpparser.api.dto.HttpResponseStatus; import org.webpieces.httpparser.api.dto.HttpResponseStatusLine; import org.webpieces.httpparser.api.dto.KnownStatusCode; public class TestChunkedParsing { private HttpParser parser = HttpParserFactory.createParser(new BufferCreationPool()); private DataWrapperGenerator dataGen = DataWrapperGeneratorFactory.createDataWrapperGenerator(); @Test public void testHex() { for(int i = 0; i < 50; i++) { String string = Integer.toHexString(i); int val = Integer.parseInt(string, 16); Assert.assertEquals(i, val); } } private byte[] unwrap(ByteBuffer buffer) { byte[] data = new byte[buffer.remaining()]; buffer.get(data); return data; } @Test public void testBasic() { String chunkedData = "4\r\nWiki\r\n5\r\npedia\r\nE\r\n in\r\n\r\nchunks.\r\n0\r\n\r\n"; HttpResponse resp = TestResponseParsing.createOkResponse(); resp.addHeader(new Header(KnownHeaderName.TRANSFER_ENCODING, "chunked")); byte[] bytes = unwrap(parser.marshalToByteBuffer(resp)); byte[] chunked = chunkedData.getBytes(); byte[] all = new byte[bytes.length+chunked.length]; System.arraycopy(bytes, 0, all, 0, bytes.length); System.arraycopy(chunked, 0, all, bytes.length, chunked.length); DataWrapper wrapper = dataGen.wrapByteArray(all); Memento memento = parser.prepareToParse(); memento = parser.parse(memento, wrapper); List<HttpPayload> msgs = memento.getParsedMessages(); Assert.assertEquals(5, msgs.size()); HttpPayload msg = msgs.get(0).getHttpResponse(); Assert.assertEquals(resp, msg); HttpChunk chunk1 = msgs.get(1).getHttpChunk(); String first = chunk1.getBody().createStringFrom(0, chunk1.getBody().getReadableSize(), Charset.defaultCharset()); Assert.assertEquals("Wiki", first); HttpChunk chunk3 = msgs.get(3).getHttpChunk(); String third = chunk3.getBody().createStringFrom(0, chunk3.getBody().getReadableSize(), Charset.defaultCharset()); Assert.assertEquals(" in\r\n\r\nchunks.", third); } @Test public void testResponseAfterChunked() { String chunkedData = "4\r\nWiki\r\n5\r\npedia\r\nE\r\n in\r\n\r\nchunks.\r\n0\r\n\r\n"; HttpResponse resp = TestResponseParsing.createOkResponse(); resp.addHeader(new Header(KnownHeaderName.TRANSFER_ENCODING, "chunked")); byte[] bytes = unwrap(parser.marshalToByteBuffer(resp)); byte[] chunked = chunkedData.getBytes(); HttpResponse resp400 = create400Response(); byte[] tail = unwrap(parser.marshalToByteBuffer(resp400)); byte[] all = new byte[bytes.length+chunked.length+tail.length]; System.arraycopy(bytes, 0, all, 0, bytes.length); System.arraycopy(chunked, 0, all, bytes.length, chunked.length); System.arraycopy(tail, 0, all, bytes.length+chunked.length, tail.length); DataWrapper wrapper = dataGen.wrapByteArray(all); Memento memento = parser.prepareToParse(); memento = parser.parse(memento, wrapper); List<HttpPayload> msgs = memento.getParsedMessages(); Assert.assertEquals(6, msgs.size()); HttpPayload msg = msgs.get(0).getHttpResponse(); Assert.assertEquals(resp, msg); HttpChunk chunk1 = msgs.get(1).getHttpChunk(); String first = chunk1.getBody().createStringFrom(0, chunk1.getBody().getReadableSize(), Charset.defaultCharset()); Assert.assertEquals("Wiki", first); HttpChunk chunk3 = msgs.get(3).getHttpChunk(); String third = chunk3.getBody().createStringFrom(0, chunk3.getBody().getReadableSize(), Charset.defaultCharset()); Assert.assertEquals(" in\r\n\r\nchunks.", third); HttpPayload tailMsg = msgs.get(5); Assert.assertEquals(resp400, tailMsg); } @Test public void testSplitChunkBodyAndResponseAfter() { String chunkedData = "1E\r\n012345678901234567890123456789\r\n7\r\nchunks.\r\n0\r\n\r\n"; HttpResponse resp = TestResponseParsing.createOkResponse(); resp.addHeader(new Header(KnownHeaderName.TRANSFER_ENCODING, "chunked")); byte[] bytes = unwrap(parser.marshalToByteBuffer(resp)); byte[] chunked = chunkedData.getBytes(); HttpResponse resp400 = create400Response(); byte[] tail = unwrap(parser.marshalToByteBuffer(resp400)); int lengthOfChunked1stHalf = 15; int lengthOfChunked2ndHalf = chunked.length - 15; byte[] firstPiece = new byte[bytes.length+lengthOfChunked1stHalf]; byte[] secondPiece = new byte[chunked.length-lengthOfChunked1stHalf+tail.length]; System.arraycopy(bytes, 0, firstPiece, 0, bytes.length); System.arraycopy(chunked, 0, firstPiece, bytes.length, lengthOfChunked1stHalf); System.arraycopy(chunked, lengthOfChunked1stHalf, secondPiece, 0, lengthOfChunked2ndHalf); System.arraycopy(tail, 0, secondPiece, lengthOfChunked2ndHalf, tail.length); DataWrapper first = dataGen.wrapByteArray(firstPiece); DataWrapper second = dataGen.wrapByteArray(secondPiece); Memento memento = parser.prepareToParse(); memento = parser.parse(memento, first); Assert.assertEquals(ParsingState.CHUNK, memento.getUnParsedState().getCurrentlyParsing()); Assert.assertEquals(11, memento.getUnParsedState().getCurrentUnparsedSize()); List<HttpPayload> msgs = memento.getParsedMessages(); Assert.assertEquals(1, msgs.size()); Assert.assertEquals(resp, msgs.get(0)); memento = parser.parse(memento, second); List<HttpPayload> parsedMessages = memento.getParsedMessages(); Assert.assertEquals(4, parsedMessages.size()); Assert.assertEquals(resp400, parsedMessages.get(3)); } @Test public void testMarshalOut() { DataWrapper payload1 = dataGen.wrapByteArray("0123456789".getBytes()); HttpChunk chunk = new HttpChunk(); chunk.addExtension(new HttpChunkExtension("asdf", "value")); chunk.addExtension(new HttpChunkExtension("something")); chunk.setBody(payload1); byte[] payload = unwrap(parser.marshalToByteBuffer(chunk)); String str = new String(payload); Assert.assertEquals("a;asdf=value;something\r\n0123456789\r\n", str); HttpLastChunk lastChunk = new HttpLastChunk(); lastChunk.addExtension(new HttpChunkExtension("this", "that")); lastChunk.addHeader(new Header("customer", "value")); String lastPayload = parser.marshalToString(lastChunk); Assert.assertEquals("0;this=that\r\ncustomer: value\r\n\r\n", lastPayload); byte[] lastBytes = unwrap(parser.marshalToByteBuffer(lastChunk)); String lastPayloadFromBytes = new String(lastBytes, HttpParserFactory.iso8859_1); Assert.assertEquals("0;this=that\r\ncustomer: value\r\n\r\n", lastPayloadFromBytes); } @Test public void testSplitChunkExtensionsAndResponseAfter() { } @Test public void testMultipleExtensions() { } //http://stackoverflow.com/questions/5590791/http-chunked-encoding-need-an-example-of-trailer-mentioned-in-spec @Test public void testLastChunkContainsTrailingHeaders() { } static HttpResponse create400Response() { Header header1 = new Header(); header1.setName(KnownHeaderName.AGE); header1.setValue("CooolValue"); HttpResponseStatus status = new HttpResponseStatus(); status.setKnownStatus(KnownStatusCode.HTTP_400_BADREQUEST); HttpResponseStatusLine statusLine = new HttpResponseStatusLine(); statusLine.setStatus(status); HttpResponse resp = new HttpResponse(); resp.setStatusLine(statusLine); resp.addHeader(header1); return resp; } }