From 6dfc7a815bcc5418c0bb35208598761e2d0d2fbc Mon Sep 17 00:00:00 2001 From: Hung Tran Date: Thu, 5 Jan 2017 15:36:09 -0800 Subject: [PATCH] Disables httpclient auto decompression of encoded data Applies only to SimpleHostRoutingFilter. fixes gh-1480 --- .../route/SimpleHostRoutingFilter.java | 1 + .../route/SimpleHostRoutingFilterTests.java | 93 +++++++++++++++++++ 2 files changed, 94 insertions(+) diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/route/SimpleHostRoutingFilter.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/route/SimpleHostRoutingFilter.java index 72f6dfe5..0c1aa821 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/route/SimpleHostRoutingFilter.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/route/SimpleHostRoutingFilter.java @@ -245,6 +245,7 @@ public class SimpleHostRoutingFilter extends ZuulFilter { httpClientBuilder.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE); } return httpClientBuilder.setConnectionManager(newConnectionManager()) + .disableContentCompression() .useSystemProperties().setDefaultRequestConfig(requestConfig) .setRetryHandler(new DefaultHttpRequestRetryHandler(0, false)) .setRedirectStrategy(new RedirectStrategy() { diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/SimpleHostRoutingFilterTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/SimpleHostRoutingFilterTests.java index 183c0132..18a4a41a 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/SimpleHostRoutingFilterTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/SimpleHostRoutingFilterTests.java @@ -17,36 +17,68 @@ package org.springframework.cloud.netflix.zuul.filters.route; import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.Arrays; +import java.util.zip.GZIPOutputStream; + +import javax.servlet.http.HttpServletResponse; import org.apache.http.HttpEntityEnclosingRequest; +import org.apache.http.HttpHost; import org.apache.http.HttpRequest; +import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.entity.InputStreamEntity; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.junit.After; import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.context.embedded.LocalServerPort; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.test.context.SpringBootTest; import org.springframework.cloud.netflix.zuul.filters.ProxyRequestHelper; import org.springframework.cloud.netflix.zuul.filters.ZuulProperties; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit4.SpringRunner; import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.StreamUtils; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; import static org.springframework.boot.test.util.EnvironmentTestUtils.addEnvironment; +import static org.springframework.util.StreamUtils.copyToByteArray; +import static org.springframework.util.StreamUtils.copyToString; /** * @author Andreas Kluth * @author Spencer Gibb */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = SampleApplication.class, + webEnvironment = RANDOM_PORT, + properties = "server.contextPath: /app") +@DirtiesContext public class SimpleHostRoutingFilterTests { private AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + @LocalServerPort + private int port; + @After public void clear() { if (this.context != null) { @@ -99,6 +131,32 @@ public class SimpleHostRoutingFilterTests { assertTrue(httpEntityEnclosingRequest.getEntity() != null); } + @Test + public void httpClientDoesNotDecompressEncodedData() throws Exception { + setupContext(); + InputStreamEntity inputStreamEntity = new InputStreamEntity(new ByteArrayInputStream(new byte[]{1})); + HttpRequest httpRequest = getFilter().buildHttpRequest("GET", "/app/compressed/get/1", inputStreamEntity, + new LinkedMultiValueMap(), new LinkedMultiValueMap(), new MockHttpServletRequest()); + + CloseableHttpResponse response = getFilter().newClient().execute(new HttpHost("localhost", this.port), httpRequest); + assertEquals(200, response.getStatusLine().getStatusCode()); + byte[] responseBytes = copyToByteArray(response.getEntity().getContent()); + assertTrue(Arrays.equals(GZIPCompression.compress("Get 1"), responseBytes)); + } + + @Test + public void httpClientPreservesUnencodedData() throws Exception { + setupContext(); + InputStreamEntity inputStreamEntity = new InputStreamEntity(new ByteArrayInputStream(new byte[]{1})); + HttpRequest httpRequest = getFilter().buildHttpRequest("GET", "/app/get/1", inputStreamEntity, + new LinkedMultiValueMap(), new LinkedMultiValueMap(), new MockHttpServletRequest()); + + CloseableHttpResponse response = getFilter().newClient().execute(new HttpHost("localhost", this.port), httpRequest); + assertEquals(200, response.getStatusLine().getStatusCode()); + String responseString = copyToString(response.getEntity().getContent(), Charset.forName("UTF-8")); + assertTrue("Get 1".equals(responseString)); + } + private void setupContext() { this.context.register(PropertyPlaceholderAutoConfiguration.class, TestConfiguration.class); @@ -118,3 +176,38 @@ public class SimpleHostRoutingFilterTests { } } } + +@Configuration +@EnableAutoConfiguration +@RestController +class SampleApplication { + + public static void main(String[] args) { + SpringApplication.run(SampleApplication.class, args); + } + + @RequestMapping(value = "/compressed/get/{id}", method = RequestMethod.GET) + public byte[] getCompressed(@PathVariable String id, HttpServletResponse response) throws IOException { + response.setHeader("content-encoding", "gzip"); + return GZIPCompression.compress("Get " + id); + } + + @RequestMapping(value = "/get/{id}", method = RequestMethod.GET) + public String getString(@PathVariable String id, HttpServletResponse response) throws IOException { + return "Get " + id; + } +} + +class GZIPCompression { + + public static byte[] compress(final String str) throws IOException { + if ((str == null) || (str.length() == 0)) { + return null; + } + ByteArrayOutputStream obj = new ByteArrayOutputStream(); + GZIPOutputStream gzip = new GZIPOutputStream(obj); + gzip.write(str.getBytes("UTF-8")); + gzip.close(); + return obj.toByteArray(); + } +}