2323import io .fusionauth .http .ContentTooLargeException ;
2424import io .fusionauth .http .HTTPValues .ContentEncodings ;
2525import io .fusionauth .http .io .ChunkedInputStream ;
26+ import io .fusionauth .http .io .FixedLengthInputStream ;
2627import io .fusionauth .http .io .PushbackInputStream ;
2728import io .fusionauth .http .log .Logger ;
2829import io .fusionauth .http .server .HTTPRequest ;
@@ -55,8 +56,6 @@ public class HTTPInputStream extends InputStream {
5556
5657 private int bytesRead ;
5758
58- private long bytesRemaining ;
59-
6059 private boolean closed ;
6160
6261 private InputStream delegate ;
@@ -75,11 +74,6 @@ public HTTPInputStream(HTTPServerConfiguration configuration, HTTPRequest reques
7574 this .chunkedBufferSize = configuration .getChunkedBufferSize ();
7675 this .maximumBytesToDrain = configuration .getMaxBytesToDrain ();
7776 this .maximumContentLength = maximumContentLength ;
78-
79- // Start the countdown
80- if (request .getContentLength () != null ) {
81- this .bytesRemaining = request .getContentLength ();
82- }
8377 }
8478
8579 @ Override
@@ -142,23 +136,6 @@ public int read(byte[] b, int off, int len) throws IOException {
142136 return 0 ;
143137 }
144138
145- // TODO : Re: Compression - fixedLength and Content-Length needs to account for the Content-Length referring to the compressed body.
146-
147- // Preamble, this could push back compressed bytes.
148- // Pushback > Throughput > Socket
149- //
150- // HTTPInputStream (this) -> Decompress > Pushback > Throughput > Socket
151- // HTTPInputStream (this) -> Decompress > Chunked > Pushback > Throughput > Socket
152- //
153- // Pushback doesn't care if the bytes are compressed or not, it is up to the caller?
154- //
155-
156- // If this is a fixed length request, and we have less than or equal to 0 bytes remaining, return -1
157- boolean fixedLength = !request .isChunked ();
158- if (fixedLength && bytesRemaining <= 0 ) {
159- return -1 ;
160- }
161-
162139 if (!initialized ) {
163140 initialize ();
164141 }
@@ -167,49 +144,19 @@ public int read(byte[] b, int off, int len) throws IOException {
167144 // - If we have read past the end of the current request, push those bytes back onto the InputStream.
168145 // - When a maximum content length has been specified, read at most one byte past the maximum.
169146 int maxReadLen = maximumContentLength == -1 ? len : Math .min (len , maximumContentLength - bytesRead + 1 );
170-
171- // TODO : Hack - This makes compression work with fixed length requests
172- if (fixedLength ) {
173- // TODO : Note : The len arg for an inflater stream is the number of un-compressed bytes.
174- // The returned number of bytes is the un-compressed bytes. So the problem here
175- // is that the bytesRemaining value we have is the compressed size of the payload so we can't
176- // us it to ensure we do not read past the current fixed length request. Sad.
177- // maxReadLen = Math.min(maxReadLen, (int) bytesRemaining);
178- }
179-
180147 int read = delegate .read (b , off , maxReadLen );
181-
182- // TODO : Can I optionally never override here? If I am fixed length, I could change len -> maxLen., and then never pushback.
183- // Would this help with compression?
184- // int maxLen = (int) Math.min(len, bytesRemaining);
185- // int maxLen = len;
186- // int read = delegate.read(b, off, maxLen);
187-
188- // TODO : This is busted with compression.
189- // bytesRemaining is calculated based upon the Content-Length.
190- // But the bytes read from the InputStream will be the bytes read that are un-compressed.
191- // So we can't use the bytes read to calculate pushback. This has to go below the Decompression in the chain.
192- int reportBytesRead = read ;
193- if (fixedLength && read > 0 ) {
194- int extraBytes = (int ) (read - bytesRemaining );
195- if (extraBytes > 0 ) {
196- reportBytesRead -= extraBytes ;
197- pushbackInputStream .push (b , (int ) bytesRemaining , extraBytes );
198- }
199-
200- bytesRemaining -= reportBytesRead ;
148+ if (read > 0 ) {
149+ bytesRead += read ;
201150 }
202151
203- bytesRead += reportBytesRead ;
204-
205152 // Note that when the request is fixed length, we will have failed early during commit().
206153 // - This will handle all requests that are not fixed length.
207154 if (maximumContentLength != -1 && bytesRead > maximumContentLength ) {
208155 String detailedMessage = "The maximum request size has been exceeded. The maximum request size is [" + maximumContentLength + "] bytes." ;
209156 throw new ContentTooLargeException (maximumContentLength , detailedMessage );
210157 }
211158
212- return reportBytesRead ;
159+ return read ;
213160 }
214161
215162 private void initialize () throws IOException {
@@ -248,29 +195,26 @@ private void initialize() throws IOException {
248195 }
249196 } else if (contentLength != null ) {
250197 logger .trace ("Client indicated it was sending an entity-body in the request. Handling body using Content-Length header {}." , contentLength );
198+ delegate = new FixedLengthInputStream (pushbackInputStream , contentLength );
251199 } else {
252200 logger .trace ("Client indicated it was NOT sending an entity-body in the request" );
253201 }
254202
255203 // If we have a maximumContentLength, and this is a fixed content length request, before we read any bytes, fail early.
256204 // For good measure do this last so if anyone downstream wants to read from the InputStream they could in theory because
257205 // we will have set up the InputStream.
206+ // TODO : Compression : we may have to delete this code, or only enforce it when compression was not used.
207+ // Content-Length represents the compressed size, not the uncompressed bytes.
258208 if (contentLength != null && maximumContentLength != -1 && contentLength > maximumContentLength ) {
259209 String detailedMessage = "The maximum request size has been exceeded. The reported Content-Length is [" + contentLength + "] and the maximum request size is [" + maximumContentLength + "] bytes." ;
260210 throw new ContentTooLargeException (maximumContentLength , detailedMessage );
261211 }
262212
263- // Those who push back:
264- // HTTPInputStream when fixed
265- // ChunkedInputStream when chunked
266- // Preamble parser
267-
268- // HTTPInputStream (this) > Pushback (delegate) > Throughput > Socket
269- // HTTPInputStream (this) > Chunked (delegate) > Pushback > Throughput > Socket
270-
271213 // The way it is currently coded
272- // HTTPInputStream (this) > Decompress > Pushback > Throughput > Socket
273- // HTTPInputStream (this) > Decompress > Chunked > Pushback > Throughput > Socket
214+ // HTTPInputStream (this) > Decompress > Fixed > Pushback > Throughput > Socket
215+
216+ // HTTPInputStream (this) > Chunked > Pushback > Throughput > Socket
217+ // > HTTPInputStream (this) > Decompress > Chunked > Pushback > Throughput > Socket
274218
275219 // TODO : Note I could leave this alone, but when we parse the header we can lower case these values and then remove the equalsIgnoreCase here?
276220 // Seems like ideally we would normalize them to lowercase earlier.
0 commit comments