Skip to content

Commit 62ae122

Browse files
authored
Merge pull request #46 from FusionAuth/degroff/handle_case_max_size
Normalize usage of toUpperCase and toLowerCase using Locale.ROOT.
2 parents 73100a4 + df075c7 commit 62ae122

File tree

11 files changed

+45
-32
lines changed

11 files changed

+45
-32
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.3.0`
5+
* Latest stable version: `1.3.1`
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.3.0</version>
30+
<version>1.3.1</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.3.0'
37+
implementation 'io.fusionauth:java-http:1.3.1'
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.3.0")
43+
dependency(id: "io.fusionauth:java-http:1.3.1")
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.3.0", licenses: ["ApacheV2_0"]) {
21+
project(group: "io.fusionauth", name: "java-http", version: "1.3.1", licenses: ["ApacheV2_0"]) {
2222
workflow {
2323
fetch {
2424
// Dependency resolution order:

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.util.ArrayList;
2020
import java.util.HashMap;
2121
import java.util.List;
22+
import java.util.Locale;
2223
import java.util.Map;
2324
import java.util.Objects;
2425

@@ -245,7 +246,7 @@ public void addAttribute(String name, String value) {
245246
return;
246247
}
247248

248-
switch (name.toLowerCase()) {
249+
switch (name.toLowerCase(Locale.ROOT)) {
249250
case HTTPValues.CookieAttributes.DomainLower:
250251
domain = value;
251252
break;

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import java.util.HashMap;
2828
import java.util.LinkedList;
2929
import java.util.List;
30+
import java.util.Locale;
3031
import java.util.Map;
3132
import java.util.Objects;
3233

@@ -264,7 +265,7 @@ private void readHeaders(Map<String, HeaderValue> headers) throws IOException, P
264265
var nextState = state.next(b);
265266
if (nextState != state) {
266267
switch (state) {
267-
case HeaderName -> headerName = build.toString().toLowerCase();
268+
case HeaderName -> headerName = build.toString().toLowerCase(Locale.ROOT);
268269
case HeaderValue -> headers.put(headerName, HTTPTools.parseHeaderValue(build.toString()));
269270
}
270271

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

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -164,13 +164,13 @@ public void addCookies(Collection<Cookie> cookies) {
164164
}
165165

166166
public void addHeader(String name, String value) {
167-
name = name.toLowerCase();
167+
name = name.toLowerCase(Locale.ROOT);
168168
headers.computeIfAbsent(name, key -> new ArrayList<>()).add(value);
169169
decodeHeader(name, value);
170170
}
171171

172172
public void addHeaders(String name, String... values) {
173-
name = name.toLowerCase();
173+
name = name.toLowerCase(Locale.ROOT);
174174
headers.computeIfAbsent(name, key -> new ArrayList<>()).addAll(List.of(values));
175175

176176
for (String value : values) {
@@ -179,7 +179,7 @@ public void addHeaders(String name, String... values) {
179179
}
180180

181181
public void addHeaders(String name, Collection<String> values) {
182-
name = name.toLowerCase();
182+
name = name.toLowerCase(Locale.ROOT);
183183
headers.computeIfAbsent(name, key -> new ArrayList<>()).addAll(values);
184184

185185
for (String value : values) {
@@ -254,12 +254,12 @@ public Map<String, Object> getAttributes() {
254254
public String getBaseURL() {
255255
// Setting the wrong value in the X-Forwarded-Proto header seems to be a common issue that causes an exception during URI.create.
256256
// Assuming request.getScheme() is not the problem, and it is related to the proxy configuration.
257-
String scheme = getScheme().toLowerCase();
257+
String scheme = getScheme().toLowerCase(Locale.ROOT);
258258
if (!scheme.equalsIgnoreCase("http") && !scheme.equalsIgnoreCase("https")) {
259259
throw new IllegalArgumentException("The request scheme is invalid. Only http or https are valid schemes. The X-Forwarded-Proto header has a value of [" + getHeader(Headers.XForwardedProto) + "], this is likely an issue in your proxy configuration.");
260260
}
261261

262-
String serverName = getHost().toLowerCase();
262+
String serverName = getHost().toLowerCase(Locale.ROOT);
263263
int serverPort = getBaseURLServerPort();
264264

265265
String uri = scheme + "://" + serverName;
@@ -381,7 +381,7 @@ public String getHeader(String name) {
381381
}
382382

383383
public List<String> getHeaders(String name) {
384-
return headers.get(name.toLowerCase());
384+
return headers.get(name.toLowerCase(Locale.ROOT));
385385
}
386386

387387
public Map<String, List<String>> getHeaders() {
@@ -650,11 +650,11 @@ public Object removeAttribute(String name) {
650650
}
651651

652652
public void removeHeader(String name) {
653-
headers.remove(name.toLowerCase());
653+
headers.remove(name.toLowerCase(Locale.ROOT));
654654
}
655655

656656
public void removeHeader(String name, String... values) {
657-
List<String> actual = headers.get(name.toLowerCase());
657+
List<String> actual = headers.get(name.toLowerCase(Locale.ROOT));
658658
if (actual != null) {
659659
actual.removeAll(List.of(values));
660660
}
@@ -671,13 +671,13 @@ public void setAttribute(String name, Object value) {
671671
}
672672

673673
public void setHeader(String name, String value) {
674-
name = name.toLowerCase();
674+
name = name.toLowerCase(Locale.ROOT);
675675
this.headers.put(name, new ArrayList<>(List.of(value)));
676676
decodeHeader(name, value);
677677
}
678678

679679
public void setHeaders(String name, String... values) {
680-
name = name.toLowerCase();
680+
name = name.toLowerCase(Locale.ROOT);
681681
this.headers.put(name, new ArrayList<>(List.of(values)));
682682

683683
for (String value : values) {
@@ -686,7 +686,7 @@ public void setHeaders(String name, String... values) {
686686
}
687687

688688
public void setHeaders(String name, Collection<String> values) {
689-
name = name.toLowerCase();
689+
name = name.toLowerCase(Locale.ROOT);
690690
this.headers.put(name, new ArrayList<>(values));
691691

692692
for (String value : values) {

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

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import java.util.HashMap;
2828
import java.util.LinkedHashMap;
2929
import java.util.List;
30+
import java.util.Locale;
3031
import java.util.Map;
3132
import java.util.stream.Collectors;
3233

@@ -82,7 +83,7 @@ public void addHeader(String name, String value) {
8283
return;
8384
}
8485

85-
headers.computeIfAbsent(name.toLowerCase(), key -> new ArrayList<>()).add(value);
86+
headers.computeIfAbsent(name.toLowerCase(Locale.ROOT), key -> new ArrayList<>()).add(value);
8687
}
8788

8889
public void clearHeaders() {
@@ -102,7 +103,7 @@ public void close() throws IOException {
102103
}
103104

104105
public boolean containsHeader(String name) {
105-
String key = name.toLowerCase();
106+
String key = name.toLowerCase(Locale.ROOT);
106107
return headers.containsKey(key) && !headers.get(key).isEmpty();
107108
}
108109

@@ -174,12 +175,12 @@ public void setException(Throwable exception) {
174175
}
175176

176177
public String getHeader(String name) {
177-
String key = name.toLowerCase();
178+
String key = name.toLowerCase(Locale.ROOT);
178179
return headers.containsKey(key) && !headers.get(key).isEmpty() ? headers.get(key).getFirst() : null;
179180
}
180181

181182
public List<String> getHeaders(String key) {
182-
return headers.get(key.toLowerCase());
183+
return headers.get(key.toLowerCase(Locale.ROOT));
183184
}
184185

185186
public Map<String, List<String>> getHeadersMap() {
@@ -260,7 +261,7 @@ public void removeCookie(String name) {
260261
*/
261262
public void removeHeader(String name) {
262263
if (name != null) {
263-
headers.remove(name.toLowerCase());
264+
headers.remove(name.toLowerCase(Locale.ROOT));
264265
}
265266
}
266267

@@ -304,7 +305,7 @@ public void setHeader(String name, String value) {
304305
return;
305306
}
306307

307-
headers.put(name.toLowerCase(), new ArrayList<>(List.of(value)));
308+
headers.put(name.toLowerCase(Locale.ROOT), new ArrayList<>(List.of(value)));
308309
}
309310

310311
/**

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.util.ArrayList;
2121
import java.util.HashMap;
2222
import java.util.List;
23+
import java.util.Locale;
2324
import java.util.Map;
2425
import java.util.Objects;
2526

@@ -493,7 +494,8 @@ public HTTPServerConfiguration withMaxRequestBodySize(Map<String, Integer> maxRe
493494
this.maxRequestBodySize.clear();
494495
// Add back a default to ensure we always have a fallback, can still be modified by the incoming configuration.
495496
this.maxRequestBodySize.put("*", DefaultMaxRequestSizes.get("*"));
496-
this.maxRequestBodySize.putAll(maxRequestBodySize);
497+
// Store lower case keys
498+
maxRequestBodySize.forEach((k, v) -> this.maxRequestBodySize.put(k.toLowerCase(Locale.ROOT), v));
497499
return this;
498500
}
499501

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -234,8 +234,8 @@ public void run() {
234234
logger.trace("[{}] Closing socket. Client closed the connection. Reason [{}].", Thread.currentThread().threadId(), e.getMessage());
235235
closeSocketOnly(CloseSocketReason.Expected);
236236
} catch (HTTPProcessingException e) {
237-
// Note that I am only tracing this, because this exception is mostly expected. Use closeSocketOnError so we can attempt to write a response.
238-
logger.trace("[{}] Closing socket with status [{}]. An unhandled [{}] exception was taken. Reason [{}].", Thread.currentThread().threadId(), e.getStatus(), e.getClass().getSimpleName(), e.getMessage());
237+
// These are expected, but are things the client may want to know about. Use closeSocketOnError so we can attempt to write a response.
238+
logger.debug("[{}] Closing socket with status [{}]. An unhandled [{}] exception was taken. Reason [{}].", Thread.currentThread().threadId(), e.getStatus(), e.getClass().getSimpleName(), e.getMessage());
239239
closeSocketOnError(response, e.getStatus());
240240
} catch (TooManyBytesToDrainException e) {
241241
// The request handler did not read the entire InputStream, we tried to drain it but there were more bytes remaining than the configured maximum.

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import java.util.HexFormat;
2727
import java.util.LinkedList;
2828
import java.util.List;
29+
import java.util.Locale;
2930
import java.util.Map;
3031
import java.util.Objects;
3132

@@ -58,6 +59,7 @@ public static int getMaxRequestBodySize(String contentType, Map<String, Integer>
5859
}
5960

6061
// Exact match
62+
contentType = contentType.toLowerCase(Locale.ROOT);
6163
Integer maximumSize = maxRequestBodySize.get(contentType);
6264
if (maximumSize != null) {
6365
return maximumSize;
@@ -426,10 +428,10 @@ private static void parseHeaderParameter(char[] chars, int start, int end, Map<S
426428
for (int i = start; i < end; i++) {
427429
if (name == null && chars[i] == '*') {
428430
encoded = true;
429-
name = new String(chars, start, i - start).toLowerCase();
431+
name = new String(chars, start, i - start).toLowerCase(Locale.ROOT);
430432
start = i + 2;
431433
} else if (name == null && chars[i] == '=') {
432-
name = new String(chars, start, i - start).toLowerCase();
434+
name = new String(chars, start, i - start).toLowerCase(Locale.ROOT);
433435
start = i + 1;
434436
} else if (name != null && encoded && charset == null && chars[i] == '\'') {
435437
String charsetName = new String(chars, start, i - start);

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.time.Duration;
2424
import java.util.ArrayList;
2525
import java.util.List;
26+
import java.util.Locale;
2627
import java.util.Map;
2728
import java.util.function.Consumer;
2829

@@ -76,7 +77,8 @@ public void post_server_configuration_max_form_data(String scheme, boolean chunk
7677
.withBodyParameterCount(42 * 1024)
7778
.withBodyParameterSize(128)
7879
// 4k * 33 > 128k
79-
.withConfiguration(config -> config.withMaxRequestBodySize(Map.of(ContentTypes.Form, 128 * 1024)))
80+
// - Use a UC Content-Type to make sure it still works
81+
.withConfiguration(config -> config.withMaxRequestBodySize(Map.of(ContentTypes.Form.toUpperCase(Locale.ROOT), 128 * 1024)))
8082
.expectResponse("""
8183
HTTP/1.1 413 \r
8284
connection: close\r

0 commit comments

Comments
 (0)