From 5861e9685bfa6556ffdd56e76df6bd83829fbba6 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Tue, 27 Mar 2018 19:54:48 -0400 Subject: [PATCH] Always specify charset for form data requests Issue: SPR-16613 --- .../http/codec/FormHttpMessageWriter.java | 24 ++++++++++++------- .../converter/FormHttpMessageConverter.java | 16 +++++++------ .../codec/FormHttpMessageWriterTests.java | 8 ++++--- .../FormHttpMessageConverterTests.java | 6 ++--- .../client/AbstractMockWebServerTestCase.java | 2 +- 5 files changed, 33 insertions(+), 23 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/http/codec/FormHttpMessageWriter.java b/spring-web/src/main/java/org/springframework/http/codec/FormHttpMessageWriter.java index fa4ed289c31..2e8a676593c 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/FormHttpMessageWriter.java +++ b/spring-web/src/main/java/org/springframework/http/codec/FormHttpMessageWriter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -61,12 +61,15 @@ public class FormHttpMessageWriter implements HttpMessageWriter MEDIA_TYPES = Collections.singletonList(MediaType.APPLICATION_FORM_URLENCODED); + private static final ResolvableType MULTIVALUE_TYPE = + ResolvableType.forClassWithGenerics(MultiValueMap.class, String.class, String.class); + private Charset defaultCharset = DEFAULT_CHARSET; @@ -117,13 +120,16 @@ public class FormHttpMessageWriter implements HttpMessageWriter hints) { - MediaType contentType = message.getHeaders().getContentType(); - if (contentType == null) { - contentType = MediaType.APPLICATION_FORM_URLENCODED; - message.getHeaders().setContentType(contentType); + mediaType = (mediaType != null ? mediaType : DEFAULT_FORM_DATA_MEDIA_TYPE); + Charset charset; + if (mediaType.getCharset() == null) { + charset = getDefaultCharset(); + mediaType = new MediaType(mediaType, charset); } - - Charset charset = getMediaTypeCharset(contentType); + else { + charset = mediaType.getCharset(); + } + message.getHeaders().setContentType(mediaType); return Mono.from(inputStream).flatMap(form -> { String value = serializeForm(form, charset); diff --git a/spring-web/src/main/java/org/springframework/http/converter/FormHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/FormHttpMessageConverter.java index a2c8964ff43..70d5e4c498e 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/FormHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/FormHttpMessageConverter.java @@ -96,6 +96,9 @@ public class FormHttpMessageConverter implements HttpMessageConverter supportedMediaTypes = new ArrayList<>(); @@ -290,15 +293,14 @@ public class FormHttpMessageConverter implements HttpMessageConverter form, @Nullable MediaType contentType, HttpOutputMessage outputMessage) throws IOException { - Charset charset; - if (contentType != null) { - outputMessage.getHeaders().setContentType(contentType); - charset = (contentType.getCharset() != null ? contentType.getCharset() : this.charset); - } - else { - outputMessage.getHeaders().setContentType(MediaType.APPLICATION_FORM_URLENCODED); + contentType = (contentType != null ? contentType : DEFAULT_FORM_DATA_MEDIA_TYPE); + Charset charset = contentType.getCharset(); + if (charset == null) { charset = this.charset; + contentType = new MediaType(contentType, charset); } + outputMessage.getHeaders().setContentType(contentType); + StringBuilder builder = new StringBuilder(); for (Iterator nameIterator = form.keySet().iterator(); nameIterator.hasNext();) { String name = nameIterator.next(); diff --git a/spring-web/src/test/java/org/springframework/http/codec/FormHttpMessageWriterTests.java b/spring-web/src/test/java/org/springframework/http/codec/FormHttpMessageWriterTests.java index 0b024adc9d8..f7a9627fa2b 100644 --- a/spring-web/src/test/java/org/springframework/http/codec/FormHttpMessageWriterTests.java +++ b/spring-web/src/test/java/org/springframework/http/codec/FormHttpMessageWriterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,7 @@ import org.junit.Test; import reactor.core.publisher.Mono; import org.springframework.core.ResolvableType; +import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.mock.http.server.reactive.test.MockServerHttpResponse; import org.springframework.util.LinkedMultiValueMap; @@ -79,8 +80,9 @@ public class FormHttpMessageWriterTests { String responseBody = response.getBodyAsString().block(); assertEquals("name+1=value+1&name+2=value+2%2B1&name+2=value+2%2B2&name+3", responseBody); - assertEquals(MediaType.APPLICATION_FORM_URLENCODED, response.getHeaders().getContentType()); - assertEquals(responseBody.getBytes().length, response.getHeaders().getContentLength()); + HttpHeaders headers = response.getHeaders(); + assertEquals("application/x-www-form-urlencoded;charset=UTF-8", headers.getContentType().toString()); + assertEquals(responseBody.getBytes().length, headers.getContentLength()); } } diff --git a/spring-web/src/test/java/org/springframework/http/converter/FormHttpMessageConverterTests.java b/spring-web/src/test/java/org/springframework/http/converter/FormHttpMessageConverterTests.java index 19eb908d0ca..5142eac379e 100644 --- a/spring-web/src/test/java/org/springframework/http/converter/FormHttpMessageConverterTests.java +++ b/spring-web/src/test/java/org/springframework/http/converter/FormHttpMessageConverterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -112,8 +112,8 @@ public class FormHttpMessageConverterTests { assertEquals("Invalid result", "name+1=value+1&name+2=value+2%2B1&name+2=value+2%2B2&name+3", outputMessage.getBodyAsString(StandardCharsets.UTF_8)); - assertEquals("Invalid content-type", new MediaType("application", "x-www-form-urlencoded"), - outputMessage.getHeaders().getContentType()); + assertEquals("Invalid content-type", "application/x-www-form-urlencoded;charset=UTF-8", + outputMessage.getHeaders().getContentType().toString()); assertEquals("Invalid content-length", outputMessage.getBodyAsBytes().length, outputMessage.getHeaders().getContentLength()); } diff --git a/spring-web/src/test/java/org/springframework/web/client/AbstractMockWebServerTestCase.java b/spring-web/src/test/java/org/springframework/web/client/AbstractMockWebServerTestCase.java index a233ae186b1..f756eaf2e3d 100644 --- a/spring-web/src/test/java/org/springframework/web/client/AbstractMockWebServerTestCase.java +++ b/spring-web/src/test/java/org/springframework/web/client/AbstractMockWebServerTestCase.java @@ -165,7 +165,7 @@ public class AbstractMockWebServerTestCase { } private MockResponse formRequest(RecordedRequest request) { - assertEquals("application/x-www-form-urlencoded", request.getHeader("Content-Type")); + assertEquals("application/x-www-form-urlencoded;charset=UTF-8", request.getHeader("Content-Type")); String body = request.getBody().readUtf8(); assertThat(body, Matchers.containsString("name+1=value+1")); assertThat(body, Matchers.containsString("name+2=value+2%2B1"));