Skip to content

Commit 10fc594

Browse files
authored
Merge pull request #43 from FusionAuth/degroff/config_max_request_size
Add additional configuration for max post, max overall request body, max header sizes
2 parents ced766d + 7db77e9 commit 10fc594

25 files changed

+1296
-260
lines changed

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
### Latest versions
44

5-
* Latest stable version: `1.2.0`
5+
* Latest stable version: `1.3.0`
66
* Now with 100% more virtual threads!
77
* Prior stable version `0.3.7`
88

@@ -27,20 +27,20 @@ To add this library to your project, you can include this dependency in your Mav
2727
<dependency>
2828
<groupId>io.fusionauth</groupId>
2929
<artifactId>java-http</artifactId>
30-
<version>1.2.0</version>
30+
<version>1.3.0</version>
3131
</dependency>
3232
```
3333

3434
If you are using Gradle, you can add this to your build file:
3535

3636
```groovy
37-
implementation 'io.fusionauth:java-http:1.2.0'
37+
implementation 'io.fusionauth:java-http:1.3.0'
3838
```
3939

4040
If you are using Savant, you can add this to your build file:
4141

4242
```groovy
43-
dependency(id: "io.fusionauth:java-http:1.2.0")
43+
dependency(id: "io.fusionauth:java-http:1.3.0")
4444
```
4545

4646
## Examples Usages:

build.savant

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ restifyVersion = "4.2.1"
1818
slf4jVersion = "2.0.17"
1919
testngVersion = "7.11.0"
2020

21-
project(group: "io.fusionauth", name: "java-http", version: "1.2.0", licenses: ["ApacheV2_0"]) {
21+
project(group: "io.fusionauth", name: "java-http", version: "1.3.0", licenses: ["ApacheV2_0"]) {
2222
workflow {
2323
fetch {
2424
// Dependency resolution order:

java-http.ipr

Lines changed: 5 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
<option name="addBlankAfter" value="false" />
1010
</LanguageOptions>
1111
</component>
12+
<component name="GitScope">
13+
<option name="currentTabIndex" value="1" />
14+
<option name="modelData" value="[{&quot;targetBranchMap&quot;:{&quot;value&quot;:{&quot;/Users/bpontarelli/dev/inversoft/fusionauth/java-http&quot;:&quot;HEAD~5&quot;}},&quot;customTabName&quot;:&quot;HEAD~5&quot;}]" />
15+
</component>
1216
<component name="InspectionProjectProfileManager">
1317
<profile version="1.0">
1418
<option name="myName" value="Project Default" />
@@ -1409,31 +1413,6 @@
14091413
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="Java 21" project-jdk-type="JavaSDK">
14101414
<output url="file://$PROJECT_DIR$/out" />
14111415
</component>
1412-
<component name="ProjectRunConfigurationManager">
1413-
<configuration default="true" type="TestNG">
1414-
<shortenClasspath name="NONE" />
1415-
<useClassPathOnly />
1416-
<option name="SUITE_NAME" value="" />
1417-
<option name="PACKAGE_NAME" value="" />
1418-
<option name="MAIN_CLASS_NAME" value="" />
1419-
<option name="GROUP_NAME" value="" />
1420-
<option name="TEST_OBJECT" value="CLASS" />
1421-
<option name="VM_PARAMETERS" value="-ea --add-exports java.base/sun.security.x509=ALL-UNNAMED --add-exports java.base/sun.security.util=ALL-UNNAMED --add-opens java.base/java.net=ALL-UNNAMED" />
1422-
<option name="PARAMETERS" value="" />
1423-
<option name="OUTPUT_DIRECTORY" value="" />
1424-
<option name="TEST_SEARCH_SCOPE">
1425-
<value defaultName="moduleWithDependencies" />
1426-
</option>
1427-
<option name="PROPERTIES_FILE" value="" />
1428-
<properties />
1429-
<listeners>
1430-
<listener class="io.fusionauth.http.BaseTest$TestListener" />
1431-
</listeners>
1432-
<method v="2">
1433-
<option name="Make" enabled="true" />
1434-
</method>
1435-
</configuration>
1436-
</component>
14371416
<component name="VcsDirectoryMappings">
14381417
<mapping directory="$PROJECT_DIR$" vcs="Git" />
14391418
</component>
@@ -1450,4 +1429,4 @@
14501429
</SOURCES>
14511430
</library>
14521431
</component>
1453-
</project>
1432+
</project>

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<modelVersion>4.0.0</modelVersion>
33
<groupId>io.fusionauth</groupId>
44
<artifactId>java-http</artifactId>
5-
<version>1.2.0</version>
5+
<version>1.3.0</version>
66
<packaging>jar</packaging>
77

88
<name>Java HTTP library (client and server)</name>
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
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;
17+
18+
/**
19+
* Thrown when the request headers exceeds the total configured maximum size.
20+
*
21+
* @author Daniel DeGroff
22+
*/
23+
public class RequestHeadersTooLargeException extends HTTPProcessingException {
24+
public long maximumRequestHeadersSize;
25+
26+
public RequestHeadersTooLargeException(long maximumRequestHeadersSize, String detailedMessage) {
27+
super(431, "Request Header Fields Too Large", detailedMessage);
28+
this.maximumRequestHeadersSize = maximumRequestHeadersSize;
29+
}
30+
}

src/main/java/io/fusionauth/http/io/MultipartConfiguration.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ public class MultipartConfiguration {
3030

3131
private long maxFileSize = 1024 * 1024; // 1 Megabyte
3232

33-
private long maxRequestSize = 10 * 1024 * 1024; // 10 Megabyte
33+
private long maxRequestSize = 10 * 1024 * 1024; // 10 Megabytes
3434

35-
private int multipartBufferSize = 8 * 1024; // 8 Kilobyte
35+
private int multipartBufferSize = 8 * 1024; // 8 Kilobytes
3636

3737
private String temporaryFileLocation = System.getProperty("java.io.tmpdir");
3838

src/main/java/io/fusionauth/http/io/PushbackInputStream.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ public class PushbackInputStream extends InputStream {
3838

3939
private int bufferPosition;
4040

41-
4241
public PushbackInputStream(InputStream delegate, Instrumenter instrumenter) {
4342
this.delegate = delegate;
4443
this.instrumenter = instrumenter;

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

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import java.nio.file.Path;
1919
import java.time.Duration;
20+
import java.util.Map;
2021

2122
import io.fusionauth.http.io.MultipartConfiguration;
2223
import io.fusionauth.http.log.LoggerFactory;
@@ -48,7 +49,7 @@ default T withBaseDir(Path baseDir) {
4849
}
4950

5051
/**
51-
* Sets the buffer size for the chunked input stream. Defaults to 4k.
52+
* Sets the buffer size for the chunked input stream. Defaults to 4 Kilobytes.
5253
*
5354
* @param chunkedBufferSize the buffer size used to read a request body that was encoded using 'chunked' transfer-encoding.
5455
* @return This.
@@ -184,6 +185,52 @@ default T withMaxPendingSocketConnections(int maxPendingSocketConnections) {
184185
return (T) this;
185186
}
186187

188+
/**
189+
* Sets the maximum size of the HTTP request body by Content-Type. If this limit is exceeded, the connection will be closed.
190+
* <p>
191+
* The default size is identified by the "*" key. This default value will be used if a more specific value has not been configured for the
192+
* requested Content-Type.
193+
* <p>
194+
* You may also use wildcards to match one to many subtypes. For example, "application/*" will provide a max size for all content types
195+
* beginning with application/ when an exact match has not been configured.
196+
* <p>
197+
* An example lookup for the Content-Type "application/x-www-form-urlencoded" is:
198+
* <ol>
199+
* <li>application/x-www-form-urlencoded</li>
200+
* <li>application/*</li>
201+
* <li>*</li>
202+
* </ol>
203+
* <p>
204+
* If the provided configuration does not contain the initial default identified by the "*" key, the server default value will be retained.
205+
* key.
206+
* <p>
207+
* Set any value to -1 to disable this limitation.
208+
* <p>
209+
* Defaults to 128 Megabytes for the default "*" and 10 Megabytes for "application/x-www-form-urlencoded".
210+
*
211+
* @param maxRequestBodySize a map specifying the maximum size in bytes for the HTTP request body by Content-Type
212+
* @return This.
213+
*/
214+
default T withMaxRequestBodySize(Map<String, Integer> maxRequestBodySize) {
215+
configuration().withMaxRequestBodySize(maxRequestBodySize);
216+
return (T) this;
217+
}
218+
219+
/**
220+
* Sets the maximum size of the HTTP request header. The request header includes the HTTP request line, and all HTTP request headers,
221+
* essentially everything except the request body. If this maximum limit is exceeded, the connection will be closed. Defaults to 128
222+
* Kilobytes.
223+
* <p>
224+
* Set this to -1 to disable this limitation.
225+
*
226+
* @param maxRequestHeaderSize the maximum size in bytes for the HTTP request header
227+
* @return This.
228+
*/
229+
default T withMaxRequestHeaderSize(int maxRequestHeaderSize) {
230+
configuration().withMaxRequestHeaderSize(maxRequestHeaderSize);
231+
return (T) this;
232+
}
233+
187234
/**
188235
* Sets the base directory for this server. This is passed to the HTTPContext, which is available from this class. This defaults to the
189236
* current working directory of the process. Defaults to 100,000.
@@ -197,7 +244,8 @@ default T withMaxRequestsPerConnection(int maxRequestsPerConnection) {
197244
}
198245

199246
/**
200-
* This configures the maximum size of a chunk in the response when the server is using chunked response encoding. Defaults to 16k.
247+
* This configures the maximum size of a chunk in the response when the server is using chunked response encoding. Defaults to 16
248+
* Kilobytes.
201249
*
202250
* @param size The size in bytes.
203251
* @return This.
@@ -209,7 +257,7 @@ default T withMaxResponseChunkSize(int size) {
209257

210258
/**
211259
* Sets the maximum number of bytes the server will allow worker threads to drain after calling the request handler. If the request
212-
* handler does not read all the bytes, and this limit is exceeded the connection will be closed. Defaults to 128k bytes.
260+
* handler does not read all the bytes, and this limit is exceeded the connection will be closed. Defaults to 128 Kilobytes bytes.
213261
*
214262
* @param maxBytesToDrain The maximum number of bytes to drain from the InputStream if the request handler did not read all the available
215263
* bytes.
@@ -245,7 +293,7 @@ default T withMinimumWriteThroughput(long bytesPerSecond) {
245293
}
246294

247295
/**
248-
* Sets the size of the buffer that is used to process the multipart request body. This defaults to 16k.
296+
* Sets the size of the buffer that is used to process the multipart request body. This defaults to 16 Kilobytes.
249297
*
250298
* @param multipartBufferSize The size of the buffer.
251299
* @return This.
@@ -297,7 +345,7 @@ default T withReadThroughputCalculationDelayDuration(Duration duration) {
297345
}
298346

299347
/**
300-
* Sets the size of the buffer that is used to process the HTTP request. This defaults to 16k.
348+
* Sets the size of the buffer that is used to process the HTTP request. This defaults to 16 Kilobytes.
301349
*
302350
* @param requestBufferSize The size of the buffer.
303351
* @return This.
@@ -310,9 +358,11 @@ default T withRequestBufferSize(int requestBufferSize) {
310358
/**
311359
* Sets the size of the buffer that is used to store the HTTP response before any bytes are written back to the client. This is useful
312360
* when the server is generating the response but encounters an error. In this case, the server will throw out the response and change to
313-
* a 500 error response. This defaults to 64k. Negative values disable the response buffer.
361+
* a 500 error response. This defaults to 64 Kilobytes. Negative values disable the response buffer.
362+
* <p>
363+
* Set to -1 do disable buffering completely.
314364
*
315-
* @param responseBufferSize The size of the buffer. Set to -1 to disable buffering completely.
365+
* @param responseBufferSize The size of the buffer.
316366
* @return This.
317367
*/
318368
default T withResponseBufferSize(int responseBufferSize) {

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

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,7 @@ public Map<String, List<String>> getFormData() {
362362
String contentType = getContentType();
363363
if (contentType != null && contentType.equalsIgnoreCase(ContentTypes.Form)) {
364364
byte[] body = getBodyBytes();
365-
HTTPTools.parseEncodedData(body, 0, body.length, formData);
365+
HTTPTools.parseEncodedData(body, 0, body.length, getCharacterEncoding(), formData);
366366
} else if (isMultipart()) {
367367
try {
368368
multipartStreamProcessor.process(inputStream, formData, files, multipartBoundary.getBytes());
@@ -801,15 +801,12 @@ private void decodeHeader(String name, String value) {
801801
}
802802
}
803803
} else {
804+
this.host = value;
804805
if ("http".equalsIgnoreCase(scheme)) {
805806
this.port = 80;
806-
}
807-
else if ("https".equalsIgnoreCase(scheme)) {
807+
} else if ("https".equalsIgnoreCase(scheme)) {
808808
this.port = 443;
809-
} else {
810-
// fallback, intentionally do nothing
811809
}
812-
this.host = value;
813810
}
814811
break;
815812
}

0 commit comments

Comments
 (0)