diff --git a/spring-web/src/main/java/org/springframework/http/converter/StringHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/StringHttpMessageConverter.java index 6e4aed4717a3..38a002ea0377 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/StringHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/StringHttpMessageConverter.java @@ -102,6 +102,17 @@ protected String readInternal(Class clazz, HttpInputMessage in @Override protected Long getContentLength(String str, @Nullable MediaType contentType) { Charset charset = getContentTypeCharset(contentType); + if (charset.equals(StandardCharsets.UTF_8) || charset.equals(StandardCharsets.US_ASCII) || charset.equals(StandardCharsets.ISO_8859_1)) { + int utf16Length = str.length(); + int i = 0; + + while (i < utf16Length && str.charAt(i) < 0x80) { + i++; + } + if (i == utf16Length) { + return (long) utf16Length; + } + } return (long) str.getBytes(charset).length; } diff --git a/spring-web/src/test/java/org/springframework/http/converter/StringHttpMessageConverterTests.java b/spring-web/src/test/java/org/springframework/http/converter/StringHttpMessageConverterTests.java index 909f4fcc8f8f..5433af08cb80 100644 --- a/spring-web/src/test/java/org/springframework/http/converter/StringHttpMessageConverterTests.java +++ b/spring-web/src/test/java/org/springframework/http/converter/StringHttpMessageConverterTests.java @@ -17,10 +17,13 @@ package org.springframework.http.converter; import java.io.IOException; +import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.web.testfixture.http.MockHttpInputMessage; @@ -185,5 +188,44 @@ void repeatableWrites() throws IOException { assertThat(outputMessage2.getBodyAsString()).isEqualTo(body); } + @ParameterizedTest + @ValueSource(strings = {"US-ASCII", "ISO_8859_1", "UTF-8"}) + public void testContentLength(String charsetName) { + Charset charset = Charset.forName(charsetName); + MediaType mediaType = new MediaType(MediaType.TEXT_PLAIN, charset); + assertThat(this.converter.getContentLength("", mediaType)).isEqualTo(0L); + assertThat(this.converter.getContentLength("Hello world", mediaType)).isEqualTo(11L); + + String nonAsciiInput = "Résumé"; + assertThat(this.converter.getContentLength(nonAsciiInput, mediaType)).isEqualTo( + nonAsciiInput.getBytes(charset).length); + + nonAsciiInput = "H\u00e9llo W\u00f6rld"; + assertThat(this.converter.getContentLength(nonAsciiInput, mediaType)).isEqualTo( + nonAsciiInput.getBytes(charset).length); + + nonAsciiInput = "\ud83d\udca9"; + assertThat(this.converter.getContentLength(nonAsciiInput, mediaType)).isEqualTo( + nonAsciiInput.getBytes(charset).length); + } + @Test + public void testContentLengthNonAsciiSuperset() { + Charset charset = StandardCharsets.UTF_16; + MediaType mediaType = new MediaType(MediaType.TEXT_PLAIN, charset); + assertThat(this.converter.getContentLength("", mediaType)).isEqualTo(0L); + assertThat(this.converter.getContentLength("Hello world", mediaType)).isEqualTo(24L); + + String nonAsciiInput = "Résumé"; + assertThat(this.converter.getContentLength(nonAsciiInput, mediaType)).isEqualTo( + nonAsciiInput.getBytes(charset).length); + + nonAsciiInput = "H\u00e9llo W\u00f6rld"; + assertThat(this.converter.getContentLength(nonAsciiInput, mediaType)).isEqualTo( + nonAsciiInput.getBytes(charset).length); + + nonAsciiInput = "\ud83d\udca9"; + assertThat(this.converter.getContentLength(nonAsciiInput, mediaType)).isEqualTo( + nonAsciiInput.getBytes(charset).length); + } }