1616package io .fusionauth .http .io ;
1717
1818import java .io .ByteArrayInputStream ;
19+ import java .io .ByteArrayOutputStream ;
1920import java .nio .charset .StandardCharsets ;
2021
22+ import io .fusionauth .http .BaseTest ;
23+ import io .fusionauth .http .HTTPValues .ContentEncodings ;
24+ import io .fusionauth .http .HTTPValues .Headers ;
2125import io .fusionauth .http .server .HTTPRequest ;
2226import io .fusionauth .http .server .HTTPServerConfiguration ;
2327import io .fusionauth .http .server .io .HTTPInputStream ;
28+ import org .testng .annotations .DataProvider ;
2429import org .testng .annotations .Test ;
2530import static org .testng .Assert .assertEquals ;
2631
2732/**
2833 * @author Daniel DeGroff
2934 */
30- public class HTTPInputStreamTest {
31- @ Test
32- public void read_chunked_withPushback () throws Exception {
35+ public class HTTPInputStreamTest extends BaseTest {
36+ @ DataProvider (name = "contentEncoding" )
37+ public Object [][] contentEncoding () {
38+ return new Object [][]{
39+ {"" },
40+ {"gzip" },
41+ {"deflate" },
42+ {"gzip, deflate" },
43+ {"deflate, gzip" }
44+ };
45+ }
46+
47+ @ Test (dataProvider = "contentEncoding" )
48+ public void read_chunked_withPushback (String contentEncoding ) throws Exception {
3349 // Ensure that when we read a chunked encoded body that the InputStream returns the correct number of bytes read even when
3450 // we read past the end of the current request and use the PushbackInputStream.
3551
3652 String content = "These pretzels are making me thirsty. These pretzels are making me thirsty. These pretzels are making me thirsty." ;
3753 int contentLength = content .getBytes (StandardCharsets .UTF_8 ).length ;
3854
39- // Chunk the content
40- byte [] bytes = """
41- 26\r
42- These pretzels are making me thirsty. \r
43- 26\r
44- These pretzels are making me thirsty. \r
45- 25\r
46- These pretzels are making me thirsty.\r
47- 0\r
48- \r
49- GET / HTTP/1.1\r
50- """ .getBytes ();
55+ // Chunk the content, add part of the next request
56+ String chunked = chunkItUp (content , 38 , null );
57+ byte [] payload = chunked .getBytes (StandardCharsets .UTF_8 );
58+
59+ // Optionally compress the payload
60+ if (!contentEncoding .isEmpty ()) {
61+ var requestEncodings = contentEncoding .toLowerCase ().trim ().split ("," );
62+ for (String part : requestEncodings ) {
63+ String encoding = part .trim ();
64+ if (encoding .equals (ContentEncodings .Deflate )) {
65+ payload = deflate (payload );
66+ } else if (encoding .equals (ContentEncodings .Gzip )) {
67+ payload = gzip (payload );
68+ }
69+ }
70+ }
71+
72+ ByteArrayOutputStream out = new ByteArrayOutputStream ();
73+ out .write (payload );
74+
75+ // Add part of the next request
76+ out .write ("GET / HTTP/1.1\r \n " .getBytes (StandardCharsets .UTF_8 ));
5177
5278 HTTPRequest request = new HTTPRequest ();
53- request .setHeader ("Transfer-Encoding" , "chunked" );
79+ request .setHeader (Headers .ContentEncoding , contentEncoding );
80+ request .setHeader (Headers .TransferEncoding , "chunked" );
5481
55- assertReadWithPushback (bytes , content , contentLength , request );
82+ byte [] bytes = out .toByteArray ();
83+ assertReadWithPushback (bytes , content , contentLength , 16 , request );
5684 }
5785
58- @ Test
59- public void read_fixedLength_withPushback () throws Exception {
86+ @ Test ( dataProvider = "contentEncoding" )
87+ public void read_fixedLength_withPushback (String contentEncoding ) throws Exception {
6088 // Ensure that when we read a fixed length body that the InputStream returns the correct number of bytes read even when
6189 // we read past the end of the current request and use the PushbackInputStream.
6290
63- String content = "These pretzels are making me thirsty. These pretzels are making me thirsty. These pretzels are making me thirsty." ;
64- int contentLength = content .getBytes (StandardCharsets .UTF_8 ).length ;
65-
6691 // Fixed length body with the start of the next request in the buffer
67- byte [] bytes = (content + "GET / HTTP/1.1\r \n " ).getBytes (StandardCharsets .UTF_8 );
92+ String content = "These pretzels are making me thirsty. These pretzels are making me thirsty. These pretzels are making me thirsty." ;
93+ byte [] payload = content .getBytes (StandardCharsets .UTF_8 );
94+ int contentLength = payload .length ;
95+
96+ // Optionally compress the payload
97+ if (!contentEncoding .isEmpty ()) {
98+ var requestEncodings = contentEncoding .toLowerCase ().trim ().split ("," );
99+ for (String part : requestEncodings ) {
100+ String encoding = part .trim ();
101+ if (encoding .equals (ContentEncodings .Deflate )) {
102+ payload = deflate (payload );
103+ } else if (encoding .equals (ContentEncodings .Gzip )) {
104+ payload = gzip (payload );
105+ }
106+ }
107+ }
108+
109+ // Content-Length must be the compressed length
110+ int compressedLength = payload .length ;
111+
112+ ByteArrayOutputStream out = new ByteArrayOutputStream ();
113+ out .write (payload );
114+
115+ // Add part of the next request
116+ out .write ("GET / HTTP/1.1\r \n " .getBytes (StandardCharsets .UTF_8 ));
68117
69118 HTTPRequest request = new HTTPRequest ();
70- request .setHeader ("Content-Length" , contentLength + "" );
119+ request .setHeader (Headers .ContentEncoding , contentEncoding );
120+ request .setHeader (Headers .ContentLength , compressedLength + "" );
71121
72- assertReadWithPushback (bytes , content , contentLength , request );
122+ // body length is 113, when compressed it is 68 (gzip) or 78 (deflate)
123+ // The number of bytes available is 129.
124+ byte [] bytes = out .toByteArray ();
125+ assertReadWithPushback (bytes , content , contentLength , 16 , request );
73126 }
74127
75- private void assertReadWithPushback (byte [] bytes , String content , int contentLength , HTTPRequest request ) throws Exception {
128+ private void assertReadWithPushback (byte [] bytes , String content , int contentLength , int pushedBack , HTTPRequest request )
129+ throws Exception {
76130 int bytesAvailable = bytes .length ;
77131 HTTPServerConfiguration configuration = new HTTPServerConfiguration ().withRequestBufferSize (bytesAvailable + 100 );
78132
@@ -83,6 +137,7 @@ private void assertReadWithPushback(byte[] bytes, String content, int contentLen
83137 byte [] buffer = new byte [configuration .getRequestBufferSize ()];
84138 int read = httpInputStream .read (buffer );
85139
140+ // TODO : Hmm.. with compression, this is returning the compressed bytes read instead of the un-compressed bytes read. WTF?
86141 assertEquals (read , contentLength );
87142 assertEquals (new String (buffer , 0 , read ), content );
88143
@@ -91,12 +146,12 @@ private void assertReadWithPushback(byte[] bytes, String content, int contentLen
91146 assertEquals (secondRead , -1 );
92147
93148 // We have 16 bytes left over
94- assertEquals (pushbackInputStream .getAvailableBufferedBytesRemaining (), 16 );
149+ assertEquals (pushbackInputStream .getAvailableBufferedBytesRemaining (), pushedBack );
95150
96151 // Next read should start at the next request
97152 byte [] leftOverBuffer = new byte [100 ];
98153 int leftOverRead = pushbackInputStream .read (leftOverBuffer );
99- assertEquals (leftOverRead , 16 );
154+ assertEquals (leftOverRead , pushedBack );
100155 assertEquals (new String (leftOverBuffer , 0 , leftOverRead ), "GET / HTTP/1.1\r \n " );
101156 }
102157}
0 commit comments