Skip to content

Commit 3454742

Browse files
committed
Working
1 parent e2b9502 commit 3454742

File tree

5 files changed

+138
-24
lines changed

5 files changed

+138
-24
lines changed

src/main/java/io/fusionauth/http/server/internal/HTTPWorker.java

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -311,20 +311,9 @@ private void closeSocketOnError(HTTPResponse response, int status) {
311311
response.setHeader(Headers.Connection, Connections.Close);
312312
response.setStatus(status);
313313
response.setContentLength(0L);
314-
// System.out.println("return [" + status + "]");
315-
316-
// Here
317-
// Close this sucker out!
318-
// socket.setSoLinger(true, 0);
319-
320-
// socket.shutdownInput();
321-
// socket.getInputStream().close();
322314
response.close();
323315
}
324316
} catch (IOException e) {
325-
System.out.println("\n\n\nHere!");
326-
System.out.println(e.getClass().getSimpleName());
327-
System.out.println(e.getMessage());
328317
logger.debug(String.format("[%s] Could not close the HTTP response.", Thread.currentThread().threadId()), e);
329318
} finally {
330319
// It is plausible that calling response.close() could throw an exception. We must ensure we close the socket.

src/main/java/io/fusionauth/http/server/io/HTTPInputStream.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -165,15 +165,10 @@ public int read(byte[] b, int off, int len) throws IOException {
165165

166166
if (read > 0) {
167167
if (fixedLength) {
168-
bytesRemaining -= read;
168+
bytesRemaining -= reportBytesRead;
169169
}
170170
}
171171

172-
// TODO : Daniel : Review : If we push back n bytes, don't we need to return read - n? This was previously read which ignored bytes pushed back.
173-
// TODO : Daniel : Write a test to prove this, send a content-length of 100, buffer size 80 (as an example), ensure this returns 80, and then
174-
// the next call returns 20?
175-
176-
177172
bytesRead += reportBytesRead;
178173

179174
// This won't cause us to fail as fast as we could, but it keeps the code a bit simpler.

src/main/java/module-info.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@
44
exports io.fusionauth.http.log;
55
exports io.fusionauth.http.security;
66
exports io.fusionauth.http.server;
7+
exports io.fusionauth.http.server.io;
78
exports io.fusionauth.http.util;
89
}

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

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public class FormDataTest extends BaseTest {
4949
.replaceAll("\\s", "");
5050

5151
@Test(dataProvider = "schemes")
52-
public void post_server_configuration_form_data_tooBig(String scheme) throws Exception {
52+
public void post_server_configuration_max_form_data(String scheme) throws Exception {
5353
// Fixed length. n, n is < max length, Ok.
5454
// Fixed length. n, n > too big
5555
// Not tested yet: Chunked, too big.
@@ -86,13 +86,25 @@ public void post_server_configuration_form_data_tooBig(String scheme) throws Exc
8686
\r
8787
""")
8888
.expectExceptionOnWrite(SocketException.class);
89+
90+
// Large, but max size has been disabled
91+
withScheme(scheme)
92+
.withBodyParameterCount(42 * 1024) // 131,072, 131,072
93+
.withBodyParameterSize(128)
94+
// Disable the limit
95+
.withConfiguration(config -> config.withMaxFormDataSize(-1))
96+
.expectResponse("""
97+
HTTP/1.1 200 \r
98+
connection: keep-alive\r
99+
content-type: application/json\r
100+
content-length: 16\r
101+
\r
102+
{"version":"42"}""")
103+
.expectNoExceptionOnWrite();
89104
}
90105

91106
@Test(dataProvider = "schemes")
92-
public void post_server_configuration_header_tooBig(String scheme) throws Exception {
93-
// http, base header will be 263
94-
// https, base header will be 167
95-
107+
public void post_server_configuration_max_request_header_size(String scheme) throws Exception {
96108
// Header size ok.
97109
withScheme(scheme)
98110
// Try and get as close as we can to the maximum. Default is 128k, which is 131,072 bytes.
@@ -104,7 +116,6 @@ public void post_server_configuration_header_tooBig(String scheme) throws Except
104116
// https: (1655 * (11 + 2 + 64 + 2)) + 175 = 130,920
105117
.withHeaderCount(1655)
106118
.withHeaderSize(64)
107-
// TODO : Daniel : Send a small body
108119
// Default is 128k, this should fit.
109120
// - The header size includes the request line, and the request headers.
110121
.expectResponse("""
@@ -122,7 +133,6 @@ public void post_server_configuration_header_tooBig(String scheme) throws Except
122133
// 2048 * 64 == 131,072 + request line will exceed 128k
123134
.withHeaderCount(1024 * 1024)
124135
.withHeaderSize(128)
125-
// TODO : Daniel : Send a small body
126136
.expectResponse("""
127137
HTTP/1.1 431 \r
128138
connection: close\r
@@ -131,6 +141,21 @@ public void post_server_configuration_header_tooBig(String scheme) throws Except
131141
""")
132142
.expectExceptionOnWrite(SocketException.class);
133143

144+
// Same big size, but disable header size limit.
145+
withScheme(scheme)
146+
.withHeaderCount(1024 * 1024)
147+
.withHeaderSize(128)
148+
// Disable the limit
149+
.withConfiguration(config -> config.withMaxRequestHeaderSize(-1))
150+
.expectResponse("""
151+
HTTP/1.1 200 \r
152+
connection: keep-alive\r
153+
content-type: application/json\r
154+
content-length: 16\r
155+
\r
156+
{"version":"42"}""")
157+
.expectNoExceptionOnWrite();
158+
134159
}
135160

136161
private Builder withScheme(String scheme) {
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*
2+
* Copyright (c) 2025, FusionAuth, All Rights Reserved
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing,
11+
* software distributed under the License is distributed on an
12+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13+
* either express or implied. See the License for the specific
14+
* language governing permissions and limitations under the License.
15+
*/
16+
package io.fusionauth.http.io;
17+
18+
import java.io.ByteArrayInputStream;
19+
import java.nio.charset.StandardCharsets;
20+
21+
import io.fusionauth.http.server.HTTPRequest;
22+
import io.fusionauth.http.server.HTTPServerConfiguration;
23+
import io.fusionauth.http.server.io.HTTPInputStream;
24+
import org.testng.annotations.Test;
25+
import static org.testng.Assert.assertEquals;
26+
27+
/**
28+
* @author Daniel DeGroff
29+
*/
30+
public class HTTPInputStreamTest {
31+
@Test
32+
public void read_chunked_withPushback() throws Exception {
33+
// Ensure that when we read a chunked encoded body that the InputStream returns the correct number of bytes read even when
34+
// we read past the end of the current request and use the PushbackInputStream.
35+
36+
int contentLength = 113;
37+
String content = "These pretzels are making me thirsty. These pretzels are making me thirsty. These pretzels are making me thirsty.";
38+
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();
51+
52+
HTTPRequest request = new HTTPRequest();
53+
request.setHeader("Transfer-Encoding", "chunked");
54+
55+
assertReadWithPushback(bytes, content, contentLength, request);
56+
}
57+
58+
@Test
59+
public void read_fixedLength_withPushback() throws Exception {
60+
// Ensure that when we read a fixed length body that the InputStream returns the correct number of bytes read even when
61+
// we read past the end of the current request and use the PushbackInputStream.
62+
63+
int contentLength = 113;
64+
String content = "These pretzels are making me thirsty. These pretzels are making me thirsty. These pretzels are making me thirsty.";
65+
66+
// 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);
68+
69+
HTTPRequest request = new HTTPRequest();
70+
request.setHeader("Content-Length", contentLength + "");
71+
72+
assertReadWithPushback(bytes, content, contentLength, request);
73+
}
74+
75+
private void assertReadWithPushback(byte[] bytes, String content, int contentLength, HTTPRequest request) throws Exception {
76+
int bytesAvailable = bytes.length;
77+
System.out.println("available bytes [" + bytesAvailable + "]");
78+
System.out.println("buffer size [" + (bytesAvailable + 100) + "]");
79+
HTTPServerConfiguration configuration = new HTTPServerConfiguration().withRequestBufferSize(bytesAvailable + 100);
80+
81+
ByteArrayInputStream is = new ByteArrayInputStream(bytes);
82+
PushbackInputStream pushbackInputStream = new PushbackInputStream(is, null);
83+
84+
HTTPInputStream httpInputStream = new HTTPInputStream(configuration, request, pushbackInputStream, -1);
85+
byte[] buffer = new byte[configuration.getRequestBufferSize()];
86+
int read = httpInputStream.read(buffer);
87+
88+
assertEquals(read, contentLength);
89+
assertEquals(new String(buffer, 0, read), content);
90+
91+
// We should be at the end of the request, so expect -1 even though we have extra bytes in the PushbackInputStream
92+
int secondRead = httpInputStream.read(buffer);
93+
assertEquals(secondRead, -1);
94+
95+
// We have 16 bytes left over
96+
assertEquals(pushbackInputStream.getAvailableBufferedBytesRemaining(), 16);
97+
98+
// Next read should start at the next request
99+
byte[] leftOverBuffer = new byte[100];
100+
int leftOverRead = pushbackInputStream.read(leftOverBuffer);
101+
assertEquals(leftOverRead, 16);
102+
assertEquals(new String(leftOverBuffer, 0, leftOverRead), "GET / HTTP/1.1\r\n");
103+
}
104+
}

0 commit comments

Comments
 (0)