Skip to content

Commit 19da2da

Browse files
authored
extract HttpHeadersUtil::parseCharacterEncoding and parseAcceptCharset (#11670)
* extract HttpHeadersUtil::parseCharacterEncoding and parseAcceptCharset To allow the usage of those methods in runtimes where no HttpRequest or HttpMessage is available * remove unnecessary class qualifier * fix test * deafult charset utf-8
1 parent 6675240 commit 19da2da

File tree

4 files changed

+110
-29
lines changed

4 files changed

+110
-29
lines changed

http/src/main/java/io/micronaut/http/HttpHeaders.java

+1-12
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import jakarta.annotation.Nullable;
2222

2323
import java.nio.charset.Charset;
24-
import java.nio.charset.StandardCharsets;
2524
import java.time.LocalDateTime;
2625
import java.time.ZoneId;
2726
import java.time.ZonedDateTime;
@@ -744,17 +743,7 @@ default Charset acceptCharset() {
744743
*/
745744
default Optional<Charset> findAcceptCharset() {
746745
return findFirst(HttpHeaders.ACCEPT_CHARSET)
747-
.map(text -> {
748-
text = HttpHeadersUtil.splitAcceptHeader(text);
749-
if (text != null) {
750-
try {
751-
return Charset.forName(text);
752-
} catch (Exception ignored) {
753-
}
754-
}
755-
// default to UTF-8
756-
return StandardCharsets.UTF_8;
757-
});
746+
.map(HttpHeadersUtil::parseAcceptCharset);
758747
}
759748

760749
/**

http/src/main/java/io/micronaut/http/util/HttpHeadersUtil.java

+68
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,14 @@
2020
import io.micronaut.core.annotation.Nullable;
2121
import io.micronaut.core.util.SupplierUtil;
2222
import io.micronaut.http.HttpHeaders;
23+
import io.micronaut.http.MediaType;
2324
import org.slf4j.Logger;
2425

26+
import java.nio.charset.Charset;
27+
import java.nio.charset.StandardCharsets;
28+
import java.nio.charset.UnsupportedCharsetException;
2529
import java.util.List;
30+
import java.util.Optional;
2631
import java.util.Set;
2732
import java.util.function.Function;
2833
import java.util.function.Supplier;
@@ -34,6 +39,7 @@
3439
* @since 3.8.0
3540
*/
3641
public final class HttpHeadersUtil {
42+
private final static Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
3743
private static final Supplier<Pattern> HEADER_MASK_PATTERNS = SupplierUtil.memoized(() ->
3844
Pattern.compile(".*(password|cred|cert|key|secret|token|auth|signat).*", Pattern.CASE_INSENSITIVE)
3945
);
@@ -135,4 +141,66 @@ public static String splitAcceptHeader(@NonNull String text) {
135141
}
136142
return text;
137143
}
144+
145+
/**
146+
* Resolve the {@link Charset} to use for request identified by the Content-Type HTTP Header value and the Accept-Charset HTTP Header value.
147+
*
148+
* @param contentTypeHeaderValue Content-Type HTTP Header Value
149+
* @param acceptCharsetHeaderValue Accept-Charset HTTP Header Value
150+
* @return A {@link Charset}
151+
* @since 4.8.8
152+
*/
153+
@NonNull
154+
public static Charset parseCharacterEncoding(@Nullable String contentTypeHeaderValue, @Nullable String acceptCharsetHeaderValue) {
155+
MediaType contentType = contentTypeHeaderValue == null ? null : MediaType.of(contentTypeHeaderValue);
156+
Charset charset = acceptCharsetHeaderValue != null ? parseAcceptCharset(acceptCharsetHeaderValue) : StandardCharsets.UTF_8;
157+
return parseCharacterEncoding(contentType, charset);
158+
}
159+
160+
/**
161+
* Resolve the {@link Charset} to use for the request.
162+
*
163+
* @param contentType ContenType
164+
* @param acceptCharset Accept Charset
165+
* @return An {@link Optional} of {@link Charset}
166+
* @since 4.8.8
167+
*/
168+
@NonNull
169+
public static Charset parseCharacterEncoding(@Nullable MediaType contentType,
170+
@NonNull Charset acceptCharset) {
171+
try {
172+
173+
if (contentType != null) {
174+
String charset = contentType.getParametersMap().get(MediaType.CHARSET_PARAMETER);
175+
if (charset != null) {
176+
try {
177+
return Charset.forName(charset);
178+
} catch (Exception e) {
179+
return DEFAULT_CHARSET;
180+
}
181+
}
182+
}
183+
} catch (UnsupportedCharsetException e) {
184+
return DEFAULT_CHARSET;
185+
}
186+
return acceptCharset;
187+
}
188+
189+
/**
190+
*
191+
* @param acceptCharsetHeaderValue Accept-Charset HeaderValue
192+
* @return Accept Charset
193+
* @since 4.8.8
194+
*/
195+
@NonNull
196+
public static Charset parseAcceptCharset(@NonNull String acceptCharsetHeaderValue) {
197+
String text = HttpHeadersUtil.splitAcceptHeader(acceptCharsetHeaderValue);
198+
if (text != null) {
199+
try {
200+
return Charset.forName(text);
201+
} catch (Exception ignored) {
202+
}
203+
}
204+
return DEFAULT_CHARSET;
205+
}
138206
}

http/src/main/java/io/micronaut/http/util/HttpUtil.java

+4-17
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
* @since 1.0
3333
*/
3434
public class HttpUtil {
35+
private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
3536

3637
/**
3738
* Return whether the given request features {@link MediaType#APPLICATION_FORM_URLENCODED} or
@@ -86,22 +87,8 @@ public static Optional<Charset> resolveCharset(@NonNull HttpMessage<?> request)
8687
@SuppressWarnings("Duplicates")
8788
@NonNull
8889
public static Charset getCharset(@NonNull HttpMessage<?> request) {
89-
try {
90-
MediaType contentType = request.getContentType().orElse(null);
91-
if (contentType != null) {
92-
String charset = contentType.getParametersMap().get(MediaType.CHARSET_PARAMETER);
93-
if (charset != null) {
94-
try {
95-
return Charset.forName(charset);
96-
} catch (Exception e) {
97-
// unsupported charset, default to UTF-8
98-
return Charset.defaultCharset();
99-
}
100-
}
101-
}
102-
} catch (UnsupportedCharsetException e) {
103-
return StandardCharsets.UTF_8;
104-
}
105-
return request.getHeaders().findAcceptCharset().orElse(StandardCharsets.UTF_8);
90+
MediaType contentType = request.getContentType().orElse(null);
91+
return HttpHeadersUtil.parseCharacterEncoding(contentType,
92+
request.getHeaders() != null ? request.getHeaders().findAcceptCharset().orElse(DEFAULT_CHARSET) : DEFAULT_CHARSET);
10693
}
10794
}

http/src/test/groovy/io/micronaut/http/util/HttpHeadersUtilSpec.groovy

+37
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@ import ch.qos.logback.core.AppenderBase
66
import io.micronaut.http.HttpHeaders
77
import org.slf4j.Logger
88
import org.slf4j.LoggerFactory
9+
import spock.lang.See
910
import spock.lang.Specification
1011

12+
import java.nio.charset.Charset
13+
import java.nio.charset.StandardCharsets
1114
import java.util.concurrent.BlockingQueue
1215
import java.util.concurrent.LinkedBlockingQueue
1316

@@ -78,6 +81,40 @@ class HttpHeadersUtilSpec extends Specification {
7881
"*" | null
7982
}
8083

84+
@See("https://udn.realityripple.com/docs/Web/HTTP/Headers/Accept-Charset")
85+
void "acceptCharset"(String headerValue, Charset expectedCharset) {
86+
when:
87+
Charset charset = HttpHeadersUtil.parseAcceptCharset(headerValue)
88+
89+
then:
90+
charset == expectedCharset
91+
92+
where:
93+
headerValue || expectedCharset
94+
'iso-8859-1' || StandardCharsets.ISO_8859_1
95+
'utf-8, iso-8859-1;q=0.5, *;q=0.1' || StandardCharsets.UTF_8
96+
'utf-8, iso-8859-1;q=0.5' || StandardCharsets.UTF_8
97+
98+
}
99+
100+
void "parse character encoding based on the Content-Type and Accept-Charset Header values"(String contentTypeHeaderValue, String acceptCharsetHeaderValue , Charset expectedCharset) {
101+
when:
102+
Charset charset = HttpHeadersUtil.parseCharacterEncoding(contentTypeHeaderValue, acceptCharsetHeaderValue)
103+
104+
then:
105+
charset == expectedCharset
106+
107+
where:
108+
contentTypeHeaderValue | acceptCharsetHeaderValue || expectedCharset
109+
null | 'iso-8859-1' || StandardCharsets.ISO_8859_1
110+
null | 'utf-8, iso-8859-1;q=0.5, *;q=0.1' || StandardCharsets.UTF_8
111+
null | 'utf-8, iso-8859-1;q=0.5' || StandardCharsets.UTF_8
112+
'application/json; charset=utf-8' | 'iso-8859-1' || StandardCharsets.UTF_8
113+
'application/json' | 'iso-8859-1' || StandardCharsets.ISO_8859_1
114+
null | null || StandardCharsets.UTF_8
115+
'application/json; charset=bogus' | 'iso-8859-1' || StandardCharsets.UTF_8
116+
}
117+
81118
static class MemoryAppender extends AppenderBase<ILoggingEvent> {
82119
final BlockingQueue<String> events = new LinkedBlockingQueue<>()
83120

0 commit comments

Comments
 (0)