@@ -44,6 +44,7 @@ public void chunkExtensions() throws Exception {
4444 // ;foo=;bar=baz Two extensions, one value, one equals
4545 // ;foo=bar;bar=baz Two extensions, two values
4646 // ; No extension, only a separator. Not sure if this is valid, but we should be able to ignore it.
47+ // 0;foo=bar;bar Extensions on the final 0 chunk
4748 withBody (
4849 """
4950 3;foo=bar\r
@@ -68,10 +69,11 @@ public void chunkExtensions() throws Exception {
6869 sion\r
6970 2;\r
7071 s!\r
71- 0\r
72+ 0;foo=bar;bar \r
7273 \r
7374 """ )
74- .assertResult ("Hi mom! Look no extensions!" );
75+ .assertResult ("Hi mom! Look no extensions!" )
76+ .assertLeftOverBytes (0 );
7577 }
7678
7779 @ Test
@@ -87,6 +89,7 @@ public void multipleChunks() throws Exception {
8789 \r
8890 """
8991 ).assertResult ("123456789012345678901234567890123456789012345678901234567890" )
92+ .assertLeftOverBytes (0 )
9093 .assertNextRead (ChunkedInputStream ::read , -1 );
9194 }
9295
@@ -101,7 +104,8 @@ public void ok() throws Exception {
101104 0\r
102105 \r
103106 """
104- ).assertResult ("Hi mom!" );
107+ ).assertResult ("Hi mom!" )
108+ .assertLeftOverBytes (0 );
105109 }
106110
107111 @ Test
@@ -149,6 +153,30 @@ public void partialHeader() throws IOException {
149153 assertEquals (inputStream .read (buf ), -1 );
150154 }
151155
156+ @ Test
157+ public void trailers () throws Exception {
158+ // It isn't clear if any HTTP server actually users or supports trailers. But, the spec indicates we should at least ignore them.
159+ // - https://www.rfc-editor.org/rfc/rfc2616.html#section-3.6.1
160+ withBody (
161+ """
162+ 30\r
163+ There is no fate but what we make for ourselves.\r
164+ 12\r
165+
166+ - Sarah Connor
167+ \r
168+ 0\r
169+ Judgement-Day: August 29, 1997 2:14 AM EDT\r
170+ \r
171+ """ )
172+ .assertResult ("""
173+ There is no fate but what we make for ourselves.
174+ - Sarah Connor
175+ """ )
176+ // If we correctly read to the end of the InputStream we should not have any bytes left over in the PushbackInputStream
177+ .assertLeftOverBytes (0 );
178+ }
179+
152180 private Builder withBody (String body ) {
153181 return new Builder ().withBody (body );
154182 }
@@ -157,20 +185,44 @@ private PushbackInputStream withParts(String... parts) {
157185 return new PushbackInputStream (new PieceMealInputStream (parts ));
158186 }
159187
188+ @ SuppressWarnings ("UnusedReturnValue" )
160189 private static class Builder {
161190 public String body ;
162191
163192 public ChunkedInputStream chunkedInputStream ;
164193
194+ public PushbackInputStream pushbackInputStream ;
195+
196+ /**
197+ * Used to ensure the parser worked correctly and was able to read to the end of the encoded body.
198+ *
199+ * @param expected the number of expected bytes that were over-read.
200+ * @return this.
201+ */
202+ public Builder assertLeftOverBytes (int expected ) throws IOException {
203+ int actual = pushbackInputStream .getAvailableBufferedBytesRemaining ();
204+ if (actual != expected ) {
205+ if (actual > 0 ) {
206+ byte [] leftOverBytes = new byte [actual ];
207+ int leftOverRead = pushbackInputStream .read (leftOverBytes );
208+ // No reason to think these would not be equal... but they better be.
209+ assertEquals (leftOverBytes .length , leftOverRead );
210+ assertEquals (actual , expected , "\n Here is what was left over in the buffer\n [" + new String (leftOverBytes ) + "]" );
211+ }
212+ }
213+
214+ return this ;
215+ }
216+
165217 public Builder assertNextRead (ThrowingFunction <ChunkedInputStream , Integer > function , int expected ) throws Exception {
166218 var result = function .apply (chunkedInputStream );
167219 assertEquals (result , expected );
168220 return this ;
169221 }
170222
171223 public Builder assertResult (String expected ) throws IOException {
172- var bis = new PushbackInputStream (new ByteArrayInputStream (body .getBytes (StandardCharsets .UTF_8 )));
173- chunkedInputStream = new ChunkedInputStream (bis , 2048 );
224+ pushbackInputStream = new PushbackInputStream (new ByteArrayInputStream (body .getBytes (StandardCharsets .UTF_8 )));
225+ chunkedInputStream = new ChunkedInputStream (pushbackInputStream , 2048 );
174226
175227 String actual = new String (chunkedInputStream .readAllBytes (), StandardCharsets .UTF_8 );
176228 assertEquals (actual , expected );
0 commit comments