From 8c33aa498375200acf5c1b7a9a73768619308cc5 Mon Sep 17 00:00:00 2001 From: gregw Date: Fri, 4 Oct 2024 09:20:40 +1000 Subject: [PATCH 01/12] Use lowercase for charsets #11741 Fix #11741 as per the WhatTFWG recommendations, use lower case for charset names. Took the opportunity for some minor optimizations: + use the already made HttpField instance in MimeTypes.Type rather than create a new one in the HttpParser.CACHE + keep the MimeType.Type associated with the pre encoded Content-Type fields --- .../migration/ServletToHandlerDocs.java | 4 + .../org/eclipse/jetty/http/HttpParser.java | 24 ++--- .../org/eclipse/jetty/http/MimeTypes.java | 97 +++++++++++++++---- .../eclipse/jetty/http/HttpParserTest.java | 20 +++- .../org/eclipse/jetty/server/FormFields.java | 11 ++- .../org/eclipse/jetty/server/Request.java | 20 +++- .../jetty/server/ErrorHandlerTest.java | 28 +++--- .../jetty/server/HttpConnectionTest.java | 4 +- .../jetty/server/HttpServerTestBase.java | 4 +- .../server/handler/DefaultHandlerTest.java | 4 +- .../jetty/ee10/servlet/ServletApiRequest.java | 10 +- .../ee10/servlet/CharacterEncodingTest.java | 2 +- .../ee10/servlet/DefaultServletTest.java | 2 +- .../ee10/servlet/ResourceServletTest.java | 2 +- .../ee10/servlet/ResponseHeadersTest.java | 4 +- .../jetty/ee11/servlet/ServletApiRequest.java | 10 +- .../ee11/servlet/CharacterEncodingTest.java | 2 +- .../ee11/servlet/DefaultServletTest.java | 2 +- .../ee11/servlet/ResourceServletTest.java | 2 +- .../ee11/servlet/ResponseHeadersTest.java | 4 +- 20 files changed, 187 insertions(+), 69 deletions(-) diff --git a/documentation/jetty/modules/code/examples/src/main/java/org/eclipse/jetty/docs/programming/migration/ServletToHandlerDocs.java b/documentation/jetty/modules/code/examples/src/main/java/org/eclipse/jetty/docs/programming/migration/ServletToHandlerDocs.java index fb64158148f2..d6a0538641fc 100644 --- a/documentation/jetty/modules/code/examples/src/main/java/org/eclipse/jetty/docs/programming/migration/ServletToHandlerDocs.java +++ b/documentation/jetty/modules/code/examples/src/main/java/org/eclipse/jetty/docs/programming/migration/ServletToHandlerDocs.java @@ -15,6 +15,7 @@ import java.io.InputStream; import java.nio.ByteBuffer; +import java.nio.charset.Charset; import java.time.Duration; import java.util.List; import java.util.Locale; @@ -28,6 +29,7 @@ import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpURI; +import org.eclipse.jetty.http.MimeTypes; import org.eclipse.jetty.http.Trailers; import org.eclipse.jetty.io.Content; import org.eclipse.jetty.server.Context; @@ -118,6 +120,8 @@ public boolean handle(Request request, Response response, Callback callback) thr // Gets the request Content-Type. // Replaces: // - servletRequest.getContentType() + MimeTypes.Type mimeType = Request.getContentMimeType(request); + Charset charset = Request.getCharset(request); String contentType = request.getHeaders().get(HttpHeader.CONTENT_TYPE); // Gets the request Content-Length. diff --git a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java index 7ba4c573f347..37109020bc04 100644 --- a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java +++ b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java @@ -19,7 +19,6 @@ import java.util.EnumSet; import java.util.LinkedHashMap; import java.util.List; -import java.util.Locale; import java.util.Map; import org.eclipse.jetty.http.HttpTokens.EndOfContent; @@ -138,25 +137,16 @@ public class HttpParser .with(new HttpField(HttpHeader.EXPIRES, "Fri, 01 Jan 1990 00:00:00 GMT")) .withAll(() -> { - Map map = new LinkedHashMap<>(); // Add common Content types as fields - for (String type : new String[]{ - "text/plain", "text/html", "text/xml", "text/json", "application/json", "application/x-www-form-urlencoded" - }) + Map map = new LinkedHashMap<>(); + for (MimeTypes.Type mimetype : MimeTypes.Type.values()) { - HttpField field = new PreEncodedHttpField(HttpHeader.CONTENT_TYPE, type); - map.put(field.toString(), field); - - for (String charset : new String[]{"utf-8", "iso-8859-1"}) + MimeTypes.ContentTypeField contentTypeField = mimetype.getContentTypeField(); + map.put(contentTypeField.toString(), contentTypeField); + if (contentTypeField.getValue().contains(";charset=")) { - PreEncodedHttpField field1 = new PreEncodedHttpField(HttpHeader.CONTENT_TYPE, type + ";charset=" + charset); - map.put(field1.toString(), field1); - PreEncodedHttpField field2 = new PreEncodedHttpField(HttpHeader.CONTENT_TYPE, type + "; charset=" + charset); - map.put(field2.toString(), field2); - PreEncodedHttpField field3 = new PreEncodedHttpField(HttpHeader.CONTENT_TYPE, type + ";charset=" + charset.toUpperCase(Locale.ENGLISH)); - map.put(field3.toString(), field3); - PreEncodedHttpField field4 = new PreEncodedHttpField(HttpHeader.CONTENT_TYPE, type + "; charset=" + charset.toUpperCase(Locale.ENGLISH)); - map.put(field4.toString(), field4); + HttpField contentTypeFieldWithSpace = new MimeTypes.ContentTypeField(contentTypeField.getMimeType(), contentTypeField.getValue().replace(";charset=", "; charset=")); + map.put(contentTypeFieldWithSpace.toString(), contentTypeFieldWithSpace); } } return map; diff --git a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/MimeTypes.java b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/MimeTypes.java index 12eecc601452..f7232f42adb9 100644 --- a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/MimeTypes.java +++ b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/MimeTypes.java @@ -124,7 +124,7 @@ public HttpField getContentTypeField(Charset charset) private final Charset _charset; private final String _charsetString; private final boolean _assumedCharset; - private final HttpField _field; + private final ContentTypeField _field; Type(String name) { @@ -133,18 +133,18 @@ public HttpField getContentTypeField(Charset charset) _charset = null; _charsetString = null; _assumedCharset = false; - _field = new PreEncodedHttpField(HttpHeader.CONTENT_TYPE, _string); + _field = new ContentTypeField(this); } Type(String name, Type base) { _string = name; - _base = base; + _base = Objects.requireNonNull(base); int i = name.indexOf(";charset="); _charset = Charset.forName(name.substring(i + 9)); _charsetString = _charset.toString().toLowerCase(Locale.ENGLISH); _assumedCharset = false; - _field = new PreEncodedHttpField(HttpHeader.CONTENT_TYPE, _string); + _field = new ContentTypeField(this); } Type(String name, Charset cs) @@ -154,9 +154,12 @@ public HttpField getContentTypeField(Charset charset) _charset = cs; _charsetString = _charset == null ? null : _charset.toString().toLowerCase(Locale.ENGLISH); _assumedCharset = true; - _field = new PreEncodedHttpField(HttpHeader.CONTENT_TYPE, _string); + _field = new ContentTypeField(this); } + /** + * @return The {@link Charset} for this type or {@code null} if it is not known + */ public Charset getCharset() { return _charset; @@ -167,6 +170,11 @@ public String getCharsetString() return _charsetString; } + /** + * Check if this type is equal to the type passed as a string + * @param type The type to compare to + * @return {@code true} if this is the same type + */ public boolean is(String type) { return _string.equalsIgnoreCase(type); @@ -183,12 +191,15 @@ public String toString() return _string; } + /** + * @return {@code true} If the {@link Charset} for this type is assumed rather than being explicitly declared. + */ public boolean isCharsetAssumed() { return _assumedCharset; } - public HttpField getContentTypeField() + public ContentTypeField getContentTypeField() { return _field; } @@ -200,6 +211,10 @@ public HttpField getContentTypeField(Charset charset) return new HttpField(HttpHeader.CONTENT_TYPE, getContentTypeWithoutCharset(_string) + ";charset=" + charset.name()); } + /** + * Get the base type of this type, which is the type without a charset specified + * @return The base type or this type if it is a base type + */ public Type getBaseType() { return _base; @@ -227,23 +242,34 @@ public Type getBaseType() }) .build(); + /** + * Get the base value, stripped of any parameters + * @param value The value + * @return A string with any semicolon separated parameters removed + */ + public static String getBase(String value) + { + int index = value.indexOf(';'); + return index == -1 ? value : value.substring(0, index); + } + + /** + * Get the base type of this type, which is the type without a charset specified + * @param contentType The mimetype as a string + * @return The base type or this type if it is a base type + */ public static Type getBaseType(String contentType) { if (StringUtil.isEmpty(contentType)) return null; Type type = CACHE.getBest(contentType); if (type == null) - return null; - if (type.asString().length() == contentType.length()) - return type.getBaseType(); - if (contentType.charAt(type.asString().length()) == ';') - return type.getBaseType(); - contentType = contentType.replace(" ", ""); - if (type.asString().length() == contentType.length()) - return type.getBaseType(); - if (contentType.charAt(type.asString().length()) == ';') - return type.getBaseType(); - return null; + { + type = CACHE.get(getBase(contentType)); + if (type == null) + return null; + } + return type.getBaseType(); } public static boolean isKnownLocale(Locale locale) @@ -326,6 +352,23 @@ public MimeTypes(MimeTypes defaults) } } + /** + * Get the explicit, assumed, or inferred Charset for a HttpField containing a mime type value + * @param field HttpField with a mime type value (e.g. Content-Type) + * @return A {@link Charset} or null; + * @throws IllegalCharsetNameException + * If the given charset name is illegal + * @throws UnsupportedCharsetException + * If no support for the named charset is available + * in this instance of the Java virtual machine + */ + public Charset getCharset(HttpField field) throws IllegalCharsetNameException, UnsupportedCharsetException + { + if (field instanceof ContentTypeField contentTypeField) + return contentTypeField.getMimeType().getCharset(); + return getCharset(field.getValue()); + } + /** * Get the explicit, assumed, or inferred Charset for a mime type * @param mimeType String form or a mimeType @@ -876,4 +919,24 @@ else if (' ' != b) return value; return builder.toString(); } + + public static class ContentTypeField extends PreEncodedHttpField + { + private final Type _type; + public ContentTypeField(MimeTypes.Type type) + { + this(type, type.toString()); + } + + public ContentTypeField(MimeTypes.Type type, String value) + { + super(HttpHeader.CONTENT_TYPE, value); + _type = type; + } + + public Type getMimeType() + { + return _type; + } + } } diff --git a/jetty-core/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java b/jetty-core/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java index 603c314d7684..faef0134ea63 100644 --- a/jetty-core/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java +++ b/jetty-core/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java @@ -2601,7 +2601,25 @@ public void testRequestMaxHeaderBytesCumulative(String eoln) @ParameterizedTest @ValueSource(strings = {"\r\n", "\n"}) @SuppressWarnings("ReferenceEquality") - public void testCachedField(String eoln) + public void testInsensitiveCachedField(String eoln) + { + ByteBuffer buffer = BufferUtil.toBuffer( + "GET / HTTP/1.1" + eoln + + "Content-Type: text/plain;Charset=UTF-8" + eoln + + eoln); + + HttpParser.RequestHandler handler = new Handler(); + HttpParser parser = new HttpParser(handler); + parseAll(parser, buffer); + + HttpField field = _fields.get(0); + assertThat(field.getValue(), is("text/plain;charset=utf-8")); + } + + @ParameterizedTest + @ValueSource(strings = {"\r\n", "\n"}) + @SuppressWarnings("ReferenceEquality") + public void testDynamicCachedField(String eoln) { ByteBuffer buffer = BufferUtil.toBuffer( "GET / HTTP/1.1" + eoln + diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/FormFields.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/FormFields.java index a3558caf89c3..cd719ff21e60 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/FormFields.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/FormFields.java @@ -19,6 +19,7 @@ import java.nio.charset.StandardCharsets; import java.util.concurrent.CompletableFuture; +import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.MimeTypes; import org.eclipse.jetty.io.Content; @@ -51,7 +52,15 @@ public static Charset getFormEncodedCharset(Request request) if (!config.getFormEncodedMethods().contains(request.getMethod())) return null; - String contentType = request.getHeaders().get(HttpHeader.CONTENT_TYPE); + HttpField contentTypeField= request.getHeaders().getField(HttpHeader.CONTENT_TYPE); + if (contentTypeField instanceof MimeTypes.ContentTypeField contentMimeTypeField) + { + MimeTypes.Type type = contentMimeTypeField.getMimeType(); + if (type != null && type.getCharset() != null) + return type.getCharset(); + } + + String contentType = contentTypeField.getValue(); if (request.getLength() == 0 || StringUtil.isBlank(contentType)) return null; diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java index 84fbc3cd2385..0bdfa5c35ecc 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java @@ -37,6 +37,7 @@ import org.eclipse.jetty.http.ComplianceViolation; import org.eclipse.jetty.http.HttpCookie; +import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpScheme; @@ -535,6 +536,21 @@ static InputStream asInputStream(Request request) return Content.Source.asInputStream(request); } + /** + * Get a known {@link MimeTypes.Type} from the request {@link HttpHeader#CONTENT_TYPE}, if any. + * @param request The request. + * @return A {@link MimeTypes} or {@code null} if the {@code Content-Type} is not set or not known. + */ + static MimeTypes.Type getContentMimeType(Request request) + { + HttpField contentType= request.getHeaders().getField(HttpHeader.CONTENT_TYPE); + if (contentType instanceof MimeTypes.ContentTypeField contentTypeField) + return contentTypeField.getMimeType(); + if (contentType == null) + return null; + return MimeTypes.CACHE.get(contentType.getValue()); + } + /** * Get a {@link Charset} from the request {@link HttpHeader#CONTENT_TYPE}, if any. * @param request The request. @@ -544,7 +560,9 @@ static InputStream asInputStream(Request request) */ static Charset getCharset(Request request) throws IllegalCharsetNameException, UnsupportedCharsetException { - String contentType = request.getHeaders().get(HttpHeader.CONTENT_TYPE); + HttpField contentType= request.getHeaders().getField(HttpHeader.CONTENT_TYPE); + if (contentType == null) + return null; return Objects.requireNonNullElse(request.getContext().getMimeTypes(), MimeTypes.DEFAULTS).getCharset(contentType); } diff --git a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ErrorHandlerTest.java b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ErrorHandlerTest.java index f06ab857f392..cca4ca9720fa 100644 --- a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ErrorHandlerTest.java +++ b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ErrorHandlerTest.java @@ -153,7 +153,7 @@ public void test404NoAccept() throws Exception assertThat("Response status code", response.getStatus(), is(404)); assertThat("Response Content-Length", response.getField(HttpHeader.CONTENT_LENGTH).getIntValue(), greaterThan(0)); - assertThat("Response Content-Type", response.get(HttpHeader.CONTENT_TYPE), containsString("text/html;charset=ISO-8859-1")); + assertThat("Response Content-Type", response.get(HttpHeader.CONTENT_TYPE), containsString("text/html;charset=iso-8859-1")); assertThat(response.get(HttpHeader.DATE), notNullValue()); assertThat(response.getContent(), containsString("content=\"text/html;charset=ISO-8859-1\"")); @@ -227,7 +227,7 @@ public void test404AllAccept() throws Exception assertThat("Response status code", response.getStatus(), is(404)); assertThat("Response Content-Length", response.getField(HttpHeader.CONTENT_LENGTH).getIntValue(), greaterThan(0)); - assertThat("Response Content-Type", response.get(HttpHeader.CONTENT_TYPE), containsString("text/html;charset=ISO-8859-1")); + assertThat("Response Content-Type", response.get(HttpHeader.CONTENT_TYPE), containsString("text/html;charset=iso-8859-1")); assertThat(response.getContent(), containsString("content=\"text/html;charset=ISO-8859-1\"")); assertContent(response); } @@ -245,7 +245,7 @@ public void test404HtmlAccept() throws Exception assertThat("Response status code", response.getStatus(), is(404)); assertThat("Response Content-Length", response.getField(HttpHeader.CONTENT_LENGTH).getIntValue(), greaterThan(0)); - assertThat("Response Content-Type", response.get(HttpHeader.CONTENT_TYPE), containsString("text/html;charset=ISO-8859-1")); + assertThat("Response Content-Type", response.get(HttpHeader.CONTENT_TYPE), containsString("text/html;charset=iso-8859-1")); assertThat(response.getContent(), containsString("content=\"text/html;charset=ISO-8859-1\"")); assertContent(response); } @@ -266,7 +266,7 @@ public void test404PostHttp10() throws Exception assertThat(response.getStatus(), is(404)); assertThat(response.getField(HttpHeader.CONTENT_LENGTH).getIntValue(), greaterThan(0)); - assertThat(response.get(HttpHeader.CONTENT_TYPE), containsString("text/html;charset=ISO-8859-1")); + assertThat(response.get(HttpHeader.CONTENT_TYPE), containsString("text/html;charset=iso-8859-1")); assertThat(response.getContent(), containsString("content=\"text/html;charset=ISO-8859-1\"")); assertThat(response.get(HttpHeader.CONNECTION), is("keep-alive")); assertContent(response); @@ -288,7 +288,7 @@ public void test404PostHttp11() throws Exception assertThat(response.getStatus(), is(404)); assertThat(response.getField(HttpHeader.CONTENT_LENGTH).getIntValue(), greaterThan(0)); - assertThat(response.get(HttpHeader.CONTENT_TYPE), containsString("text/html;charset=ISO-8859-1")); + assertThat(response.get(HttpHeader.CONTENT_TYPE), containsString("text/html;charset=iso-8859-1")); assertThat(response.getContent(), containsString("content=\"text/html;charset=ISO-8859-1\"")); assertThat(response.getField(HttpHeader.CONNECTION), nullValue()); assertContent(response); @@ -307,7 +307,7 @@ public void testMoreSpecificAccept() throws Exception assertThat("Response status code", response.getStatus(), is(404)); assertThat("Response Content-Length", response.getField(HttpHeader.CONTENT_LENGTH).getIntValue(), greaterThan(0)); - assertThat("Response Content-Type", response.get(HttpHeader.CONTENT_TYPE), containsString("text/html;charset=ISO-8859-1")); + assertThat("Response Content-Type", response.get(HttpHeader.CONTENT_TYPE), containsString("text/html;charset=iso-8859-1")); assertThat(response.getContent(), containsString("content=\"text/html;charset=ISO-8859-1\"")); assertContent(response); @@ -327,7 +327,7 @@ public void test404HtmlAcceptAnyCharset() throws Exception assertThat("Response status code", response.getStatus(), is(404)); assertThat("Response Content-Length", response.getField(HttpHeader.CONTENT_LENGTH).getIntValue(), greaterThan(0)); - assertThat("Response Content-Type", response.get(HttpHeader.CONTENT_TYPE), containsString("text/html;charset=UTF-8")); + assertThat("Response Content-Type", response.get(HttpHeader.CONTENT_TYPE), containsString("text/html;charset=utf-8")); assertThat(response.getContent(), containsString("content=\"text/html;charset=UTF-8\"")); assertContent(response); @@ -347,7 +347,7 @@ public void test404HtmlAcceptUtf8Charset() throws Exception assertThat("Response status code", response.getStatus(), is(404)); assertThat("Response Content-Length", response.getField(HttpHeader.CONTENT_LENGTH).getIntValue(), greaterThan(0)); - assertThat("Response Content-Type", response.get(HttpHeader.CONTENT_TYPE), containsString("text/html;charset=UTF-8")); + assertThat("Response Content-Type", response.get(HttpHeader.CONTENT_TYPE), containsString("text/html;charset=utf-8")); assertThat(response.getContent(), containsString("content=\"text/html;charset=UTF-8\"")); assertContent(response); @@ -400,7 +400,7 @@ public void test404HtmlAcceptUnknownUtf8Charset() throws Exception assertThat("Response status code", response.getStatus(), is(404)); assertThat("Response Content-Length", response.getField(HttpHeader.CONTENT_LENGTH).getIntValue(), greaterThan(0)); - assertThat("Response Content-Type", response.get(HttpHeader.CONTENT_TYPE), containsString("text/html;charset=UTF-8")); + assertThat("Response Content-Type", response.get(HttpHeader.CONTENT_TYPE), containsString("text/html;charset=utf-8")); assertThat(response.getContent(), containsString("content=\"text/html;charset=UTF-8\"")); assertContent(response); @@ -420,7 +420,7 @@ public void test404PreferHtml() throws Exception assertThat("Response status code", response.getStatus(), is(404)); assertThat("Response Content-Length", response.getField(HttpHeader.CONTENT_LENGTH).getIntValue(), greaterThan(0)); - assertThat("Response Content-Type", response.get(HttpHeader.CONTENT_TYPE), containsString("text/html;charset=UTF-8")); + assertThat("Response Content-Type", response.get(HttpHeader.CONTENT_TYPE), containsString("text/html;charset=utf-8")); assertThat(response.getContent(), containsString("Error 404 Not Found")); assertContent(response); @@ -457,7 +457,7 @@ public void testThrowBadMessage() throws Exception assertThat("Response status code", response.getStatus(), is(444)); assertThat("Response Content-Length", response.getField(HttpHeader.CONTENT_LENGTH).getIntValue(), greaterThan(0)); - assertThat("Response Content-Type", response.get(HttpHeader.CONTENT_TYPE), containsString("text/html;charset=ISO-8859-1")); + assertThat("Response Content-Type", response.get(HttpHeader.CONTENT_TYPE), containsString("text/html;charset=iso-8859-1")); assertThat(response.getContent(), containsString("content=\"text/html;charset=ISO-8859-1\"")); assertContent(response); @@ -475,7 +475,7 @@ public void testBadMessage() throws Exception assertThat("Response status code", response.getStatus(), is(400)); assertThat("Response Content-Length", response.getField(HttpHeader.CONTENT_LENGTH).getIntValue(), greaterThan(0)); - assertThat("Response Content-Type", response.get(HttpHeader.CONTENT_TYPE), containsString("text/html;charset=ISO-8859-1")); + assertThat("Response Content-Type", response.get(HttpHeader.CONTENT_TYPE), containsString("text/html;charset=iso-8859-1")); assertThat(response.getContent(), containsString("content=\"text/html;charset=ISO-8859-1\"")); assertContent(response); @@ -601,7 +601,7 @@ public void testComplexCauseMessageNoAcceptHeader(String path) throws Exception assertThat("Response status code", response.getStatus(), is(500)); assertThat("Response Content-Length", response.getField(HttpHeader.CONTENT_LENGTH).getIntValue(), greaterThan(0)); - assertThat("Response Content-Type", response.get(HttpHeader.CONTENT_TYPE), containsString("text/html;charset=ISO-8859-1")); + assertThat("Response Content-Type", response.get(HttpHeader.CONTENT_TYPE), containsString("text/html;charset=iso-8859-1")); assertThat(response.getContent(), containsString("content=\"text/html;charset=ISO-8859-1\"")); String content = assertContent(response); @@ -633,7 +633,7 @@ public void testComplexCauseMessageAcceptUtf8Header(String path) throws Exceptio assertThat("Response status code", response.getStatus(), is(500)); assertThat("Response Content-Length", response.getField(HttpHeader.CONTENT_LENGTH).getIntValue(), greaterThan(0)); - assertThat("Response Content-Type", response.get(HttpHeader.CONTENT_TYPE), containsString("text/html;charset=UTF-8")); + assertThat("Response Content-Type", response.get(HttpHeader.CONTENT_TYPE), containsString("text/html;charset=utf-8")); assertThat(response.getContent(), containsString("content=\"text/html;charset=UTF-8\"")); String content = assertContent(response); diff --git a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java index b93a6daa100f..a151e9990bea 100644 --- a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java +++ b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java @@ -965,7 +965,7 @@ public void testUnconsumed() throws Exception checkNotContained(response, offset, "56789"); offset = checkContains(response, offset, "HTTP/1.1 200"); offset = checkContains(response, offset, "pathInContext=/R2"); - offset = checkContains(response, offset, "charset=UTF-8"); + offset = checkContains(response, offset, "charset=utf-8"); checkContains(response, offset, "abcdefghij"); } @@ -1061,7 +1061,7 @@ public void testUnconsumedErrorStream() throws Exception offset = checkContains(response, offset, "HTTP/1.1 599"); offset = checkContains(response, offset, "HTTP/1.1 200"); offset = checkContains(response, offset, "/R2"); - offset = checkContains(response, offset, "text/plain; charset=UTF-8"); + offset = checkContains(response, offset, "text/plain; charset=utf-8"); checkContains(response, offset, "abcdefghij"); } diff --git a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java index 6cf5573bc50a..14abf2d42383 100644 --- a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java +++ b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java @@ -111,7 +111,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture protected static final String REQUEST2_HEADER = "POST / HTTP/1.0\n" + "Host: localhost\n" + - "Content-Type: text/xml; charset=ISO-8859-1\n" + + "Content-Type: text/xml; charset=iso-8859-1\n" + "Content-Length: "; protected static final String REQUEST2_CONTENT = "\n" + @@ -127,7 +127,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture protected static final String RESPONSE2 = "HTTP/1.1 200 OK\n" + - "Content-Type: text/xml; charset=ISO-8859-1\n" + + "Content-Type: text/xml; charset=iso-8859-1\n" + "Content-Length: " + REQUEST2_CONTENT.getBytes().length + "\n" + "\n" + REQUEST2_CONTENT; diff --git a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/handler/DefaultHandlerTest.java b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/handler/DefaultHandlerTest.java index 1e5996cc601c..692ef04ac642 100644 --- a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/handler/DefaultHandlerTest.java +++ b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/handler/DefaultHandlerTest.java @@ -79,7 +79,7 @@ public void testRoot() throws Exception HttpTester.Response response = HttpTester.parseResponse(input); assertEquals(HttpStatus.NOT_FOUND_404, response.getStatus()); - assertEquals("text/html;charset=UTF-8", response.get(HttpHeader.CONTENT_TYPE)); + assertEquals("text/html;charset=utf-8", response.get(HttpHeader.CONTENT_TYPE)); String content = new String(response.getContentBytes(), StandardCharsets.UTF_8); assertThat(content, containsString("Contexts known to this server are:")); @@ -105,7 +105,7 @@ public void testSomePath() throws Exception HttpTester.Response response = HttpTester.parseResponse(input); assertEquals(HttpStatus.NOT_FOUND_404, response.getStatus()); - assertEquals("text/html;charset=ISO-8859-1", response.get(HttpHeader.CONTENT_TYPE)); + assertEquals("text/html;charset=iso-8859-1", response.get(HttpHeader.CONTENT_TYPE)); String content = new String(response.getContentBytes(), StandardCharsets.ISO_8859_1); assertThat(content, not(containsString("Contexts known to this server are:"))); diff --git a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletApiRequest.java b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletApiRequest.java index cd5504e9c120..41326c4a0d53 100644 --- a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletApiRequest.java +++ b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletApiRequest.java @@ -1183,7 +1183,15 @@ public long getContentLengthLong() public String getContentType() { if (_contentType == null) - _contentType = getFields().get(HttpHeader.CONTENT_TYPE); + { + HttpField contentType = getFields().getField(HttpHeader.CONTENT_TYPE); + if (contentType != null) + { + _contentType = contentType.getValue(); + if (_charset == null && contentType instanceof MimeTypes.ContentTypeField contentTypeField) + _charset = contentTypeField.getMimeType().getCharset(); + } + } return _contentType; } diff --git a/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/CharacterEncodingTest.java b/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/CharacterEncodingTest.java index f4ad0dfd1dd1..094b827e2787 100644 --- a/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/CharacterEncodingTest.java +++ b/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/CharacterEncodingTest.java @@ -134,7 +134,7 @@ public void testCharacterEncodingSetTwice() throws Exception // Now test for properly formatted HTTP Response Headers. assertThat("Response Code", response.getStatus(), is(200)); - assertThat(response.get(HttpHeader.CONTENT_TYPE), is("text/plain;charset=UTF-8")); + assertThat(response.get(HttpHeader.CONTENT_TYPE), is("text/plain;charset=utf-8")); } } diff --git a/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/DefaultServletTest.java b/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/DefaultServletTest.java index 41845f7dfd75..ba25912b6289 100644 --- a/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/DefaultServletTest.java +++ b/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/DefaultServletTest.java @@ -494,7 +494,7 @@ public void testSimpleListing() throws Exception HttpTester.Response response = HttpTester.parseResponse(rawResponse); assertThat(response.getStatus(), is(200)); - assertThat(response.getField("content-type").getValue(), is("text/html;charset=UTF-8")); + assertThat(response.getField("content-type").getValue(), is("text/html;charset=utf-8")); String body = response.getContent(); assertThat(body, containsString("")); } diff --git a/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/ResourceServletTest.java b/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/ResourceServletTest.java index 7d470cbbe618..89828695530b 100644 --- a/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/ResourceServletTest.java +++ b/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/ResourceServletTest.java @@ -557,7 +557,7 @@ public void testSimpleListing() throws Exception HttpTester.Response response = HttpTester.parseResponse(rawResponse); assertThat(response.getStatus(), is(200)); - assertThat(response.getField("content-type").getValue(), is("text/html;charset=UTF-8")); + assertThat(response.getField("content-type").getValue(), is("text/html;charset=utf-8")); String body = response.getContent(); assertThat(body, containsString("")); } diff --git a/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/ResponseHeadersTest.java b/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/ResponseHeadersTest.java index fd5848e70d0d..939357f5dfc2 100644 --- a/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/ResponseHeadersTest.java +++ b/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/ResponseHeadersTest.java @@ -146,7 +146,7 @@ protected void doGet(HttpServletRequest req, HttpServletResponse response) throw // Now test for properly formatted HTTP Response Headers. assertThat("Response Code", response.getStatus(), is(200)); - assertThat("Response Header Content-Type", response.get("Content-Type"), is("text/plain;charset=UTF-8")); + assertThat("Response Header Content-Type", response.get("Content-Type"), is("text/plain;charset=utf-8")); String expected = StringUtil.replace(actualPathInfo, "%0A", " "); // replace OBS fold with space expected = URLDecoder.decode(expected, StandardCharsets.UTF_8); // decode the rest @@ -189,7 +189,7 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t // Now test for properly formatted HTTP Response Headers. assertThat("Response Code", response.getStatus(), is(200)); // The Content-Type should not have a charset= portion - assertThat("Response Header Content-Type", response.get("Content-Type"), is("text/html;charset=UTF-8")); + assertThat("Response Header Content-Type", response.get("Content-Type"), is("text/html;charset=utf-8")); } @Test diff --git a/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/ServletApiRequest.java b/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/ServletApiRequest.java index 5a7203b31009..4aaa4ced8cb3 100644 --- a/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/ServletApiRequest.java +++ b/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/ServletApiRequest.java @@ -1192,7 +1192,15 @@ public long getContentLengthLong() public String getContentType() { if (_contentType == null) - _contentType = getFields().get(HttpHeader.CONTENT_TYPE); + { + HttpField contentType = getFields().getField(HttpHeader.CONTENT_TYPE); + if (contentType != null) + { + _contentType = contentType.getValue(); + if (_charset == null && contentType instanceof MimeTypes.ContentTypeField contentTypeField) + _charset = contentTypeField.getMimeType().getCharset(); + } + } return _contentType; } diff --git a/jetty-ee11/jetty-ee11-servlet/src/test/java/org/eclipse/jetty/ee11/servlet/CharacterEncodingTest.java b/jetty-ee11/jetty-ee11-servlet/src/test/java/org/eclipse/jetty/ee11/servlet/CharacterEncodingTest.java index 79ec8f1d0a75..c7b414c14e28 100644 --- a/jetty-ee11/jetty-ee11-servlet/src/test/java/org/eclipse/jetty/ee11/servlet/CharacterEncodingTest.java +++ b/jetty-ee11/jetty-ee11-servlet/src/test/java/org/eclipse/jetty/ee11/servlet/CharacterEncodingTest.java @@ -134,7 +134,7 @@ public void testCharacterEncodingSetTwice() throws Exception // Now test for properly formatted HTTP Response Headers. assertThat("Response Code", response.getStatus(), is(200)); - assertThat(response.get(HttpHeader.CONTENT_TYPE), is("text/plain;charset=UTF-8")); + assertThat(response.get(HttpHeader.CONTENT_TYPE), is("text/plain;charset=utf-8")); } } diff --git a/jetty-ee11/jetty-ee11-servlet/src/test/java/org/eclipse/jetty/ee11/servlet/DefaultServletTest.java b/jetty-ee11/jetty-ee11-servlet/src/test/java/org/eclipse/jetty/ee11/servlet/DefaultServletTest.java index b2618dd5f397..ed8653dd4638 100644 --- a/jetty-ee11/jetty-ee11-servlet/src/test/java/org/eclipse/jetty/ee11/servlet/DefaultServletTest.java +++ b/jetty-ee11/jetty-ee11-servlet/src/test/java/org/eclipse/jetty/ee11/servlet/DefaultServletTest.java @@ -494,7 +494,7 @@ public void testSimpleListing() throws Exception HttpTester.Response response = HttpTester.parseResponse(rawResponse); assertThat(response.getStatus(), is(200)); - assertThat(response.getField("content-type").getValue(), is("text/html;charset=UTF-8")); + assertThat(response.getField("content-type").getValue(), is("text/html;charset=utf-8")); String body = response.getContent(); assertThat(body, containsString("")); } diff --git a/jetty-ee11/jetty-ee11-servlet/src/test/java/org/eclipse/jetty/ee11/servlet/ResourceServletTest.java b/jetty-ee11/jetty-ee11-servlet/src/test/java/org/eclipse/jetty/ee11/servlet/ResourceServletTest.java index 0100fe971f38..b87c4967188b 100644 --- a/jetty-ee11/jetty-ee11-servlet/src/test/java/org/eclipse/jetty/ee11/servlet/ResourceServletTest.java +++ b/jetty-ee11/jetty-ee11-servlet/src/test/java/org/eclipse/jetty/ee11/servlet/ResourceServletTest.java @@ -555,7 +555,7 @@ public void testSimpleListing() throws Exception HttpTester.Response response = HttpTester.parseResponse(rawResponse); assertThat(response.getStatus(), is(200)); - assertThat(response.getField("content-type").getValue(), is("text/html;charset=UTF-8")); + assertThat(response.getField("content-type").getValue(), is("text/html;charset=utf-8")); String body = response.getContent(); assertThat(body, containsString("")); } diff --git a/jetty-ee11/jetty-ee11-servlet/src/test/java/org/eclipse/jetty/ee11/servlet/ResponseHeadersTest.java b/jetty-ee11/jetty-ee11-servlet/src/test/java/org/eclipse/jetty/ee11/servlet/ResponseHeadersTest.java index 77d43ffa9278..33b079575f7b 100644 --- a/jetty-ee11/jetty-ee11-servlet/src/test/java/org/eclipse/jetty/ee11/servlet/ResponseHeadersTest.java +++ b/jetty-ee11/jetty-ee11-servlet/src/test/java/org/eclipse/jetty/ee11/servlet/ResponseHeadersTest.java @@ -147,7 +147,7 @@ protected void doGet(HttpServletRequest req, HttpServletResponse response) throw // Now test for properly formatted HTTP Response Headers. assertThat("Response Code", response.getStatus(), is(200)); - assertThat("Response Header Content-Type", response.get("Content-Type"), is("text/plain;charset=UTF-8")); + assertThat("Response Header Content-Type", response.get("Content-Type"), is("text/plain;charset=utf-8")); String expected = StringUtil.replace(actualPathInfo, "%0A", " "); // replace OBS fold with space expected = URLDecoder.decode(expected, StandardCharsets.UTF_8); // decode the rest @@ -190,7 +190,7 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t // Now test for properly formatted HTTP Response Headers. assertThat("Response Code", response.getStatus(), is(200)); // The Content-Type should not have a charset= portion - assertThat("Response Header Content-Type", response.get("Content-Type"), is("text/html;charset=UTF-8")); + assertThat("Response Header Content-Type", response.get("Content-Type"), is("text/html;charset=utf-8")); } @Test From ccec3e4176d62023bc5b9ff84ad8243eeb732931 Mon Sep 17 00:00:00 2001 From: gregw Date: Fri, 4 Oct 2024 11:21:28 +1000 Subject: [PATCH 02/12] Use lowercase for charsets #11741 Fix #11741 as per the WhatTFWG recommendations, use lower case for charset names. Took the opportunity for some minor optimizations: + use the already made HttpField instance in MimeTypes.Type rather than create a new one in the HttpParser.CACHE + keep the MimeType.Type associated with the pre encoded Content-Type fields --- .../src/main/java/org/eclipse/jetty/http/MimeTypes.java | 1 + 1 file changed, 1 insertion(+) diff --git a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/MimeTypes.java b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/MimeTypes.java index f7232f42adb9..4b299e2c0ee4 100644 --- a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/MimeTypes.java +++ b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/MimeTypes.java @@ -923,6 +923,7 @@ else if (' ' != b) public static class ContentTypeField extends PreEncodedHttpField { private final Type _type; + public ContentTypeField(MimeTypes.Type type) { this(type, type.toString()); From 1001c318b10c8ec1b526ddb339b49922892c5401 Mon Sep 17 00:00:00 2001 From: gregw Date: Fri, 4 Oct 2024 11:24:16 +1000 Subject: [PATCH 03/12] Use lowercase for charsets #11741 Fix #11741 as per the WhatTFWG recommendations, use lower case for charset names. Took the opportunity for some minor optimizations: + use the already made HttpField instance in MimeTypes.Type rather than create a new one in the HttpParser.CACHE + keep the MimeType.Type associated with the pre encoded Content-Type fields --- .../src/main/java/org/eclipse/jetty/server/FormFields.java | 2 +- .../src/main/java/org/eclipse/jetty/server/Request.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/FormFields.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/FormFields.java index cd719ff21e60..9f15a7943772 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/FormFields.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/FormFields.java @@ -52,7 +52,7 @@ public static Charset getFormEncodedCharset(Request request) if (!config.getFormEncodedMethods().contains(request.getMethod())) return null; - HttpField contentTypeField= request.getHeaders().getField(HttpHeader.CONTENT_TYPE); + HttpField contentTypeField = request.getHeaders().getField(HttpHeader.CONTENT_TYPE); if (contentTypeField instanceof MimeTypes.ContentTypeField contentMimeTypeField) { MimeTypes.Type type = contentMimeTypeField.getMimeType(); diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java index 0bdfa5c35ecc..5583af0d4944 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java @@ -543,7 +543,7 @@ static InputStream asInputStream(Request request) */ static MimeTypes.Type getContentMimeType(Request request) { - HttpField contentType= request.getHeaders().getField(HttpHeader.CONTENT_TYPE); + HttpField contentType = request.getHeaders().getField(HttpHeader.CONTENT_TYPE); if (contentType instanceof MimeTypes.ContentTypeField contentTypeField) return contentTypeField.getMimeType(); if (contentType == null) @@ -560,7 +560,7 @@ static MimeTypes.Type getContentMimeType(Request request) */ static Charset getCharset(Request request) throws IllegalCharsetNameException, UnsupportedCharsetException { - HttpField contentType= request.getHeaders().getField(HttpHeader.CONTENT_TYPE); + HttpField contentType = request.getHeaders().getField(HttpHeader.CONTENT_TYPE); if (contentType == null) return null; return Objects.requireNonNullElse(request.getContext().getMimeTypes(), MimeTypes.DEFAULTS).getCharset(contentType); From 8c25183f9d52cd9dae887c09598c46296af2c38b Mon Sep 17 00:00:00 2001 From: gregw Date: Fri, 4 Oct 2024 15:19:41 +1000 Subject: [PATCH 04/12] Use lowercase for charsets #11741 Fix #11741 as per the WhatTFWG recommendations, use lower case for charset names. Took the opportunity for some minor optimizations: + use the already made HttpField instance in MimeTypes.Type rather than create a new one in the HttpParser.CACHE + keep the MimeType.Type associated with the pre encoded Content-Type fields --- .../src/main/java/org/eclipse/jetty/server/FormFields.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/FormFields.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/FormFields.java index 9f15a7943772..d75ec94980dd 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/FormFields.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/FormFields.java @@ -53,6 +53,9 @@ public static Charset getFormEncodedCharset(Request request) return null; HttpField contentTypeField = request.getHeaders().getField(HttpHeader.CONTENT_TYPE); + if (contentTypeField == null) + return null; + if (contentTypeField instanceof MimeTypes.ContentTypeField contentMimeTypeField) { MimeTypes.Type type = contentMimeTypeField.getMimeType(); From 3d7f5da03cfc1b3c6270d75decfc3a910f4d0aae Mon Sep 17 00:00:00 2001 From: gregw Date: Sun, 6 Oct 2024 11:28:21 +1100 Subject: [PATCH 05/12] Use lowercase for charsets #11741 Fix #11741 as per the WhatTFWG recommendations, use lower case for charset names. Took the opportunity for some minor optimizations: + use the already made HttpField instance in MimeTypes.Type rather than create a new one in the HttpParser.CACHE + keep the MimeType.Type associated with the pre encoded Content-Type fields --- .../eclipse/jetty/client/ContentResponseTest.java | 6 ++++-- .../client/util/TypedContentProviderTest.java | 3 ++- .../ee10/test/HttpInputTransientErrorTest.java | 7 ++++--- .../eclipse/jetty/ee11/servlet/ResponseTest.java | 3 ++- .../ee11/test/HttpInputTransientErrorTest.java | 15 ++++++++------- .../ee9/test/HttpInputTransientErrorTest.java | 2 +- 6 files changed, 21 insertions(+), 15 deletions(-) diff --git a/jetty-core/jetty-client/src/test/java/org/eclipse/jetty/client/ContentResponseTest.java b/jetty-core/jetty-client/src/test/java/org/eclipse/jetty/client/ContentResponseTest.java index 0258053a5d31..a3715ff3bda0 100644 --- a/jetty-core/jetty-client/src/test/java/org/eclipse/jetty/client/ContentResponseTest.java +++ b/jetty-core/jetty-client/src/test/java/org/eclipse/jetty/client/ContentResponseTest.java @@ -23,9 +23,11 @@ import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Response; import org.eclipse.jetty.util.Callback; +import org.hamcrest.Matchers; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ArgumentsSource; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; @@ -114,7 +116,7 @@ public boolean handle(Request request, Response response, Callback callback) thr assertEquals(200, response.getStatus()); assertEquals(content, response.getContentAsString()); assertEquals(mediaType, response.getMediaType()); - assertEquals(encoding, response.getEncoding()); + assertThat(response.getEncoding(), Matchers.equalToIgnoringCase(encoding)); } @ParameterizedTest @@ -144,6 +146,6 @@ public boolean handle(Request request, Response response, Callback callback) thr assertEquals(200, response.getStatus()); assertEquals(content, response.getContentAsString()); assertEquals(mediaType, response.getMediaType()); - assertEquals(encoding, response.getEncoding()); + assertThat(response.getEncoding(), Matchers.equalToIgnoringCase(encoding)); } } diff --git a/jetty-core/jetty-client/src/test/java/org/eclipse/jetty/client/util/TypedContentProviderTest.java b/jetty-core/jetty-client/src/test/java/org/eclipse/jetty/client/util/TypedContentProviderTest.java index 72515336bbf6..1057d69585bf 100644 --- a/jetty-core/jetty-client/src/test/java/org/eclipse/jetty/client/util/TypedContentProviderTest.java +++ b/jetty-core/jetty-client/src/test/java/org/eclipse/jetty/client/util/TypedContentProviderTest.java @@ -29,6 +29,7 @@ import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Response; import org.eclipse.jetty.util.Fields; +import org.hamcrest.Matchers; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ArgumentsSource; @@ -96,7 +97,7 @@ public void testFormContentProviderWithDifferentContentType(Scenario scenario) t protected void service(Request request, Response response) throws Throwable { assertEquals("POST", request.getMethod()); - assertEquals(contentType, request.getHeaders().get(HttpHeader.CONTENT_TYPE)); + assertThat(request.getHeaders().get(HttpHeader.CONTENT_TYPE), Matchers.equalToIgnoringCase(contentType)); assertEquals(content, Content.Source.asString(request)); } }); diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/HttpInputTransientErrorTest.java b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/HttpInputTransientErrorTest.java index a767e2edea3b..57fdb3f1697c 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/HttpInputTransientErrorTest.java +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/HttpInputTransientErrorTest.java @@ -46,6 +46,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalToIgnoringCase; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; import static org.junit.jupiter.api.Assertions.assertInstanceOf; @@ -181,7 +182,7 @@ public void onError(Throwable t) assertThat("Unexpected response status\n" + response + response.getContent(), response.getStatus(), is(HttpStatus.OK_200)); assertThat(response.get(HttpHeader.CONNECTION), nullValue()); - assertThat(response.get(HttpHeader.CONTENT_TYPE), is("text/plain;charset=UTF-8")); + assertThat(response.get(HttpHeader.CONTENT_TYPE), equalToIgnoringCase("text/plain;charset=utf-8")); assertThat(response.getContent(), containsString("read=10")); assertInstanceOf(TimeoutException.class, failure.get()); assertThat(events, contains("onError", "onAllDataRead")); @@ -273,7 +274,7 @@ public void onError(Throwable t) HttpTester.Response response = HttpTester.parseResponse(localEndPoint.getResponse(false, 5, TimeUnit.SECONDS)); assertThat("Unexpected response status\n" + response + response.getContent(), response.getStatus(), is(HttpStatus.OK_200)); - assertThat(response.get(HttpHeader.CONTENT_TYPE), is("text/plain;charset=UTF-8")); + assertThat(response.get(HttpHeader.CONTENT_TYPE), equalToIgnoringCase("text/plain;charset=utf-8")); assertThat(response.getContent(), containsString("read=10")); assertThat(failure.get(), nullValue()); } @@ -382,7 +383,7 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws I HttpTester.Response response = HttpTester.parseResponse(localEndPoint.getResponse(false, 5, TimeUnit.SECONDS)); assertThat("Unexpected response status\n" + response + response.getContent(), response.getStatus(), is(HttpStatus.OK_200)); - assertThat(response.get(HttpHeader.CONTENT_TYPE), is("text/plain;charset=UTF-8")); + assertThat(response.get(HttpHeader.CONTENT_TYPE), equalToIgnoringCase("text/plain;charset=utf-8")); assertThat(response.getContent(), containsString("read=10")); assertInstanceOf(IOException.class, failure.get()); assertInstanceOf(TimeoutException.class, failure.get().getCause()); diff --git a/jetty-ee11/jetty-ee11-servlet/src/test/java/org/eclipse/jetty/ee11/servlet/ResponseTest.java b/jetty-ee11/jetty-ee11-servlet/src/test/java/org/eclipse/jetty/ee11/servlet/ResponseTest.java index 4823fe15ae5a..b29a5b81f8f9 100644 --- a/jetty-ee11/jetty-ee11-servlet/src/test/java/org/eclipse/jetty/ee11/servlet/ResponseTest.java +++ b/jetty-ee11/jetty-ee11-servlet/src/test/java/org/eclipse/jetty/ee11/servlet/ResponseTest.java @@ -47,6 +47,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.emptyString; +import static org.hamcrest.Matchers.equalToIgnoringCase; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.notNullValue; @@ -140,7 +141,7 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t HttpTester.Response response = HttpTester.parseResponse(responseBuffer); assertThat(response.getStatus(), is(410)); - assertThat(response.get("Content-Type"), is("text/html;charset=ISO-8859-1")); + assertThat(response.get("Content-Type"), equalToIgnoringCase("text/html;charset=iso-8859-1")); assertThat(response.getContent(), containsString("The content is gone.")); } diff --git a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/HttpInputTransientErrorTest.java b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/HttpInputTransientErrorTest.java index ff99bb3d7379..1fb31407f909 100644 --- a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/HttpInputTransientErrorTest.java +++ b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/HttpInputTransientErrorTest.java @@ -45,6 +45,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalToIgnoringCase; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; import static org.junit.jupiter.api.Assertions.assertInstanceOf; @@ -101,7 +102,7 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws I { AsyncContext asyncContext = req.startAsync(req, resp); asyncContext.setTimeout(0); - resp.setContentType("text/plain;charset=UTF-8"); + resp.setContentType("text/plain;charset=utf-8"); // Since the client sends a request with a content-length header, but sends // the content only after idle timeout expired, this ReadListener will have @@ -132,7 +133,7 @@ public void onAllDataRead() throws IOException { events.add("onAllDataRead"); resp.setStatus(HttpStatus.OK_200); - resp.setContentType("text/plain;charset=UTF-8"); + resp.setContentType("text/plain;charset=utf-8"); resp.getWriter().println("read=" + counter.get()); asyncContext.complete(); } @@ -180,7 +181,7 @@ public void onError(Throwable t) assertThat("Unexpected response status\n" + response + response.getContent(), response.getStatus(), is(HttpStatus.OK_200)); assertThat(response.get(HttpHeader.CONNECTION), nullValue()); - assertThat(response.get(HttpHeader.CONTENT_TYPE), is("text/plain;charset=UTF-8")); + assertThat(response.get(HttpHeader.CONTENT_TYPE), equalToIgnoringCase("text/plain;charset=utf-8")); assertThat(response.getContent(), containsString("read=10")); assertInstanceOf(TimeoutException.class, failure.get()); assertThat(events, contains("onError", "onAllDataRead")); @@ -200,7 +201,7 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) { AsyncContext asyncContext = req.startAsync(req, resp); asyncContext.setTimeout(0); - resp.setContentType("text/plain;charset=UTF-8"); + resp.setContentType("text/plain;charset=utf-8"); // Not calling setReadListener will make Jetty set the ServletChannelState // in state WAITING upon doPost return, so idle timeouts are ignored. @@ -272,7 +273,7 @@ public void onError(Throwable t) HttpTester.Response response = HttpTester.parseResponse(localEndPoint.getResponse(false, 5, TimeUnit.SECONDS)); assertThat("Unexpected response status\n" + response + response.getContent(), response.getStatus(), is(HttpStatus.OK_200)); - assertThat(response.get(HttpHeader.CONTENT_TYPE), is("text/plain;charset=UTF-8")); + assertThat(response.get(HttpHeader.CONTENT_TYPE), equalToIgnoringCase("text/plain;charset=utf-8")); assertThat(response.getContent(), containsString("read=10")); assertThat(failure.get(), nullValue()); } @@ -362,7 +363,7 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws I String content = IO.toString(req.getInputStream()); resp.setStatus(HttpStatus.OK_200); - resp.setContentType("text/plain;charset=UTF-8"); + resp.setContentType("text/plain;charset=utf-8"); resp.getWriter().println("read=" + content.length()); } }); @@ -381,7 +382,7 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws I HttpTester.Response response = HttpTester.parseResponse(localEndPoint.getResponse(false, 5, TimeUnit.SECONDS)); assertThat("Unexpected response status\n" + response + response.getContent(), response.getStatus(), is(HttpStatus.OK_200)); - assertThat(response.get(HttpHeader.CONTENT_TYPE), is("text/plain;charset=UTF-8")); + assertThat(response.get(HttpHeader.CONTENT_TYPE), equalToIgnoringCase("text/plain;charset=utf-8")); assertThat(response.getContent(), containsString("read=10")); assertInstanceOf(IOException.class, failure.get()); assertInstanceOf(TimeoutException.class, failure.get().getCause()); diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/HttpInputTransientErrorTest.java b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/HttpInputTransientErrorTest.java index 2d040c460bfa..1f5fceb0f4dc 100644 --- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/HttpInputTransientErrorTest.java +++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/HttpInputTransientErrorTest.java @@ -94,7 +94,7 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws I { AsyncContext asyncContext = req.startAsync(req, resp); asyncContext.setTimeout(0); - resp.setContentType("text/plain;charset=UTF-8"); + resp.setContentType("text/plain;charset=utf-8"); req.getInputStream().setReadListener(new ReadListener() { From 6c0b6f94cb3a63ea98afee5a54bd62d02600a284 Mon Sep 17 00:00:00 2001 From: gregw Date: Mon, 7 Oct 2024 08:40:45 +1100 Subject: [PATCH 06/12] Use lowercase for charsets #11741 Fix #11741 as per the WhatTFWG recommendations, use lower case for charset names. Took the opportunity for some minor optimizations: + use the already made HttpField instance in MimeTypes.Type rather than create a new one in the HttpParser.CACHE + keep the MimeType.Type associated with the pre encoded Content-Type fields --- .../org/eclipse/jetty/ee10/servlet/ResponseHeadersTest.java | 3 ++- .../org/eclipse/jetty/ee11/servlet/ResponseHeadersTest.java | 3 ++- .../org/eclipse/jetty/ee9/servlet/ResponseHeadersTest.java | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/ResponseHeadersTest.java b/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/ResponseHeadersTest.java index 939357f5dfc2..3100f32c5cab 100644 --- a/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/ResponseHeadersTest.java +++ b/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/ResponseHeadersTest.java @@ -40,6 +40,7 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.endsWith; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.equalToIgnoringCase; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; @@ -146,7 +147,7 @@ protected void doGet(HttpServletRequest req, HttpServletResponse response) throw // Now test for properly formatted HTTP Response Headers. assertThat("Response Code", response.getStatus(), is(200)); - assertThat("Response Header Content-Type", response.get("Content-Type"), is("text/plain;charset=utf-8")); + assertThat("Response Header Content-Type", response.get("Content-Type"), equalToIgnoringCase("text/plain;charset=utf-8")); String expected = StringUtil.replace(actualPathInfo, "%0A", " "); // replace OBS fold with space expected = URLDecoder.decode(expected, StandardCharsets.UTF_8); // decode the rest diff --git a/jetty-ee11/jetty-ee11-servlet/src/test/java/org/eclipse/jetty/ee11/servlet/ResponseHeadersTest.java b/jetty-ee11/jetty-ee11-servlet/src/test/java/org/eclipse/jetty/ee11/servlet/ResponseHeadersTest.java index 33b079575f7b..3b8c67f0ce32 100644 --- a/jetty-ee11/jetty-ee11-servlet/src/test/java/org/eclipse/jetty/ee11/servlet/ResponseHeadersTest.java +++ b/jetty-ee11/jetty-ee11-servlet/src/test/java/org/eclipse/jetty/ee11/servlet/ResponseHeadersTest.java @@ -41,6 +41,7 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.endsWith; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.equalToIgnoringCase; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; @@ -147,7 +148,7 @@ protected void doGet(HttpServletRequest req, HttpServletResponse response) throw // Now test for properly formatted HTTP Response Headers. assertThat("Response Code", response.getStatus(), is(200)); - assertThat("Response Header Content-Type", response.get("Content-Type"), is("text/plain;charset=utf-8")); + assertThat("Response Header Content-Type", response.get("Content-Type"), equalToIgnoringCase("text/plain;charset=utf-8")); String expected = StringUtil.replace(actualPathInfo, "%0A", " "); // replace OBS fold with space expected = URLDecoder.decode(expected, StandardCharsets.UTF_8); // decode the rest diff --git a/jetty-ee9/jetty-ee9-servlet/src/test/java/org/eclipse/jetty/ee9/servlet/ResponseHeadersTest.java b/jetty-ee9/jetty-ee9-servlet/src/test/java/org/eclipse/jetty/ee9/servlet/ResponseHeadersTest.java index f24a77fa5e5b..43f8edf09192 100644 --- a/jetty-ee9/jetty-ee9-servlet/src/test/java/org/eclipse/jetty/ee9/servlet/ResponseHeadersTest.java +++ b/jetty-ee9/jetty-ee9-servlet/src/test/java/org/eclipse/jetty/ee9/servlet/ResponseHeadersTest.java @@ -44,6 +44,7 @@ import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalToIgnoringCase; import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -242,7 +243,7 @@ public void testMultilineResponseHeaderValue() throws Exception // Now test for properly formatted HTTP Response Headers. assertThat("Response Code", response.getStatus(), is(200)); - assertThat("Response Header Content-Type", response.get("Content-Type"), is("text/plain;charset=UTF-8")); + assertThat("Response Header Content-Type", response.get("Content-Type"), equalToIgnoringCase("text/plain;charset=utf-8")); String expected = StringUtil.replace(actualPathInfo, "%0a", " "); // replace OBS fold with space expected = StringUtil.replace(expected, "%0A", " "); // replace OBS fold with space From c87adb64dbb68212217e06e94154da2b998dbf97 Mon Sep 17 00:00:00 2001 From: gregw Date: Thu, 10 Oct 2024 08:35:40 +1100 Subject: [PATCH 07/12] javadoc --- .../src/main/java/org/eclipse/jetty/http/MimeTypes.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/MimeTypes.java b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/MimeTypes.java index 4b299e2c0ee4..82c7c7035946 100644 --- a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/MimeTypes.java +++ b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/MimeTypes.java @@ -920,6 +920,10 @@ else if (' ' != b) return builder.toString(); } + /** + * A {@link PreEncodedHttpField} for `Content-Type` that can hold a {@link MimeTypes.Type} field + * for later recovery. + */ public static class ContentTypeField extends PreEncodedHttpField { private final Type _type; From 1172d595b181f654b2be1d2fd56971f371cb7fd4 Mon Sep 17 00:00:00 2001 From: gregw Date: Tue, 15 Oct 2024 08:53:19 +1100 Subject: [PATCH 08/12] updates from review --- .../migration/ServletToHandlerDocs.java | 16 ++++-- .../org/eclipse/jetty/http/HttpParser.java | 5 +- .../org/eclipse/jetty/http/MimeTypes.java | 49 ++++++++++++++++++- .../org/eclipse/jetty/server/FormFields.java | 35 ++----------- .../org/eclipse/jetty/server/Request.java | 15 ------ .../jetty/ee10/servlet/ServletApiRequest.java | 4 +- .../jetty/ee11/servlet/ServletApiRequest.java | 4 +- 7 files changed, 71 insertions(+), 57 deletions(-) diff --git a/documentation/jetty/modules/code/examples/src/main/java/org/eclipse/jetty/docs/programming/migration/ServletToHandlerDocs.java b/documentation/jetty/modules/code/examples/src/main/java/org/eclipse/jetty/docs/programming/migration/ServletToHandlerDocs.java index d6a0538641fc..151e4049abe6 100644 --- a/documentation/jetty/modules/code/examples/src/main/java/org/eclipse/jetty/docs/programming/migration/ServletToHandlerDocs.java +++ b/documentation/jetty/modules/code/examples/src/main/java/org/eclipse/jetty/docs/programming/migration/ServletToHandlerDocs.java @@ -25,6 +25,7 @@ import java.util.function.Supplier; import org.eclipse.jetty.http.HttpCookie; +import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpStatus; @@ -120,9 +121,18 @@ public boolean handle(Request request, Response response, Callback callback) thr // Gets the request Content-Type. // Replaces: // - servletRequest.getContentType() - MimeTypes.Type mimeType = Request.getContentMimeType(request); - Charset charset = Request.getCharset(request); - String contentType = request.getHeaders().get(HttpHeader.CONTENT_TYPE); + // - servletRequest.getCharacterEncoding() + HttpField contentTypeField = request.getHeaders().getField(HttpHeader.CONTENT_TYPE); + MimeTypes.Type knownType = MimeTypes.getMimeTypeFromContentType(contentTypeField); + if (knownType != null) + { + Charset charset = knownType.getCharset(); + } + else + { + String contentType = contentTypeField.getValue(); + String charset = MimeTypes.getCharsetFromContentType(contentType); + } // Gets the request Content-Length. // Replaces: diff --git a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java index 37109020bc04..a323efcbb774 100644 --- a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java +++ b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java @@ -141,11 +141,12 @@ public class HttpParser Map map = new LinkedHashMap<>(); for (MimeTypes.Type mimetype : MimeTypes.Type.values()) { - MimeTypes.ContentTypeField contentTypeField = mimetype.getContentTypeField(); + HttpField contentTypeField = mimetype.getContentTypeField(); map.put(contentTypeField.toString(), contentTypeField); if (contentTypeField.getValue().contains(";charset=")) { - HttpField contentTypeFieldWithSpace = new MimeTypes.ContentTypeField(contentTypeField.getMimeType(), contentTypeField.getValue().replace(";charset=", "; charset=")); + HttpField contentTypeFieldWithSpace = + new MimeTypes.ContentTypeField(MimeTypes.getMimeTypeFromContentType(contentTypeField), contentTypeField.getValue().replace(";charset=", "; charset=")); map.put(contentTypeFieldWithSpace.toString(), contentTypeFieldWithSpace); } } diff --git a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/MimeTypes.java b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/MimeTypes.java index 82c7c7035946..119931fa2572 100644 --- a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/MimeTypes.java +++ b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/MimeTypes.java @@ -199,7 +199,7 @@ public boolean isCharsetAssumed() return _assumedCharset; } - public ContentTypeField getContentTypeField() + public HttpField getContentTypeField() { return _field; } @@ -681,6 +681,46 @@ private static String normalizeMimeType(String type) return StringUtil.asciiToLowerCase(type); } + public static MimeTypes.Type getMimeTypeFromContentType(HttpField field) + { + if (field == null) + return null; + + assert field.getHeader() == HttpHeader.CONTENT_TYPE; + + if (field instanceof MimeTypes.ContentTypeField contentTypeField) + return contentTypeField.getMimeType(); + + return MimeTypes.CACHE.get(field.getValue()); + } + + /** + * Efficiently extract the charset value from a {@code Content-Type} {@link HttpField}. + * @param field A {@code Content-Type} field. + * @return The {@link Charset} + */ + public static Charset getCharsetFromContentType(HttpField field) + { + if (field == null) + return null; + + assert field.getHeader() == HttpHeader.CONTENT_TYPE; + + if (field instanceof ContentTypeField contentTypeField) + return contentTypeField._type.getCharset(); + + String charset = getCharsetFromContentType(field.getValue()); + if (charset == null) + return null; + + return Charset.forName(charset); + } + + /** + * Efficiently extract the charset value from a {@code Content-Type} string + * @param value A content-type value (e.g. {@code text/plain; charset=utf8}). + * @return The charset value (e.g. {@code utf-8}). + */ public static String getCharsetFromContentType(String value) { if (value == null) @@ -794,6 +834,11 @@ else if (' ' != b) return null; } + /** + * Efficiently extract the base mime-type from a content-type value + * @param value A content-type value (e.g. {@code text/plain; charset=utf8}). + * @return The base mime-type value (e.g. {@code text/plain}). + */ public static String getContentTypeWithoutCharset(String value) { int end = value.length(); @@ -924,7 +969,7 @@ else if (' ' != b) * A {@link PreEncodedHttpField} for `Content-Type` that can hold a {@link MimeTypes.Type} field * for later recovery. */ - public static class ContentTypeField extends PreEncodedHttpField + static class ContentTypeField extends PreEncodedHttpField { private final Type _type; diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/FormFields.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/FormFields.java index d75ec94980dd..e8c22f7daec7 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/FormFields.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/FormFields.java @@ -28,7 +28,6 @@ import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.CharsetStringBuilder; import org.eclipse.jetty.util.Fields; -import org.eclipse.jetty.util.StringUtil; import static org.eclipse.jetty.util.UrlEncoded.decodeHexByte; @@ -56,37 +55,11 @@ public static Charset getFormEncodedCharset(Request request) if (contentTypeField == null) return null; - if (contentTypeField instanceof MimeTypes.ContentTypeField contentMimeTypeField) - { - MimeTypes.Type type = contentMimeTypeField.getMimeType(); - if (type != null && type.getCharset() != null) - return type.getCharset(); - } - - String contentType = contentTypeField.getValue(); - if (request.getLength() == 0 || StringUtil.isBlank(contentType)) - return null; - - String contentTypeWithoutCharset = MimeTypes.getContentTypeWithoutCharset(contentType); - MimeTypes.Type type = MimeTypes.CACHE.get(contentTypeWithoutCharset); - if (type != null) - { - if (type != MimeTypes.Type.FORM_ENCODED) - return null; - } - else - { - // Could be a non-cached Content-Type with other parameters such as "application/x-www-form-urlencoded; p=v". - // Verify that it is actually application/x-www-form-urlencoded. - int semi = contentTypeWithoutCharset.indexOf(';'); - if (semi > 0) - contentTypeWithoutCharset = contentTypeWithoutCharset.substring(0, semi); - if (!MimeTypes.Type.FORM_ENCODED.is(contentTypeWithoutCharset.trim())) - return null; - } + Charset charset = MimeTypes.getCharsetFromContentType(contentTypeField); + if (charset != null) + return charset; - String cs = MimeTypes.getCharsetFromContentType(contentType); - return StringUtil.isEmpty(cs) ? StandardCharsets.UTF_8 : Charset.forName(cs); + return StandardCharsets.UTF_8; } /** diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java index 5583af0d4944..757df3c518c5 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java @@ -536,21 +536,6 @@ static InputStream asInputStream(Request request) return Content.Source.asInputStream(request); } - /** - * Get a known {@link MimeTypes.Type} from the request {@link HttpHeader#CONTENT_TYPE}, if any. - * @param request The request. - * @return A {@link MimeTypes} or {@code null} if the {@code Content-Type} is not set or not known. - */ - static MimeTypes.Type getContentMimeType(Request request) - { - HttpField contentType = request.getHeaders().getField(HttpHeader.CONTENT_TYPE); - if (contentType instanceof MimeTypes.ContentTypeField contentTypeField) - return contentTypeField.getMimeType(); - if (contentType == null) - return null; - return MimeTypes.CACHE.get(contentType.getValue()); - } - /** * Get a {@link Charset} from the request {@link HttpHeader#CONTENT_TYPE}, if any. * @param request The request. diff --git a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletApiRequest.java b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletApiRequest.java index 41326c4a0d53..e120c88b722e 100644 --- a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletApiRequest.java +++ b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletApiRequest.java @@ -1188,8 +1188,8 @@ public String getContentType() if (contentType != null) { _contentType = contentType.getValue(); - if (_charset == null && contentType instanceof MimeTypes.ContentTypeField contentTypeField) - _charset = contentTypeField.getMimeType().getCharset(); + if (_charset == null) + _charset = MimeTypes.getCharsetFromContentType(contentType); } } return _contentType; diff --git a/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/ServletApiRequest.java b/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/ServletApiRequest.java index 4aaa4ced8cb3..cd5f487b2533 100644 --- a/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/ServletApiRequest.java +++ b/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/ServletApiRequest.java @@ -1197,8 +1197,8 @@ public String getContentType() if (contentType != null) { _contentType = contentType.getValue(); - if (_charset == null && contentType instanceof MimeTypes.ContentTypeField contentTypeField) - _charset = contentTypeField.getMimeType().getCharset(); + if (_charset == null) + _charset = MimeTypes.getCharsetFromContentType(contentType); } } return _contentType; From 7913cbbb103db7e2c4086c0e501f80c0a90ddf2b Mon Sep 17 00:00:00 2001 From: gregw Date: Tue, 15 Oct 2024 09:29:36 +1100 Subject: [PATCH 09/12] updates from review --- .../migration/ServletToHandlerDocs.java | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/documentation/jetty/modules/code/examples/src/main/java/org/eclipse/jetty/docs/programming/migration/ServletToHandlerDocs.java b/documentation/jetty/modules/code/examples/src/main/java/org/eclipse/jetty/docs/programming/migration/ServletToHandlerDocs.java index 151e4049abe6..186e87788bdd 100644 --- a/documentation/jetty/modules/code/examples/src/main/java/org/eclipse/jetty/docs/programming/migration/ServletToHandlerDocs.java +++ b/documentation/jetty/modules/code/examples/src/main/java/org/eclipse/jetty/docs/programming/migration/ServletToHandlerDocs.java @@ -121,18 +121,16 @@ public boolean handle(Request request, Response response, Callback callback) thr // Gets the request Content-Type. // Replaces: // - servletRequest.getContentType() - // - servletRequest.getCharacterEncoding() HttpField contentTypeField = request.getHeaders().getField(HttpHeader.CONTENT_TYPE); + String contentType = contentTypeField.getValue(); MimeTypes.Type knownType = MimeTypes.getMimeTypeFromContentType(contentTypeField); - if (knownType != null) - { - Charset charset = knownType.getCharset(); - } - else - { - String contentType = contentTypeField.getValue(); - String charset = MimeTypes.getCharsetFromContentType(contentType); - } + + // Gets the request Character Encoding. + // Replaces: + // - servletRequest.getCharacterEncoding() + Charset charset = knownType == null + ? MimeTypes.getCharsetFromContentType(contentTypeField) + : knownType.getCharset(); // Gets the request Content-Length. // Replaces: From c454e3373b46b3b617334488ed9a45e0bdb4d100 Mon Sep 17 00:00:00 2001 From: gregw Date: Tue, 15 Oct 2024 12:07:28 +1100 Subject: [PATCH 10/12] Fix tests --- .../security/authentication/FormAuthenticator.java | 2 +- .../java/org/eclipse/jetty/server/FormFields.java | 11 +++++++++++ .../jetty/ee10/servlet/security/ConstraintTest.java | 1 + 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/jetty-core/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/FormAuthenticator.java b/jetty-core/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/FormAuthenticator.java index 79e2d5828917..906dd8bf1aa2 100644 --- a/jetty-core/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/FormAuthenticator.java +++ b/jetty-core/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/FormAuthenticator.java @@ -89,7 +89,7 @@ public FormAuthenticator(String login, String error, boolean dispatch) * If true, uris that cause a redirect to a login page will always * be remembered. If false, only the first uri that leads to a login * page redirect is remembered. - * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=379909 + * See bug 379909 * * @param alwaysSave true to always save the uri */ diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/FormFields.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/FormFields.java index e8c22f7daec7..84b892f5c6f2 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/FormFields.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/FormFields.java @@ -55,6 +55,17 @@ public static Charset getFormEncodedCharset(Request request) if (contentTypeField == null) return null; + MimeTypes.Type type = MimeTypes.getMimeTypeFromContentType(contentTypeField); + if (type != null) + { + if (type.getBaseType() != MimeTypes.Type.FORM_ENCODED) + return null; + return type.getCharset(); + } + + if (!MimeTypes.Type.FORM_ENCODED.is(MimeTypes.getContentTypeWithoutCharset(contentTypeField.getValue()))) + return null; + Charset charset = MimeTypes.getCharsetFromContentType(contentTypeField); if (charset != null) return charset; diff --git a/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/security/ConstraintTest.java b/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/security/ConstraintTest.java index dba74873b8e0..838de91a9c03 100644 --- a/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/security/ConstraintTest.java +++ b/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/security/ConstraintTest.java @@ -911,6 +911,7 @@ public void testBasic(Scenario scenario) throws Exception _server.start(); String rawResponse = _connector.getResponse(scenario.rawRequest); HttpTester.Response response = HttpTester.parseResponse(HttpTester.from(rawResponse), scenario.rawRequest.startsWith("HEAD ")); + assertNotNull(response); assertThat(response.toString(), response.getStatus(), is(scenario.expectedStatus)); if (scenario.extraAsserts != null) scenario.extraAsserts.accept(response); From c4297400669bc6dbc219e5d70fa92563bcf078ff Mon Sep 17 00:00:00 2001 From: gregw Date: Tue, 15 Oct 2024 17:49:08 +1100 Subject: [PATCH 11/12] Fix tests --- .../src/main/java/org/eclipse/jetty/server/FormFields.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/FormFields.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/FormFields.java index 84b892f5c6f2..0c7a94487ed8 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/FormFields.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/FormFields.java @@ -60,17 +60,16 @@ public static Charset getFormEncodedCharset(Request request) { if (type.getBaseType() != MimeTypes.Type.FORM_ENCODED) return null; - return type.getCharset(); + + return type.getCharset() == null ? StandardCharsets.UTF_8 : type.getCharset(); } if (!MimeTypes.Type.FORM_ENCODED.is(MimeTypes.getContentTypeWithoutCharset(contentTypeField.getValue()))) return null; Charset charset = MimeTypes.getCharsetFromContentType(contentTypeField); - if (charset != null) - return charset; - return StandardCharsets.UTF_8; + return charset == null ? StandardCharsets.UTF_8 : charset; } /** From 99fbfc389fd69cf53ea169640692ab08633c6732 Mon Sep 17 00:00:00 2001 From: gregw Date: Tue, 15 Oct 2024 21:46:22 +1100 Subject: [PATCH 12/12] Fix tests --- .../src/main/java/org/eclipse/jetty/server/FormFields.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/FormFields.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/FormFields.java index 0c7a94487ed8..28a09292de6b 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/FormFields.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/FormFields.java @@ -64,7 +64,11 @@ public static Charset getFormEncodedCharset(Request request) return type.getCharset() == null ? StandardCharsets.UTF_8 : type.getCharset(); } - if (!MimeTypes.Type.FORM_ENCODED.is(MimeTypes.getContentTypeWithoutCharset(contentTypeField.getValue()))) + String contentType = contentTypeField.getValue(); + int semicolon = contentType.indexOf(';'); + if (semicolon >= 0) + contentType = contentType.substring(0, semicolon).trim(); + if (!MimeTypes.Type.FORM_ENCODED.is(contentType)) return null; Charset charset = MimeTypes.getCharsetFromContentType(contentTypeField);