Browse Source

Improve charset determination (#491)

pull/496/head
Olga Maciaszek-Sharma 4 years ago committed by GitHub
parent
commit
cf20710cad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 40
      docs/src/main/asciidoc/_configprops.adoc
  2. 6
      docs/src/main/asciidoc/spring-cloud-openfeign.adoc
  3. 6
      spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java
  4. 19
      spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientsConfiguration.java
  5. 44
      spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/FeignEncoderProperties.java
  6. 142
      spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringEncoder.java
  7. 4
      spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/circuitbreaker/CircuitBreakerTests.java
  8. 9
      spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/proto/ProtobufNotInClasspathTest.java
  9. 28
      spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/proto/ProtobufSpringEncoderTest.java
  10. 38
      spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/SpringEncoderTests.java
  11. BIN
      spring-cloud-openfeign-core/src/test/resources/dummy.pdf

40
docs/src/main/asciidoc/_configprops.adoc

@ -1,26 +1,28 @@ @@ -1,26 +1,28 @@
|===
|Name | Default | Description
|feign.circuitbreaker.enabled | false | If true, an OpenFeign client will be wrapped with a Spring Cloud CircuitBreaker circuit breaker.
|feign.autoconfiguration.jackson.enabled | `false` | If true, PageJacksonModule and SortJacksonModule bean will be provided for Jackson page decoding.
|feign.circuitbreaker.enabled | `false` | If true, an OpenFeign client will be wrapped with a Spring Cloud CircuitBreaker circuit breaker.
|feign.client.config | |
|feign.client.decode-slash | true | Feign clients do not encode slash `/` characters by default. To change this behavior, set the `decodeSlash` to `false`.
|feign.client.default-config | default |
|feign.client.default-to-properties | true |
|feign.compression.request.enabled | false | Enables the request sent by Feign to be compressed.
|feign.compression.request.mime-types | [text/xml, application/xml, application/json] | The list of supported mime types.
|feign.compression.request.min-request-size | 2048 | The minimum threshold content size.
|feign.compression.response.enabled | false | Enables the response from Feign to be compressed.
|feign.compression.response.useGzipDecoder | false | Enables the default gzip decoder to be used.
|feign.httpclient.connection-timeout | 2000 |
|feign.httpclient.connection-timer-repeat | 3000 |
|feign.httpclient.disable-ssl-validation | false |
|feign.httpclient.enabled | true | Enables the use of the Apache HTTP Client by Feign.
|feign.httpclient.follow-redirects | true |
|feign.httpclient.max-connections | 200 |
|feign.httpclient.max-connections-per-route | 50 |
|feign.httpclient.time-to-live | 900 |
|feign.client.decode-slash | `true` | Feign clients do not encode slash `/` characters by default. To change this behavior, set the `decodeSlash` to `false`.
|feign.client.default-config | `default` |
|feign.client.default-to-properties | `true` |
|feign.compression.request.enabled | `false` | Enables the request sent by Feign to be compressed.
|feign.compression.request.mime-types | `[text/xml, application/xml, application/json]` | The list of supported mime types.
|feign.compression.request.min-request-size | `2048` | The minimum threshold content size.
|feign.compression.response.enabled | `false` | Enables the response from Feign to be compressed.
|feign.compression.response.useGzipDecoder | `false` | Enables the default gzip decoder to be used.
|feign.encoder.charset-from-content-type | `false` |
|feign.httpclient.connection-timeout | `2000` |
|feign.httpclient.connection-timer-repeat | `3000` |
|feign.httpclient.disable-ssl-validation | `false` |
|feign.httpclient.enabled | `true` | Enables the use of the Apache HTTP Client by Feign.
|feign.httpclient.follow-redirects | `true` |
|feign.httpclient.max-connections | `200` |
|feign.httpclient.max-connections-per-route | `50` |
|feign.httpclient.time-to-live | `900` |
|feign.httpclient.time-to-live-unit | |
|feign.hystrix.enabled | false | If true, an OpenFeign client will be wrapped with a Hystrix circuit breaker.
|feign.okhttp.enabled | false | Enables the use of the OK HTTP Client by Feign.
|feign.hystrix.enabled | `false` | If true, an OpenFeign client will be wrapped with a Hystrix circuit breaker.
|feign.okhttp.enabled | `false` | Enables the use of the OK HTTP Client by Feign.
|===

6
docs/src/main/asciidoc/spring-cloud-openfeign.adoc

@ -277,6 +277,12 @@ public FeignClientConfigurer feignClientConfigurer() { @@ -277,6 +277,12 @@ public FeignClientConfigurer feignClientConfigurer() {
TIP: By default, Feign clients do not encode slash `/` characters. You can change this behaviour, by setting the value of `feign.client.decodeSlash` to `false`.
==== `SpringEncoder` configuration
In the `SpringEncoder` that we provide, we set `null` charset for binary content types and `UTF-8` for all the other ones.
You can modify this behaviour to derive the charset from the `Content-Type` header charset instead by setting the value of `feign.encoder.charset-from-content-type` to `true`.
[[timeout-handling]]
=== Timeout Handling

6
spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2013-2020 the original author or authors.
* Copyright 2013-2021 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.
@ -55,6 +55,7 @@ import org.springframework.cloud.commons.httpclient.ApacheHttpClientFactory; @@ -55,6 +55,7 @@ import org.springframework.cloud.commons.httpclient.ApacheHttpClientFactory;
import org.springframework.cloud.commons.httpclient.OkHttpClientConnectionPoolFactory;
import org.springframework.cloud.commons.httpclient.OkHttpClientFactory;
import org.springframework.cloud.openfeign.support.DefaultGzipDecoderConfiguration;
import org.springframework.cloud.openfeign.support.FeignEncoderProperties;
import org.springframework.cloud.openfeign.support.FeignHttpClientProperties;
import org.springframework.cloud.openfeign.support.PageJacksonModule;
import org.springframework.cloud.openfeign.support.SortJacksonModule;
@ -71,11 +72,12 @@ import org.springframework.data.domain.Sort; @@ -71,11 +72,12 @@ import org.springframework.data.domain.Sort;
* @author Grzegorz Poznachowski
* @author Nikita Konev
* @author Tim Peeters
* @author Olga Maciaszek-Sharma
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Feign.class)
@EnableConfigurationProperties({ FeignClientProperties.class,
FeignHttpClientProperties.class })
FeignHttpClientProperties.class, FeignEncoderProperties.class })
@Import(DefaultGzipDecoderConfiguration.class)
public class FeignAutoConfiguration {

19
spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientsConfiguration.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2013-2020 the original author or authors.
* Copyright 2013-2021 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.
@ -45,6 +45,7 @@ import org.springframework.cloud.client.circuitbreaker.CircuitBreaker; @@ -45,6 +45,7 @@ import org.springframework.cloud.client.circuitbreaker.CircuitBreaker;
import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory;
import org.springframework.cloud.openfeign.clientconfig.FeignClientConfigurer;
import org.springframework.cloud.openfeign.support.AbstractFormWriter;
import org.springframework.cloud.openfeign.support.FeignEncoderProperties;
import org.springframework.cloud.openfeign.support.PageableSpringEncoder;
import org.springframework.cloud.openfeign.support.ResponseEntityDecoder;
import org.springframework.cloud.openfeign.support.SpringDecoder;
@ -63,6 +64,7 @@ import static feign.form.ContentType.MULTIPART; @@ -63,6 +64,7 @@ import static feign.form.ContentType.MULTIPART;
* @author Dave Syer
* @author Venil Noronha
* @author Darren Foong
* @author Olga Maciaszek-Sharma
*/
@Configuration(proxyBeanMethods = false)
public class FeignClientsConfiguration {
@ -85,6 +87,9 @@ public class FeignClientsConfiguration { @@ -85,6 +87,9 @@ public class FeignClientsConfiguration {
@Autowired(required = false)
private FeignClientProperties feignClientProperties;
@Autowired(required = false)
private FeignEncoderProperties encoderProperties;
@Bean
@ConditionalOnMissingBean
public Decoder feignDecoder() {
@ -96,7 +101,7 @@ public class FeignClientsConfiguration { @@ -96,7 +101,7 @@ public class FeignClientsConfiguration {
@ConditionalOnMissingBean
@ConditionalOnMissingClass("org.springframework.data.domain.Pageable")
public Encoder feignEncoder(ObjectProvider<AbstractFormWriter> formWriterProvider) {
return springEncoder(formWriterProvider);
return springEncoder(formWriterProvider, encoderProperties);
}
@Bean
@ -105,7 +110,7 @@ public class FeignClientsConfiguration { @@ -105,7 +110,7 @@ public class FeignClientsConfiguration {
public Encoder feignEncoderPageable(
ObjectProvider<AbstractFormWriter> formWriterProvider) {
PageableSpringEncoder encoder = new PageableSpringEncoder(
springEncoder(formWriterProvider));
springEncoder(formWriterProvider, encoderProperties));
if (springDataWebProperties != null) {
encoder.setPageParameter(
@ -162,15 +167,17 @@ public class FeignClientsConfiguration { @@ -162,15 +167,17 @@ public class FeignClientsConfiguration {
};
}
private Encoder springEncoder(ObjectProvider<AbstractFormWriter> formWriterProvider) {
private Encoder springEncoder(ObjectProvider<AbstractFormWriter> formWriterProvider,
FeignEncoderProperties encoderProperties) {
AbstractFormWriter formWriter = formWriterProvider.getIfAvailable();
if (formWriter != null) {
return new SpringEncoder(new SpringPojoFormEncoder(formWriter),
this.messageConverters);
this.messageConverters, encoderProperties);
}
else {
return new SpringEncoder(new SpringFormEncoder(), this.messageConverters);
return new SpringEncoder(new SpringFormEncoder(), this.messageConverters,
encoderProperties);
}
}

44
spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/FeignEncoderProperties.java

@ -0,0 +1,44 @@ @@ -0,0 +1,44 @@
/*
* Copyright 2013-2021 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.openfeign.support;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Properties for {@link SpringEncoder}.
*
* @author Olga Maciaszek-Sharma
*
* @since 2.2.8
*/
@ConfigurationProperties("feign.encoder")
public class FeignEncoderProperties {
/**
* Indicates whether the charset should be derived from the {@code Content-Type} header.
*/
private boolean charsetFromContentType = false;
public boolean isCharsetFromContentType() {
return charsetFromContentType;
}
public void setCharsetFromContentType(boolean charsetFromContentType) {
this.charsetFromContentType = charsetFromContentType;
}
}

142
spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringEncoder.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2013-2020 the original author or authors.
* Copyright 2013-2021 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,10 +22,10 @@ import java.io.OutputStream; @@ -22,10 +22,10 @@ import java.io.OutputStream;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collection;
import java.util.Objects;
import java.util.stream.Stream;
import feign.Request;
import feign.RequestTemplate;
import feign.codec.EncodeException;
import feign.codec.Encoder;
@ -55,7 +55,9 @@ import static org.springframework.cloud.openfeign.support.FeignUtils.getHttpHead @@ -55,7 +55,9 @@ import static org.springframework.cloud.openfeign.support.FeignUtils.getHttpHead
* @author Ahmad Mozafarnia
* @author Aaron Whiteside
* @author Darren Foong
* @author Olga Maciaszek-Sharma
*/
@SuppressWarnings("rawtypes")
public class SpringEncoder implements Encoder {
private static final Log log = LogFactory.getLog(SpringEncoder.class);
@ -64,15 +66,23 @@ public class SpringEncoder implements Encoder { @@ -64,15 +66,23 @@ public class SpringEncoder implements Encoder {
private final ObjectFactory<HttpMessageConverters> messageConverters;
private final FeignEncoderProperties encoderProperties;
public SpringEncoder(ObjectFactory<HttpMessageConverters> messageConverters) {
this.springFormEncoder = new SpringFormEncoder();
this.messageConverters = messageConverters;
this(new SpringFormEncoder(), messageConverters);
}
public SpringEncoder(SpringFormEncoder springFormEncoder,
ObjectFactory<HttpMessageConverters> messageConverters) {
this(springFormEncoder, messageConverters, new FeignEncoderProperties());
}
public SpringEncoder(SpringFormEncoder springFormEncoder,
ObjectFactory<HttpMessageConverters> messageConverters,
FeignEncoderProperties encoderProperties) {
this.springFormEncoder = springFormEncoder;
this.messageConverters = messageConverters;
this.encoderProperties = encoderProperties;
}
@Override
@ -89,7 +99,7 @@ public class SpringEncoder implements Encoder { @@ -89,7 +99,7 @@ public class SpringEncoder implements Encoder {
requestContentType = MediaType.valueOf(type);
}
if (Objects.equals(requestContentType, MediaType.MULTIPART_FORM_DATA)) {
if (isMultipartType(requestContentType)) {
this.springFormEncoder.encode(requestBody, bodyType, request);
return;
}
@ -100,56 +110,74 @@ public class SpringEncoder implements Encoder { @@ -100,56 +110,74 @@ public class SpringEncoder implements Encoder {
+ "should be specified as MediaType.MULTIPART_FORM_DATA_VALUE");
}
}
encodeWithMessageConverter(requestBody, bodyType, request,
requestContentType);
}
}
for (HttpMessageConverter messageConverter : this.messageConverters
.getObject().getConverters()) {
FeignOutputMessage outputMessage;
try {
if (messageConverter instanceof GenericHttpMessageConverter) {
outputMessage = checkAndWrite(requestBody, bodyType,
requestContentType,
(GenericHttpMessageConverter) messageConverter, request);
}
else {
outputMessage = checkAndWrite(requestBody, requestContentType,
messageConverter, request);
}
}
catch (IOException | HttpMessageConversionException ex) {
throw new EncodeException("Error converting request body", ex);
private void encodeWithMessageConverter(Object requestBody, Type bodyType,
RequestTemplate request, MediaType requestContentType) {
for (HttpMessageConverter messageConverter : this.messageConverters.getObject()
.getConverters()) {
FeignOutputMessage outputMessage;
try {
if (messageConverter instanceof GenericHttpMessageConverter) {
outputMessage = checkAndWrite(requestBody, bodyType,
requestContentType,
(GenericHttpMessageConverter) messageConverter, request);
}
if (outputMessage != null) {
// clear headers
request.headers(null);
// converters can modify headers, so update the request
// with the modified headers
request.headers(getHeaders(outputMessage.getHeaders()));
// do not use charset for binary data and protobuf
Charset charset;
if (messageConverter instanceof ByteArrayHttpMessageConverter) {
charset = null;
}
else if (messageConverter instanceof ProtobufHttpMessageConverter
&& ProtobufHttpMessageConverter.PROTOBUF.isCompatibleWith(
outputMessage.getHeaders().getContentType())) {
charset = null;
}
else {
charset = StandardCharsets.UTF_8;
}
request.body(Request.Body.encoded(
outputMessage.getOutputStream().toByteArray(), charset));
return;
else {
outputMessage = checkAndWrite(requestBody, requestContentType,
messageConverter, request);
}
}
String message = "Could not write request: no suitable HttpMessageConverter "
+ "found for request type [" + requestBody.getClass().getName() + "]";
if (requestContentType != null) {
message += " and content type [" + requestContentType + "]";
catch (IOException | HttpMessageConversionException ex) {
throw new EncodeException("Error converting request body", ex);
}
if (outputMessage != null) {
// clear headers
request.headers(null);
// converters can modify headers, so update the request
// with the modified headers
request.headers(getHeaders(outputMessage.getHeaders()));
// do not use charset for binary data and protobuf
Charset charset;
MediaType contentType = outputMessage.getHeaders().getContentType();
Charset charsetFromContentType = contentType != null
? contentType.getCharset() : null;
if (encoderProperties != null
&& encoderProperties.isCharsetFromContentType()
&& charsetFromContentType != null) {
charset = charsetFromContentType;
}
else if (shouldHaveNullCharset(messageConverter, outputMessage)) {
charset = null;
}
else {
charset = StandardCharsets.UTF_8;
}
request.body(outputMessage.getOutputStream().toByteArray(), charset);
return;
}
throw new EncodeException(message);
}
String message = "Could not write request: no suitable HttpMessageConverter "
+ "found for request type [" + requestBody.getClass().getName() + "]";
if (requestContentType != null) {
message += " and content type [" + requestContentType + "]";
}
throw new EncodeException(message);
}
private boolean shouldHaveNullCharset(HttpMessageConverter messageConverter,
FeignOutputMessage outputMessage) {
return binaryContentType(outputMessage)
|| messageConverter instanceof ByteArrayHttpMessageConverter
|| messageConverter instanceof ProtobufHttpMessageConverter
&& ProtobufHttpMessageConverter.PROTOBUF.isCompatibleWith(
outputMessage.getHeaders().getContentType());
}
@SuppressWarnings("unchecked")
@ -195,6 +223,20 @@ public class SpringEncoder implements Encoder { @@ -195,6 +223,20 @@ public class SpringEncoder implements Encoder {
}
}
private boolean isMultipartType(MediaType requestContentType) {
return Arrays.asList(MediaType.MULTIPART_FORM_DATA, MediaType.MULTIPART_MIXED,
MediaType.MULTIPART_RELATED).contains(requestContentType);
}
private boolean binaryContentType(FeignOutputMessage outputMessage) {
MediaType contentType = outputMessage.getHeaders().getContentType();
return contentType == null || Stream
.of(MediaType.APPLICATION_CBOR, MediaType.APPLICATION_OCTET_STREAM,
MediaType.APPLICATION_PDF, MediaType.IMAGE_GIF,
MediaType.IMAGE_JPEG, MediaType.IMAGE_PNG)
.anyMatch(mediaType -> mediaType.includes(contentType));
}
private final class FeignOutputMessage implements HttpOutputMessage {
private final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

4
spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/circuitbreaker/CircuitBreakerTests.java

@ -30,7 +30,6 @@ import org.springframework.beans.factory.annotation.Autowired; @@ -30,7 +30,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.cloud.client.circuitbreaker.CircuitBreaker;
import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory;
import org.springframework.cloud.client.circuitbreaker.ConfigBuilder;
@ -72,9 +71,6 @@ public class CircuitBreakerTests { @@ -72,9 +71,6 @@ public class CircuitBreakerTests {
@Autowired
TestClientWithFactory testClientWithFactory;
@LocalServerPort
private int port = 0;
@BeforeAll
public static void beforeClass() {
System.setProperty("server.port",

9
spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/proto/ProtobufNotInClasspathTest.java

@ -20,7 +20,6 @@ import feign.RequestTemplate; @@ -20,7 +20,6 @@ import feign.RequestTemplate;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.openfeign.support.SpringEncoder;
@ -41,12 +40,8 @@ public class ProtobufNotInClasspathTest { @@ -41,12 +40,8 @@ public class ProtobufNotInClasspathTest {
@Test
public void testEncodeWhenProtobufNotInClasspath() {
ObjectFactory<HttpMessageConverters> converters = new ObjectFactory<HttpMessageConverters>() {
@Override
public HttpMessageConverters getObject() throws BeansException {
return new HttpMessageConverters(new StringHttpMessageConverter());
}
};
ObjectFactory<HttpMessageConverters> converters = () -> new HttpMessageConverters(
new StringHttpMessageConverter());
RequestTemplate requestTemplate = new RequestTemplate();
requestTemplate.method(POST);
new SpringEncoder(converters).encode("a=b", String.class, requestTemplate);

28
spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/proto/ProtobufSpringEncoderTest.java

@ -40,11 +40,9 @@ import org.junit.runner.RunWith; @@ -40,11 +40,9 @@ import org.junit.runner.RunWith;
import org.mockito.ArgumentMatchers;
import org.mockito.BDDMockito;
import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.junit.MockitoJUnitRunner;
import org.mockito.stubbing.Answer;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.openfeign.support.SpringEncoder;
@ -73,7 +71,7 @@ public class ProtobufSpringEncoderTest { @@ -73,7 +71,7 @@ public class ProtobufSpringEncoderTest {
.build();
@Test
public void testProtobuf() throws IOException, URISyntaxException {
public void testProtobuf() throws IOException {
// protobuf convert to request by feign and ProtobufHttpMessageConverter
RequestTemplate requestTemplate = newRequestTemplate();
newEncoder().encode(this.request, Request.class, requestTemplate);
@ -110,12 +108,8 @@ public class ProtobufSpringEncoderTest { @@ -110,12 +108,8 @@ public class ProtobufSpringEncoderTest {
}
private SpringEncoder newEncoder() {
ObjectFactory<HttpMessageConverters> converters = new ObjectFactory<HttpMessageConverters>() {
@Override
public HttpMessageConverters getObject() throws BeansException {
return new HttpMessageConverters(new ProtobufHttpMessageConverter());
}
};
ObjectFactory<HttpMessageConverters> converters = () -> new HttpMessageConverters(
new ProtobufHttpMessageConverter());
return new SpringEncoder(converters);
}
@ -126,17 +120,13 @@ public class ProtobufSpringEncoderTest { @@ -126,17 +120,13 @@ public class ProtobufSpringEncoderTest {
}
private HttpEntity toApacheHttpEntity(RequestTemplate requestTemplate)
throws IOException, URISyntaxException {
throws IOException {
final List<HttpUriRequest> request = new ArrayList<>(1);
BDDMockito.given(this.httpClient.execute(ArgumentMatchers.<HttpUriRequest>any()))
.will(new Answer<HttpResponse>() {
@Override
public HttpResponse answer(InvocationOnMock invocationOnMock)
throws Throwable {
request.add((HttpUriRequest) invocationOnMock.getArguments()[0]);
return new BasicHttpResponse(new BasicStatusLine(
new ProtocolVersion("http", 1, 1), 200, null));
}
BDDMockito.given(this.httpClient.execute(ArgumentMatchers.any()))
.will((Answer<HttpResponse>) invocationOnMock -> {
request.add((HttpUriRequest) invocationOnMock.getArguments()[0]);
return new BasicHttpResponse(new BasicStatusLine(
new ProtocolVersion("http", 1, 1), 200, null));
});
new ApacheHttpClient(this.httpClient).execute(
requestTemplate.resolve(new HashMap<>()).request(),

38
spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/SpringEncoderTests.java

@ -36,9 +36,11 @@ import org.springframework.boot.test.context.SpringBootTest; @@ -36,9 +36,11 @@ import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.cloud.openfeign.FeignContext;
import org.springframework.cloud.openfeign.encoding.HttpEncoding;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
@ -47,6 +49,7 @@ import org.springframework.http.converter.GenericHttpMessageConverter; @@ -47,6 +49,7 @@ import org.springframework.http.converter.GenericHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.http.converter.ResourceHttpMessageConverter;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@ -59,6 +62,7 @@ import static org.springframework.http.HttpHeaders.CONTENT_LENGTH; @@ -59,6 +62,7 @@ import static org.springframework.http.HttpHeaders.CONTENT_LENGTH;
import static org.springframework.http.HttpHeaders.CONTENT_TYPE;
import static org.springframework.http.MediaType.APPLICATION_OCTET_STREAM_VALUE;
import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE;
import static org.springframework.http.MediaType.TEXT_PLAIN_VALUE;
/**
* @author Spencer Gibb
@ -79,6 +83,9 @@ public class SpringEncoderTests { @@ -79,6 +83,9 @@ public class SpringEncoderTests {
@Qualifier("myHttpMessageConverter")
private HttpMessageConverter<?> myConverter;
@Autowired
private ApplicationContext applicationContext;
@Autowired
@Qualifier("myGenericHttpMessageConverter")
private GenericHttpMessageConverter<?> myGenericConverter;
@ -185,6 +192,32 @@ public class SpringEncoderTests { @@ -185,6 +192,32 @@ public class SpringEncoderTests {
.as("Body content cannot be decoded").contains("hi");
}
@Test
public void testNoCharsetForBinaryFiles() {
Encoder encoder = context.getInstance("test", Encoder.class);
assertThat(encoder).isNotNull();
RequestTemplate request = new RequestTemplate();
request.header(CONTENT_TYPE, APPLICATION_OCTET_STREAM_VALUE);
Resource resource = applicationContext.getResource("classpath:dummy.pdf");
encoder.encode(resource, Resource.class, request);
assertThat(request.requestBody().getEncoding()).isEmpty();
}
@Test
public void testUTF8CharsetForTextFiles() {
Encoder encoder = context.getInstance("test", Encoder.class);
assertThat(encoder).isNotNull();
RequestTemplate request = new RequestTemplate();
request.header(CONTENT_TYPE, TEXT_PLAIN_VALUE);
String test = "test";
encoder.encode(test, String.class, request);
assertThat(request.requestBody().getEncoding().get().name()).isEqualTo("UTF-8");
}
protected interface TestClient {
}
@ -213,6 +246,11 @@ public class SpringEncoderTests { @@ -213,6 +246,11 @@ public class SpringEncoderTests {
return new MyHttpMessageConverter();
}
@Bean
ResourceHttpMessageConverter resourceHttpMessageConverter() {
return new ResourceHttpMessageConverter();
}
@Bean
GenericHttpMessageConverter<?> myGenericHttpMessageConverter() {
return new MyGenericHttpMessageConverter();

BIN
spring-cloud-openfeign-core/src/test/resources/dummy.pdf

Binary file not shown.
Loading…
Cancel
Save