Skip to content

Commit 61186e6

Browse files
committed
Working
1 parent 3454742 commit 61186e6

File tree

3 files changed

+88
-2
lines changed

3 files changed

+88
-2
lines changed

src/main/java/io/fusionauth/http/util/HTTPTools.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,9 @@ public static HeaderValue parseHeaderValue(String value) {
276276

277277
/**
278278
* Parses the request preamble directly from the given InputStream.
279+
* <p>
280+
* The HTTP request is made up of the request line, headers and an optional body. The request preamble comprises the Request line and the
281+
* Headers. All that remains after the preamble is the optional body.
279282
*
280283
* @param inputStream The input stream to read the preamble from.
281284
* @param maxRequestHeaderSize The maximum number of bytes to read for the header. If exceed throw an exception.
@@ -317,6 +320,10 @@ public static void parseRequestPreamble(PushbackInputStream inputStream, int max
317320
readObserver.run();
318321
}
319322

323+
// bytesRead will include bytes that are read past the end of the preamble. This should be ok because we will only throw an exception
324+
// for readExceeded once we have reached this state AND we are not yet in a complete state.
325+
// - So if we exceed the max header size from this read, as long as this buffer includes the entire preamble we will not throw
326+
// an exception.
320327
bytesRead += read;
321328
readExceeded = maxRequestHeaderSize != -1 && bytesRead >= maxRequestHeaderSize;
322329

src/test/java/io/fusionauth/http/io/HTTPInputStreamTest.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,6 @@ public void read_fixedLength_withPushback() throws Exception {
7474

7575
private void assertReadWithPushback(byte[] bytes, String content, int contentLength, HTTPRequest request) throws Exception {
7676
int bytesAvailable = bytes.length;
77-
System.out.println("available bytes [" + bytesAvailable + "]");
78-
System.out.println("buffer size [" + (bytesAvailable + 100) + "]");
7977
HTTPServerConfiguration configuration = new HTTPServerConfiguration().withRequestBufferSize(bytesAvailable + 100);
8078

8179
ByteArrayInputStream is = new ByteArrayInputStream(bytes);

src/test/java/io/fusionauth/http/util/HTTPToolsTest.java

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,20 @@
1515
*/
1616
package io.fusionauth.http.util;
1717

18+
import java.io.ByteArrayInputStream;
1819
import java.net.URLEncoder;
1920
import java.nio.charset.StandardCharsets;
2021
import java.util.HashMap;
2122
import java.util.List;
2223
import java.util.Map;
2324

2425
import com.inversoft.json.ToString;
26+
import io.fusionauth.http.HTTPMethod;
27+
import io.fusionauth.http.io.PushbackInputStream;
28+
import io.fusionauth.http.server.HTTPRequest;
29+
import io.fusionauth.http.server.HTTPServerConfiguration;
30+
import io.fusionauth.http.server.internal.HTTPBuffers;
31+
import io.fusionauth.http.server.io.HTTPInputStream;
2532
import io.fusionauth.http.util.HTTPTools.HeaderValue;
2633
import org.testng.annotations.Test;
2734
import static org.testng.Assert.assertEquals;
@@ -101,4 +108,78 @@ public void parseHeaderValue() {
101108
assertEquals(HTTPTools.parseHeaderValue("value; f =f"), new HeaderValue("value", Map.of("f ", "f")));
102109
assertEquals(HTTPTools.parseHeaderValue("value; f= f"), new HeaderValue("value", Map.of("f", " f")));
103110
}
111+
112+
@Test
113+
public void parsePreamble() throws Exception {
114+
// Ensure that we can correctly read the preamble when the InputStream contains the next request.
115+
116+
//noinspection ExtractMethodRecommender
117+
String request = """
118+
GET / HTTP/1.1\r
119+
Host: localhost:42\r
120+
Connection: close\r
121+
Content-Length: 113\r
122+
Header1: Value1\r
123+
Header2: Value2\r
124+
Header3: Value3\r
125+
Header4: Value4\r
126+
Header5: Value5\r
127+
Header6: Value6\r
128+
Header7: Value7\r
129+
Header8: Value8\r
130+
Header9: Value9\r
131+
Header10: Value10\r
132+
\r
133+
These pretzels are making me thirsty. These pretzels are making me thirsty. These pretzels are making me thirsty.GET / HTTP/1.1\r
134+
""";
135+
136+
// Fixed length body with the start of the next request in the buffer
137+
byte[] bytes = request.getBytes(StandardCharsets.UTF_8);
138+
int bytesAvailable = bytes.length;
139+
140+
// Ensure the request buffer size will contain the entire request.
141+
HTTPServerConfiguration configuration = new HTTPServerConfiguration().withRequestBufferSize(bytesAvailable + 100);
142+
143+
ByteArrayInputStream is = new ByteArrayInputStream(bytes);
144+
PushbackInputStream pushbackInputStream = new PushbackInputStream(is, null);
145+
146+
HTTPRequest httpRequest = new HTTPRequest();
147+
HTTPBuffers buffers = new HTTPBuffers(configuration);
148+
byte[] requestBuffer = buffers.requestBuffer();
149+
150+
HTTPTools.initialize(configuration.getLoggerFactory());
151+
HTTPTools.parseRequestPreamble(pushbackInputStream, 128 * 1024, httpRequest, requestBuffer, () -> {
152+
});
153+
154+
// Ensure we parsed the request and that the right number of bytes is left over
155+
assertEquals(httpRequest.getMethod(), HTTPMethod.GET);
156+
assertEquals(httpRequest.getHost(), "localhost");
157+
assertEquals(httpRequest.getPort(), 42);
158+
assertEquals(httpRequest.getContentLength(), 113);
159+
assertEquals(httpRequest.getHeaders().size(), 13);
160+
assertEquals(httpRequest.getHeader("Content-Length"), "113");
161+
assertEquals(httpRequest.getHeader("Connection"), "close");
162+
assertEquals(httpRequest.getHeader("Host"), "localhost:42");
163+
for (int i = 1; i <= 10; i++) {
164+
assertEquals(httpRequest.getHeader("Header" + i), "Value" + i);
165+
}
166+
167+
// Expect 129 bytes left over which is 113 for the body + 16 from the next request
168+
assertEquals(pushbackInputStream.getAvailableBufferedBytesRemaining(), 113 + 16);
169+
170+
// Read the remaining bytes for this request, we should still have some left over.
171+
HTTPInputStream httpInputStream = new HTTPInputStream(configuration, httpRequest, pushbackInputStream, -1);
172+
byte[] buffer = new byte[1024];
173+
int read = httpInputStream.read(buffer);
174+
assertEquals(read, 113);
175+
176+
// Another read should return -1 because we are at the end of this request.
177+
int nextRead = httpInputStream.read(buffer);
178+
assertEquals(nextRead, -1);
179+
180+
// The next read from the pushback which will be used by the next request should return the remaining bytes.
181+
assertEquals(pushbackInputStream.getAvailableBufferedBytesRemaining(), 16);
182+
int nextRequestRead = pushbackInputStream.read(buffer);
183+
assertEquals(nextRequestRead, 16);
184+
}
104185
}

0 commit comments

Comments
 (0)