Skip to content

Commit 9ab78cc

Browse files
committed
merge in main
2 parents b4fd8c2 + 08e3895 commit 9ab78cc

File tree

11 files changed

+56
-44
lines changed

11 files changed

+56
-44
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
@@ -175,13 +175,13 @@ public void addCookies(Collection<Cookie> cookies) {
175175
}
176176

177177
public void addHeader(String name, String value) {
178-
name = name.toLowerCase();
178+
name = name.toLowerCase(Locale.ROOT);
179179
headers.computeIfAbsent(name, key -> new ArrayList<>()).add(value);
180180
decodeHeader(name, value);
181181
}
182182

183183
public void addHeaders(String name, String... values) {
184-
name = name.toLowerCase();
184+
name = name.toLowerCase(Locale.ROOT);
185185
headers.computeIfAbsent(name, key -> new ArrayList<>()).addAll(List.of(values));
186186

187187
for (String value : values) {
@@ -190,7 +190,7 @@ public void addHeaders(String name, String... values) {
190190
}
191191

192192
public void addHeaders(String name, Collection<String> values) {
193-
name = name.toLowerCase();
193+
name = name.toLowerCase(Locale.ROOT);
194194
headers.computeIfAbsent(name, key -> new ArrayList<>()).addAll(values);
195195

196196
for (String value : values) {
@@ -265,12 +265,12 @@ public Map<String, Object> getAttributes() {
265265
public String getBaseURL() {
266266
// Setting the wrong value in the X-Forwarded-Proto header seems to be a common issue that causes an exception during URI.create.
267267
// Assuming request.getScheme() is not the problem, and it is related to the proxy configuration.
268-
String scheme = getScheme().toLowerCase();
268+
String scheme = getScheme().toLowerCase(Locale.ROOT);
269269
if (!scheme.equalsIgnoreCase("http") && !scheme.equalsIgnoreCase("https")) {
270270
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.");
271271
}
272272

273-
String serverName = getHost().toLowerCase();
273+
String serverName = getHost().toLowerCase(Locale.ROOT);
274274
int serverPort = getBaseURLServerPort();
275275

276276
String uri = scheme + "://" + serverName;
@@ -401,7 +401,7 @@ public String getHeader(String name) {
401401
}
402402

403403
public List<String> getHeaders(String name) {
404-
return headers.get(name.toLowerCase());
404+
return headers.get(name.toLowerCase(Locale.ROOT));
405405
}
406406

407407
public Map<String, List<String>> getHeaders() {
@@ -670,11 +670,11 @@ public Object removeAttribute(String name) {
670670
}
671671

672672
public void removeHeader(String name) {
673-
headers.remove(name.toLowerCase());
673+
headers.remove(name.toLowerCase(Locale.ROOT));
674674
}
675675

676676
public void removeHeader(String name, String... values) {
677-
List<String> actual = headers.get(name.toLowerCase());
677+
List<String> actual = headers.get(name.toLowerCase(Locale.ROOT));
678678
if (actual != null) {
679679
actual.removeAll(List.of(values));
680680
}
@@ -691,13 +691,13 @@ public void setAttribute(String name, Object value) {
691691
}
692692

693693
public void setHeader(String name, String value) {
694-
name = name.toLowerCase();
694+
name = name.toLowerCase(Locale.ROOT);
695695
this.headers.put(name, new ArrayList<>(List.of(value)));
696696
decodeHeader(name, value);
697697
}
698698

699699
public void setHeaders(String name, String... values) {
700-
name = name.toLowerCase();
700+
name = name.toLowerCase(Locale.ROOT);
701701
this.headers.put(name, new ArrayList<>(List.of(values)));
702702

703703
for (String value : values) {
@@ -706,7 +706,7 @@ public void setHeaders(String name, String... values) {
706706
}
707707

708708
public void setHeaders(String name, Collection<String> values) {
709-
name = name.toLowerCase();
709+
name = name.toLowerCase(Locale.ROOT);
710710
this.headers.put(name, new ArrayList<>(values));
711711

712712
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
@@ -235,8 +235,8 @@ public void run() {
235235
logger.trace("[{}] Closing socket. Client closed the connection. Reason [{}].", Thread.currentThread().threadId(), e.getMessage());
236236
closeSocketOnly(CloseSocketReason.Expected);
237237
} catch (HTTPProcessingException e) {
238-
// Note that I am only tracing this, because this exception is mostly expected. Use closeSocketOnError so we can attempt to write a response.
239-
logger.trace("[{}] Closing socket with status [{}]. An unhandled [{}] exception was taken. Reason [{}].", Thread.currentThread().threadId(), e.getStatus(), e.getClass().getSimpleName(), e.getMessage());
238+
// These are expected, but are things the client may want to know about. Use closeSocketOnError so we can attempt to write a response.
239+
logger.debug("[{}] Closing socket with status [{}]. An unhandled [{}] exception was taken. Reason [{}].", Thread.currentThread().threadId(), e.getStatus(), e.getClass().getSimpleName(), e.getMessage());
240240
closeSocketOnError(response, e.getStatus());
241241
} catch (TooManyBytesToDrainException e) {
242242
// 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: 14 additions & 13 deletions
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
@@ -197,18 +199,6 @@ public Builder(String scheme) {
197199
this.scheme = scheme;
198200
}
199201

200-
public Builder assertOptionalExceptionOnWrite(Class<? extends Exception> clazz) {
201-
// Note that this assertion really depends upon the system the test is run on, the size of the request, and the amount of data that can be cached.
202-
// - So this is an optional assertion - if exception is not null, then we should be able to assert some attributes.
203-
// - With the larger sizes this exception is mostly always thrown when running tests locally, but in GHA, it doesn't always occur.
204-
if (thrownOnWrite != null) {
205-
assertEquals(thrownOnWrite.getClass(), clazz);
206-
assertEquals(thrownOnWrite.getMessage(), "Broken pipe");
207-
}
208-
209-
return this;
210-
}
211-
212202
public Builder expectNoExceptionOnWrite() {
213203
assertNull(thrownOnWrite);
214204
return this;
@@ -320,6 +310,17 @@ public Builder expectResponse(String response) throws Exception {
320310
return this;
321311
}
322312

313+
public Builder assertOptionalExceptionOnWrite(Class<? extends Exception> clazz) {
314+
// Note that this assertion really depends upon the system the test is run on, the size of the request, and the amount of data that can be cached.
315+
// - So this is an optional assertion - if exception is not null, then we should be able to assert some attributes.
316+
// - With the larger sizes this exception is mostly always thrown when running tests locally, but in GHA, it doesn't always occur.
317+
if (thrownOnWrite != null) {
318+
assertEquals(thrownOnWrite.getClass(), clazz);
319+
}
320+
321+
return this;
322+
}
323+
323324
public Builder withBodyParameterCount(int bodyParameterCount) {
324325
this.bodyParameterCount = bodyParameterCount;
325326
return this;

0 commit comments

Comments
 (0)