Commit 4dc016f3 by Stéphane Lagraulet

Merge branch 'master' into zuul_hystrix_threadpool

Conflicts: spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/route/support/AbstractRibbonCommand.java
parents 67838807 d90e9921
general:
branches:
ignore:
- gh-pages # list of branches to ignore
machine:
java:
version: oraclejdk8
environment:
_JAVA_OPTIONS: "-Xms1024m -Xmx2048m"
dependencies:
override:
- ./mvnw -s .settings.xml -U --fail-never dependency:go-offline || true
test:
override:
- ./mvnw -s .settings.xml clean install -U -P sonar -nsu --batch-mode -Dmaven.test.redirectTestOutputToFile=true -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn
post:
- find . -type f -regex ".*/spring-cloud-*.*/target/*.*" | cpio -pdm $CIRCLE_ARTIFACTS
- mkdir -p $CIRCLE_TEST_REPORTS/junit/
- find . -type f -regex ".*/target/.*-reports/.*" -exec cp {} $CIRCLE_TEST_REPORTS/junit/ \;
- bash <(curl -s https://codecov.io/bash)
notify:
webhooks:
# A list of hook hashes, containing the url field
# gitter hook
- url: https://webhooks.gitter.im/e/22e6bb4eb945dd61ba54
...@@ -26,6 +26,8 @@ import java.util.Map.Entry; ...@@ -26,6 +26,8 @@ import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import javax.servlet.ServletInputStream; import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletRequestWrapper;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.Part; import javax.servlet.http.Part;
...@@ -57,13 +59,19 @@ import com.netflix.zuul.http.ServletInputStreamWrapper; ...@@ -57,13 +59,19 @@ import com.netflix.zuul.http.ServletInputStreamWrapper;
public class FormBodyWrapperFilter extends ZuulFilter { public class FormBodyWrapperFilter extends ZuulFilter {
private Field requestField; private Field requestField;
private Field servletRequestField;
public FormBodyWrapperFilter() { public FormBodyWrapperFilter() {
this.requestField = ReflectionUtils.findField(HttpServletRequestWrapper.class, this.requestField = ReflectionUtils.findField(HttpServletRequestWrapper.class,
"req", HttpServletRequest.class); "req", HttpServletRequest.class);
this.servletRequestField = ReflectionUtils.findField(ServletRequestWrapper.class,
"request", ServletRequest.class);
Assert.notNull(this.requestField, Assert.notNull(this.requestField,
"HttpServletRequestWrapper.req field not found"); "HttpServletRequestWrapper.req field not found");
Assert.notNull(this.servletRequestField,
"ServletRequestWrapper.request field not found");
this.requestField.setAccessible(true); this.requestField.setAccessible(true);
this.servletRequestField.setAccessible(true);
} }
@Override @Override
...@@ -113,6 +121,9 @@ public class FormBodyWrapperFilter extends ZuulFilter { ...@@ -113,6 +121,9 @@ public class FormBodyWrapperFilter extends ZuulFilter {
.getField(this.requestField, request); .getField(this.requestField, request);
wrapper = new FormBodyRequestWrapper(wrapped); wrapper = new FormBodyRequestWrapper(wrapped);
ReflectionUtils.setField(this.requestField, request, wrapper); ReflectionUtils.setField(this.requestField, request, wrapper);
if(request instanceof ServletRequestWrapper) {
ReflectionUtils.setField(this.servletRequestField, request, wrapper);
}
} }
else { else {
wrapper = new FormBodyRequestWrapper(request); wrapper = new FormBodyRequestWrapper(request);
...@@ -161,6 +172,11 @@ public class FormBodyWrapperFilter extends ZuulFilter { ...@@ -161,6 +172,11 @@ public class FormBodyWrapperFilter extends ZuulFilter {
} }
@Override @Override
public long getContentLengthLong() {
return getContentLength();
}
@Override
public ServletInputStream getInputStream() throws IOException { public ServletInputStream getInputStream() throws IOException {
if (this.contentData == null) { if (this.contentData == null) {
buildContentData(); buildContentData();
......
...@@ -24,6 +24,8 @@ import static org.junit.Assert.assertNull; ...@@ -24,6 +24,8 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.UUID; import java.util.UUID;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
...@@ -50,6 +52,7 @@ import org.springframework.cloud.netflix.zuul.filters.route.RestClientRibbonComm ...@@ -50,6 +52,7 @@ import org.springframework.cloud.netflix.zuul.filters.route.RestClientRibbonComm
import org.springframework.cloud.netflix.zuul.filters.route.RestClientRibbonCommandFactory; import org.springframework.cloud.netflix.zuul.filters.route.RestClientRibbonCommandFactory;
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandContext; import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandContext;
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandFactory; import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandFactory;
import org.springframework.cloud.netflix.zuul.filters.route.support.NoEncodingFormHttpMessageConverter;
import org.springframework.cloud.netflix.zuul.filters.route.support.ZuulProxyTestBase; import org.springframework.cloud.netflix.zuul.filters.route.support.ZuulProxyTestBase;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
...@@ -59,9 +62,13 @@ import org.springframework.http.HttpMethod; ...@@ -59,9 +62,13 @@ import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpResponse; import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.mock.http.client.MockClientHttpResponse; import org.springframework.mock.http.client.MockClientHttpResponse;
import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.MatrixVariable; import org.springframework.web.bind.annotation.MatrixVariable;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
...@@ -227,6 +234,24 @@ public class RestClientRibbonCommandIntegrationTests extends ZuulProxyTestBase { ...@@ -227,6 +234,24 @@ public class RestClientRibbonCommandIntegrationTests extends ZuulProxyTestBase {
this.ribbonCommandFactory instanceof TestConfig.MyRibbonCommandFactory); this.ribbonCommandFactory instanceof TestConfig.MyRibbonCommandFactory);
} }
@Override
@SuppressWarnings("deprecation")
@Test
public void javascriptEncodedFormParams() {
TestRestTemplate testRestTemplate = new TestRestTemplate();
ArrayList<HttpMessageConverter<?>> converters = new ArrayList<>();
converters.addAll(Arrays.asList(new StringHttpMessageConverter(),
new NoEncodingFormHttpMessageConverter()));
testRestTemplate.getRestTemplate().setMessageConverters(converters);
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add("foo", "(bar)");
ResponseEntity<String> result = testRestTemplate.postForEntity(
"http://localhost:" + this.port + "/simple/local", map, String.class);
assertEquals(HttpStatus.OK, result.getStatusCode());
assertEquals("Posted [(bar)] and Content-Length was: -1!", result.getBody());
}
@Test @Test
public void routeLocatorOverridden() { public void routeLocatorOverridden() {
assertTrue("routeLocator not a MyRouteLocator", assertTrue("routeLocator not a MyRouteLocator",
......
/*
* Copyright 2013-2016 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
*
* http://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.netflix.zuul.filters.route.support;
import java.io.IOException;
import java.util.Iterator;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StreamUtils;
/**
* @author Jacques-Etienne Beaudet
*/
public class NoEncodingFormHttpMessageConverter extends FormHttpMessageConverter {
@SuppressWarnings("unchecked")
@Override
public void write(MultiValueMap<String, ?> map, MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
MultiValueMap<String, String> form = (MultiValueMap<String, String>) map;
StringBuilder builder = new StringBuilder();
for (Iterator<String> nameIterator = form.keySet().iterator(); nameIterator.hasNext();) {
String name = nameIterator.next();
for (Iterator<String> valueIterator = form.get(name).iterator(); valueIterator.hasNext();) {
String value = valueIterator.next();
builder.append(name);
if (value != null) {
builder.append('=');
builder.append(value);
if (valueIterator.hasNext()) {
builder.append('&');
}
}
}
if (nameIterator.hasNext()) {
builder.append('&');
}
}
final byte[] bytes = builder.toString().getBytes(FormHttpMessageConverter.DEFAULT_CHARSET);
outputMessage.getHeaders().setContentLength(bytes.length);
outputMessage.getHeaders().setContentType(MediaType.APPLICATION_FORM_URLENCODED);
StreamUtils.copy(bytes, outputMessage.getBody());
}
}
...@@ -22,6 +22,8 @@ import static org.junit.Assert.assertEquals; ...@@ -22,6 +22,8 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assume.assumeThat; import static org.junit.Assume.assumeThat;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
...@@ -49,7 +51,12 @@ import org.springframework.context.annotation.Configuration; ...@@ -49,7 +51,12 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpEntity; import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
...@@ -57,6 +64,7 @@ import org.springframework.web.bind.annotation.RequestMapping; ...@@ -57,6 +64,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration; import org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import com.netflix.loadbalancer.Server; import com.netflix.loadbalancer.Server;
...@@ -247,6 +255,23 @@ public abstract class ZuulProxyTestBase { ...@@ -247,6 +255,23 @@ public abstract class ZuulProxyTestBase {
assertEquals("Patched 1!", result.getBody()); assertEquals("Patched 1!", result.getBody());
} }
@SuppressWarnings("deprecation")
@Test
public void javascriptEncodedFormParams() {
TestRestTemplate testRestTemplate = new TestRestTemplate();
ArrayList<HttpMessageConverter<?>> converters = new ArrayList<>();
converters.addAll(Arrays.asList(new StringHttpMessageConverter(),
new NoEncodingFormHttpMessageConverter()));
testRestTemplate.getRestTemplate().setMessageConverters(converters);
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add("foo", "(bar)");
ResponseEntity<String> result = testRestTemplate.postForEntity(
"http://localhost:" + this.port + "/simple/local", map, String.class);
assertEquals(HttpStatus.OK, result.getStatusCode());
assertEquals("Posted [(bar)] and Content-Length was: 13!", result.getBody());
}
protected abstract boolean supportsPatch(); protected abstract boolean supportsPatch();
public static abstract class AbstractZuulProxyApplication public static abstract class AbstractZuulProxyApplication
...@@ -268,6 +293,12 @@ public abstract class ZuulProxyTestBase { ...@@ -268,6 +293,12 @@ public abstract class ZuulProxyTestBase {
return "Hello local"; return "Hello local";
} }
@RequestMapping(value = "/local", method = RequestMethod.POST)
public String postWithFormParam(HttpServletRequest request,
@RequestBody MultiValueMap<String, String> body) {
return "Posted " + body.get("foo") + " and Content-Length was: " + request.getContentLength() + "!";
}
@RequestMapping(value = "/local/{id}", method = RequestMethod.DELETE) @RequestMapping(value = "/local/{id}", method = RequestMethod.DELETE)
public String delete(@PathVariable String id) { public String delete(@PathVariable String id) {
return "Deleted " + id + "!"; return "Deleted " + id + "!";
...@@ -341,6 +372,19 @@ public abstract class ZuulProxyTestBase { ...@@ -341,6 +372,19 @@ public abstract class ZuulProxyTestBase {
} }
} }
@Configuration
public class FormEncodedMessageConverterConfiguration extends WebMvcConfigurerAdapter {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
FormHttpMessageConverter converter = new FormHttpMessageConverter();
MediaType mediaType = new MediaType("application", "x-www-form-urlencoded", Charset.forName("UTF-8"));
converter.setSupportedMediaTypes(Arrays.asList(mediaType));
converters.add(converter);
super.configureMessageConverters(converters);
}
}
// Load balancer with fixed server list for "simple" pointing to localhost // Load balancer with fixed server list for "simple" pointing to localhost
@Configuration @Configuration
public static class SimpleRibbonClientConfiguration { public static class SimpleRibbonClientConfiguration {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment