Bumping versions
This commit is contained in:
@@ -137,8 +137,7 @@ public class ProxyExchange<T> {
|
||||
/**
|
||||
* Contains headers that are considered case-sensitive by default.
|
||||
*/
|
||||
public static Set<String> DEFAULT_SENSITIVE = new HashSet<>(
|
||||
Arrays.asList("cookie", "authorization"));
|
||||
public static Set<String> DEFAULT_SENSITIVE = new HashSet<>(Arrays.asList("cookie", "authorization"));
|
||||
|
||||
private URI uri;
|
||||
|
||||
@@ -160,16 +159,14 @@ public class ProxyExchange<T> {
|
||||
|
||||
private Type responseType;
|
||||
|
||||
public ProxyExchange(RestTemplate rest, NativeWebRequest webRequest,
|
||||
ModelAndViewContainer mavContainer, WebDataBinderFactory binderFactory,
|
||||
Type type) {
|
||||
public ProxyExchange(RestTemplate rest, NativeWebRequest webRequest, ModelAndViewContainer mavContainer,
|
||||
WebDataBinderFactory binderFactory, Type type) {
|
||||
this.responseType = type;
|
||||
this.rest = rest;
|
||||
this.webRequest = webRequest;
|
||||
this.mavContainer = mavContainer;
|
||||
this.binderFactory = binderFactory;
|
||||
this.delegate = new RequestResponseBodyMethodProcessor(
|
||||
rest.getMessageConverters());
|
||||
this.delegate = new RequestResponseBodyMethodProcessor(rest.getMessageConverters());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -249,28 +246,24 @@ public class ProxyExchange<T> {
|
||||
}
|
||||
|
||||
public String path() {
|
||||
return (String) this.webRequest.getAttribute(
|
||||
HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE,
|
||||
return (String) this.webRequest.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE,
|
||||
WebRequest.SCOPE_REQUEST);
|
||||
}
|
||||
|
||||
public String path(String prefix) {
|
||||
String path = path();
|
||||
if (!path.startsWith(prefix)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Path does not start with prefix (" + prefix + "): " + path);
|
||||
throw new IllegalArgumentException("Path does not start with prefix (" + prefix + "): " + path);
|
||||
}
|
||||
return path.substring(prefix.length());
|
||||
}
|
||||
|
||||
public void forward(String path) {
|
||||
HttpServletRequest request = this.webRequest
|
||||
.getNativeRequest(HttpServletRequest.class);
|
||||
HttpServletResponse response = this.webRequest
|
||||
.getNativeResponse(HttpServletResponse.class);
|
||||
HttpServletRequest request = this.webRequest.getNativeRequest(HttpServletRequest.class);
|
||||
HttpServletResponse response = this.webRequest.getNativeResponse(HttpServletResponse.class);
|
||||
try {
|
||||
request.getRequestDispatcher(path).forward(
|
||||
new BodyForwardingHttpServletRequest(request, response), response);
|
||||
request.getRequestDispatcher(path).forward(new BodyForwardingHttpServletRequest(request, response),
|
||||
response);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new IllegalStateException("Cannot forward request", e);
|
||||
@@ -278,79 +271,65 @@ public class ProxyExchange<T> {
|
||||
}
|
||||
|
||||
public ResponseEntity<T> get() {
|
||||
RequestEntity<?> requestEntity = headers((BodyBuilder) RequestEntity.get(uri))
|
||||
.build();
|
||||
RequestEntity<?> requestEntity = headers((BodyBuilder) RequestEntity.get(uri)).build();
|
||||
return exchange(requestEntity);
|
||||
}
|
||||
|
||||
public <S> ResponseEntity<S> get(
|
||||
Function<ResponseEntity<T>, ResponseEntity<S>> converter) {
|
||||
public <S> ResponseEntity<S> get(Function<ResponseEntity<T>, ResponseEntity<S>> converter) {
|
||||
return converter.apply(get());
|
||||
}
|
||||
|
||||
public ResponseEntity<T> head() {
|
||||
RequestEntity<?> requestEntity = headers((BodyBuilder) RequestEntity.head(uri))
|
||||
.build();
|
||||
RequestEntity<?> requestEntity = headers((BodyBuilder) RequestEntity.head(uri)).build();
|
||||
return exchange(requestEntity);
|
||||
}
|
||||
|
||||
public <S> ResponseEntity<S> head(
|
||||
Function<ResponseEntity<T>, ResponseEntity<S>> converter) {
|
||||
public <S> ResponseEntity<S> head(Function<ResponseEntity<T>, ResponseEntity<S>> converter) {
|
||||
return converter.apply(head());
|
||||
}
|
||||
|
||||
public ResponseEntity<T> options() {
|
||||
RequestEntity<?> requestEntity = headers((BodyBuilder) RequestEntity.options(uri))
|
||||
.build();
|
||||
RequestEntity<?> requestEntity = headers((BodyBuilder) RequestEntity.options(uri)).build();
|
||||
return exchange(requestEntity);
|
||||
}
|
||||
|
||||
public <S> ResponseEntity<S> options(
|
||||
Function<ResponseEntity<T>, ResponseEntity<S>> converter) {
|
||||
public <S> ResponseEntity<S> options(Function<ResponseEntity<T>, ResponseEntity<S>> converter) {
|
||||
return converter.apply(options());
|
||||
}
|
||||
|
||||
public ResponseEntity<T> post() {
|
||||
RequestEntity<Object> requestEntity = headers(RequestEntity.post(uri))
|
||||
.body(body());
|
||||
RequestEntity<Object> requestEntity = headers(RequestEntity.post(uri)).body(body());
|
||||
return exchange(requestEntity);
|
||||
}
|
||||
|
||||
public <S> ResponseEntity<S> post(
|
||||
Function<ResponseEntity<T>, ResponseEntity<S>> converter) {
|
||||
public <S> ResponseEntity<S> post(Function<ResponseEntity<T>, ResponseEntity<S>> converter) {
|
||||
return converter.apply(post());
|
||||
}
|
||||
|
||||
public ResponseEntity<T> delete() {
|
||||
RequestEntity<Object> requestEntity = headers(
|
||||
(BodyBuilder) RequestEntity.delete(uri)).body(body());
|
||||
RequestEntity<Object> requestEntity = headers((BodyBuilder) RequestEntity.delete(uri)).body(body());
|
||||
return exchange(requestEntity);
|
||||
}
|
||||
|
||||
public <S> ResponseEntity<S> delete(
|
||||
Function<ResponseEntity<T>, ResponseEntity<S>> converter) {
|
||||
public <S> ResponseEntity<S> delete(Function<ResponseEntity<T>, ResponseEntity<S>> converter) {
|
||||
return converter.apply(delete());
|
||||
}
|
||||
|
||||
public ResponseEntity<T> put() {
|
||||
RequestEntity<Object> requestEntity = headers(RequestEntity.put(uri))
|
||||
.body(body());
|
||||
RequestEntity<Object> requestEntity = headers(RequestEntity.put(uri)).body(body());
|
||||
return exchange(requestEntity);
|
||||
}
|
||||
|
||||
public <S> ResponseEntity<S> put(
|
||||
Function<ResponseEntity<T>, ResponseEntity<S>> converter) {
|
||||
public <S> ResponseEntity<S> put(Function<ResponseEntity<T>, ResponseEntity<S>> converter) {
|
||||
return converter.apply(put());
|
||||
}
|
||||
|
||||
public ResponseEntity<T> patch() {
|
||||
RequestEntity<Object> requestEntity = headers(RequestEntity.patch(uri))
|
||||
.body(body());
|
||||
RequestEntity<Object> requestEntity = headers(RequestEntity.patch(uri)).body(body());
|
||||
return exchange(requestEntity);
|
||||
}
|
||||
|
||||
public <S> ResponseEntity<S> patch(
|
||||
Function<ResponseEntity<T>, ResponseEntity<S>> converter) {
|
||||
public <S> ResponseEntity<S> patch(Function<ResponseEntity<T>, ResponseEntity<S>> converter) {
|
||||
return converter.apply(patch());
|
||||
}
|
||||
|
||||
@@ -359,8 +338,7 @@ public class ProxyExchange<T> {
|
||||
if (type instanceof TypeVariable || type instanceof WildcardType) {
|
||||
type = Object.class;
|
||||
}
|
||||
return rest.exchange(requestEntity,
|
||||
ParameterizedTypeReference.forType(responseType));
|
||||
return rest.exchange(requestEntity, ParameterizedTypeReference.forType(responseType));
|
||||
}
|
||||
|
||||
private BodyBuilder headers(BodyBuilder builder) {
|
||||
@@ -380,14 +358,13 @@ public class ProxyExchange<T> {
|
||||
|
||||
private void proxy() {
|
||||
try {
|
||||
URI uri = new URI(webRequest.getNativeRequest(HttpServletRequest.class)
|
||||
.getRequestURL().toString());
|
||||
URI uri = new URI(webRequest.getNativeRequest(HttpServletRequest.class).getRequestURL().toString());
|
||||
appendForwarded(uri);
|
||||
appendXForwarded(uri);
|
||||
}
|
||||
catch (URISyntaxException e) {
|
||||
throw new IllegalStateException("Cannot create URI for request: " + webRequest
|
||||
.getNativeRequest(HttpServletRequest.class).getRequestURL());
|
||||
throw new IllegalStateException("Cannot create URI for request: "
|
||||
+ webRequest.getNativeRequest(HttpServletRequest.class).getRequestURL());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -450,8 +427,7 @@ public class ProxyExchange<T> {
|
||||
return result.getTarget();
|
||||
}
|
||||
}
|
||||
MethodParameter input = new MethodParameter(
|
||||
ClassUtils.getMethod(BodyGrabber.class, "body", Object.class), 0);
|
||||
MethodParameter input = new MethodParameter(ClassUtils.getMethod(BodyGrabber.class, "body", Object.class), 0);
|
||||
try {
|
||||
delegate.resolveArgument(input, mavContainer, webRequest, binderFactory);
|
||||
}
|
||||
@@ -459,8 +435,7 @@ public class ProxyExchange<T> {
|
||||
throw new IllegalStateException("Cannot resolve body", e);
|
||||
}
|
||||
String name = Conventions.getVariableNameForParameter(input);
|
||||
BindingResult result = (BindingResult) mavContainer.getModel()
|
||||
.get(BindingResult.MODEL_KEY_PREFIX + name);
|
||||
BindingResult result = (BindingResult) mavContainer.getModel().get(BindingResult.MODEL_KEY_PREFIX + name);
|
||||
return result.getTarget();
|
||||
}
|
||||
|
||||
@@ -493,8 +468,7 @@ public class ProxyExchange<T> {
|
||||
|
||||
private HttpServletResponse response;
|
||||
|
||||
BodyForwardingHttpServletRequest(HttpServletRequest request,
|
||||
HttpServletResponse response) {
|
||||
BodyForwardingHttpServletRequest(HttpServletRequest request, HttpServletResponse response) {
|
||||
super(request);
|
||||
this.request = request;
|
||||
this.response = response;
|
||||
@@ -508,16 +482,13 @@ public class ProxyExchange<T> {
|
||||
@Override
|
||||
public ServletInputStream getInputStream() throws IOException {
|
||||
Object body = body();
|
||||
MethodParameter output = new MethodParameter(
|
||||
ClassUtils.getMethod(BodySender.class, "body"), -1);
|
||||
ServletOutputToInputConverter response = new ServletOutputToInputConverter(
|
||||
this.response);
|
||||
MethodParameter output = new MethodParameter(ClassUtils.getMethod(BodySender.class, "body"), -1);
|
||||
ServletOutputToInputConverter response = new ServletOutputToInputConverter(this.response);
|
||||
ServletWebRequest webRequest = new ServletWebRequest(this.request, response);
|
||||
try {
|
||||
delegate.handleReturnValue(body, output, mavContainer, webRequest);
|
||||
}
|
||||
catch (HttpMessageNotWritableException
|
||||
| HttpMediaTypeNotAcceptableException e) {
|
||||
catch (HttpMessageNotWritableException | HttpMediaTypeNotAcceptableException e) {
|
||||
throw new IllegalStateException("Cannot convert body", e);
|
||||
}
|
||||
return response.getInputStream();
|
||||
@@ -596,8 +567,7 @@ class ServletOutputToInputConverter extends HttpServletResponseWrapper {
|
||||
}
|
||||
|
||||
public ServletInputStream getInputStream() {
|
||||
ByteArrayInputStream body = new ByteArrayInputStream(
|
||||
builder.toString().getBytes());
|
||||
ByteArrayInputStream body = new ByteArrayInputStream(builder.toString().getBytes());
|
||||
return new ServletInputStream() {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -72,11 +72,9 @@ public class ProxyExchangeArgumentResolver implements HandlerMethodArgumentResol
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object resolveArgument(MethodParameter parameter,
|
||||
ModelAndViewContainer mavContainer, NativeWebRequest webRequest,
|
||||
WebDataBinderFactory binderFactory) throws Exception {
|
||||
ProxyExchange<?> proxy = new ProxyExchange<>(rest, webRequest, mavContainer,
|
||||
binderFactory, type(parameter));
|
||||
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
|
||||
ProxyExchange<?> proxy = new ProxyExchange<>(rest, webRequest, mavContainer, binderFactory, type(parameter));
|
||||
proxy.headers(headers);
|
||||
if (this.autoForwardedHeaders.size() > 0) {
|
||||
proxy.headers(extractAutoForwardedHeaders(webRequest));
|
||||
@@ -97,15 +95,13 @@ public class ProxyExchangeArgumentResolver implements HandlerMethodArgumentResol
|
||||
}
|
||||
|
||||
private HttpHeaders extractAutoForwardedHeaders(NativeWebRequest webRequest) {
|
||||
HttpServletRequest nativeRequest = webRequest
|
||||
.getNativeRequest(HttpServletRequest.class);
|
||||
HttpServletRequest nativeRequest = webRequest.getNativeRequest(HttpServletRequest.class);
|
||||
Enumeration<String> headerNames = nativeRequest.getHeaderNames();
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
while (headerNames.hasMoreElements()) {
|
||||
String header = headerNames.nextElement();
|
||||
if (this.autoForwardedHeaders.contains(header.toLowerCase())) {
|
||||
headers.addAll(header,
|
||||
Collections.list(nativeRequest.getHeaders(header)));
|
||||
headers.addAll(header, Collections.list(nativeRequest.getHeaders(header)));
|
||||
}
|
||||
}
|
||||
return headers;
|
||||
|
||||
@@ -56,8 +56,8 @@ public class ProxyResponseAutoConfiguration implements WebMvcConfigurer {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ProxyExchangeArgumentResolver proxyExchangeArgumentResolver(
|
||||
Optional<RestTemplateBuilder> optional, ProxyProperties proxy) {
|
||||
public ProxyExchangeArgumentResolver proxyExchangeArgumentResolver(Optional<RestTemplateBuilder> optional,
|
||||
ProxyProperties proxy) {
|
||||
RestTemplateBuilder builder = optional.orElse(new RestTemplateBuilder());
|
||||
RestTemplate template = builder.build();
|
||||
template.setErrorHandler(new NoOpResponseErrorHandler());
|
||||
@@ -67,8 +67,7 @@ public class ProxyResponseAutoConfiguration implements WebMvcConfigurer {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
ProxyExchangeArgumentResolver resolver = new ProxyExchangeArgumentResolver(
|
||||
template);
|
||||
ProxyExchangeArgumentResolver resolver = new ProxyExchangeArgumentResolver(template);
|
||||
resolver.setHeaders(proxy.convertHeaders());
|
||||
resolver.setAutoForwardedHeaders(proxy.getAutoForward());
|
||||
resolver.setSensitive(proxy.getSensitive()); // can be null
|
||||
@@ -76,8 +75,7 @@ public class ProxyResponseAutoConfiguration implements WebMvcConfigurer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addArgumentResolvers(
|
||||
List<HandlerMethodArgumentResolver> argumentResolvers) {
|
||||
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
|
||||
argumentResolvers.add(context.getBean(ProxyExchangeArgumentResolver.class));
|
||||
}
|
||||
|
||||
|
||||
@@ -84,26 +84,22 @@ public class ProductionConfigurationTests {
|
||||
|
||||
@Test
|
||||
public void path() throws Exception {
|
||||
assertThat(rest.getForObject("/proxy/path/1", Foo.class).getName())
|
||||
.isEqualTo("foo");
|
||||
assertThat(rest.getForObject("/proxy/path/1", Foo.class).getName()).isEqualTo("foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resource() throws Exception {
|
||||
assertThat(rest.getForObject("/proxy/html/test.html", String.class))
|
||||
.contains("<body>Test");
|
||||
assertThat(rest.getForObject("/proxy/html/test.html", String.class)).contains("<body>Test");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resourceWithNoType() throws Exception {
|
||||
assertThat(rest.getForObject("/proxy/typeless/test.html", String.class))
|
||||
.contains("<body>Test");
|
||||
assertThat(rest.getForObject("/proxy/typeless/test.html", String.class)).contains("<body>Test");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void missing() throws Exception {
|
||||
assertThat(rest.getForEntity("/proxy/missing/0", Foo.class).getStatusCode())
|
||||
.isEqualTo(HttpStatus.NOT_FOUND);
|
||||
assertThat(rest.getForEntity("/proxy/missing/0", Foo.class).getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -113,20 +109,18 @@ public class ProductionConfigurationTests {
|
||||
|
||||
@Test
|
||||
public void post() throws Exception {
|
||||
assertThat(rest.postForObject("/proxy/0", Collections.singletonMap("name", "foo"),
|
||||
Bar.class).getName()).isEqualTo("host=localhost:" + port + ";foo");
|
||||
assertThat(rest.postForObject("/proxy/0", Collections.singletonMap("name", "foo"), Bar.class).getName())
|
||||
.isEqualTo("host=localhost:" + port + ";foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forward() throws Exception {
|
||||
assertThat(rest.getForObject("/forward/foos/0", Foo.class).getName())
|
||||
.isEqualTo("bye");
|
||||
assertThat(rest.getForObject("/forward/foos/0", Foo.class).getName()).isEqualTo("bye");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forwardHeader() throws Exception {
|
||||
ResponseEntity<Foo> result = rest.getForEntity("/forward/special/foos/0",
|
||||
Foo.class);
|
||||
ResponseEntity<Foo> result = rest.getForEntity("/forward/special/foos/0", Foo.class);
|
||||
assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(result.getBody().getName()).isEqualTo("FOO");
|
||||
}
|
||||
@@ -134,11 +128,8 @@ public class ProductionConfigurationTests {
|
||||
@Test
|
||||
public void postForwardHeader() throws Exception {
|
||||
ResponseEntity<List<Bar>> result = rest.exchange(
|
||||
RequestEntity
|
||||
.post(rest.getRestTemplate().getUriTemplateHandler()
|
||||
.expand("/forward/special/bars"))
|
||||
.body(Collections
|
||||
.singletonList(Collections.singletonMap("name", "foo"))),
|
||||
RequestEntity.post(rest.getRestTemplate().getUriTemplateHandler().expand("/forward/special/bars"))
|
||||
.body(Collections.singletonList(Collections.singletonMap("name", "foo"))),
|
||||
new ParameterizedTypeReference<List<Bar>>() {
|
||||
});
|
||||
assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
@@ -147,13 +138,11 @@ public class ProductionConfigurationTests {
|
||||
|
||||
@Test
|
||||
public void postForwardBody() throws Exception {
|
||||
ResponseEntity<String> result = rest.exchange(
|
||||
RequestEntity
|
||||
.post(rest.getRestTemplate().getUriTemplateHandler()
|
||||
.expand("/forward/body/bars"))
|
||||
.body(Collections
|
||||
.singletonList(Collections.singletonMap("name", "foo"))),
|
||||
String.class);
|
||||
ResponseEntity<String> result = rest
|
||||
.exchange(
|
||||
RequestEntity.post(rest.getRestTemplate().getUriTemplateHandler().expand("/forward/body/bars"))
|
||||
.body(Collections.singletonList(Collections.singletonMap("name", "foo"))),
|
||||
String.class);
|
||||
assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(result.getBody()).contains("foo");
|
||||
}
|
||||
@@ -161,11 +150,8 @@ public class ProductionConfigurationTests {
|
||||
@Test
|
||||
public void postForwardForgetBody() throws Exception {
|
||||
ResponseEntity<String> result = rest.exchange(
|
||||
RequestEntity
|
||||
.post(rest.getRestTemplate().getUriTemplateHandler()
|
||||
.expand("/forward/forget/bars"))
|
||||
.body(Collections
|
||||
.singletonList(Collections.singletonMap("name", "foo"))),
|
||||
RequestEntity.post(rest.getRestTemplate().getUriTemplateHandler().expand("/forward/forget/bars"))
|
||||
.body(Collections.singletonList(Collections.singletonMap("name", "foo"))),
|
||||
String.class);
|
||||
assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(result.getBody()).contains("foo");
|
||||
@@ -174,11 +160,8 @@ public class ProductionConfigurationTests {
|
||||
@Test
|
||||
public void postForwardBodyFoo() throws Exception {
|
||||
ResponseEntity<List<Bar>> result = rest.exchange(
|
||||
RequestEntity
|
||||
.post(rest.getRestTemplate().getUriTemplateHandler()
|
||||
.expand("/forward/body/bars"))
|
||||
.body(Collections
|
||||
.singletonList(Collections.singletonMap("name", "foo"))),
|
||||
RequestEntity.post(rest.getRestTemplate().getUriTemplateHandler().expand("/forward/body/bars"))
|
||||
.body(Collections.singletonList(Collections.singletonMap("name", "foo"))),
|
||||
new ParameterizedTypeReference<List<Bar>>() {
|
||||
});
|
||||
assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
@@ -188,57 +171,44 @@ public class ProductionConfigurationTests {
|
||||
@Test
|
||||
public void list() throws Exception {
|
||||
assertThat(rest.exchange(
|
||||
RequestEntity
|
||||
.post(rest.getRestTemplate().getUriTemplateHandler()
|
||||
.expand("/proxy"))
|
||||
.body(Collections
|
||||
.singletonList(Collections.singletonMap("name", "foo"))),
|
||||
RequestEntity.post(rest.getRestTemplate().getUriTemplateHandler().expand("/proxy"))
|
||||
.body(Collections.singletonList(Collections.singletonMap("name", "foo"))),
|
||||
new ParameterizedTypeReference<List<Bar>>() {
|
||||
}).getBody().iterator().next().getName())
|
||||
.isEqualTo("host=localhost:" + port + ";foo");
|
||||
}).getBody().iterator().next().getName()).isEqualTo("host=localhost:" + port + ";foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bodyless() throws Exception {
|
||||
assertThat(rest.postForObject("/proxy/0", Collections.singletonMap("name", "foo"),
|
||||
Bar.class).getName()).isEqualTo("host=localhost:" + port + ";foo");
|
||||
assertThat(rest.postForObject("/proxy/0", Collections.singletonMap("name", "foo"), Bar.class).getName())
|
||||
.isEqualTo("host=localhost:" + port + ";foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void entity() throws Exception {
|
||||
assertThat(rest.exchange(
|
||||
RequestEntity
|
||||
.post(rest.getRestTemplate().getUriTemplateHandler()
|
||||
.expand("/proxy/entity"))
|
||||
.body(Collections.singletonMap("name", "foo")),
|
||||
new ParameterizedTypeReference<List<Bar>>() {
|
||||
}).getBody().iterator().next().getName())
|
||||
.isEqualTo("host=localhost:" + port + ";foo");
|
||||
assertThat(
|
||||
rest.exchange(RequestEntity.post(rest.getRestTemplate().getUriTemplateHandler().expand("/proxy/entity"))
|
||||
.body(Collections.singletonMap("name", "foo")), new ParameterizedTypeReference<List<Bar>>() {
|
||||
}).getBody().iterator().next().getName()).isEqualTo("host=localhost:" + port + ";foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void entityWithType() throws Exception {
|
||||
assertThat(rest.exchange(
|
||||
RequestEntity
|
||||
.post(rest.getRestTemplate().getUriTemplateHandler()
|
||||
.expand("/proxy/type"))
|
||||
.body(Collections.singletonMap("name", "foo")),
|
||||
new ParameterizedTypeReference<List<Bar>>() {
|
||||
}).getBody().iterator().next().getName())
|
||||
.isEqualTo("host=localhost:" + port + ";foo");
|
||||
assertThat(
|
||||
rest.exchange(RequestEntity.post(rest.getRestTemplate().getUriTemplateHandler().expand("/proxy/type"))
|
||||
.body(Collections.singletonMap("name", "foo")), new ParameterizedTypeReference<List<Bar>>() {
|
||||
}).getBody().iterator().next().getName()).isEqualTo("host=localhost:" + port + ";foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void single() throws Exception {
|
||||
assertThat(rest.postForObject("/proxy/single",
|
||||
Collections.singletonMap("name", "foobar"), Bar.class).getName())
|
||||
.isEqualTo("host=localhost:" + port + ";foobar");
|
||||
assertThat(rest.postForObject("/proxy/single", Collections.singletonMap("name", "foobar"), Bar.class).getName())
|
||||
.isEqualTo("host=localhost:" + port + ";foobar");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void converter() throws Exception {
|
||||
assertThat(rest.postForObject("/proxy/converter",
|
||||
Collections.singletonMap("name", "foobar"), Bar.class).getName())
|
||||
assertThat(
|
||||
rest.postForObject("/proxy/converter", Collections.singletonMap("name", "foobar"), Bar.class).getName())
|
||||
.isEqualTo("host=localhost:" + port + ";foobar");
|
||||
}
|
||||
|
||||
@@ -250,8 +220,7 @@ public class ProductionConfigurationTests {
|
||||
|
||||
@Test
|
||||
public void deleteWithoutBody() throws Exception {
|
||||
ResponseEntity<Void> deleteResponse = rest.exchange("/proxy/{id}/no-body",
|
||||
HttpMethod.DELETE, null, Void.TYPE,
|
||||
ResponseEntity<Void> deleteResponse = rest.exchange("/proxy/{id}/no-body", HttpMethod.DELETE, null, Void.TYPE,
|
||||
Collections.singletonMap("id", "123"));
|
||||
assertThat(deleteResponse.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
}
|
||||
@@ -261,28 +230,20 @@ public class ProductionConfigurationTests {
|
||||
Foo foo = new Foo("to-be-deleted");
|
||||
ParameterizedTypeReference<Map<String, Foo>> returnType = new ParameterizedTypeReference<Map<String, Foo>>() {
|
||||
};
|
||||
ResponseEntity<Map<String, Foo>> deleteResponse = rest.exchange("/proxy/{id}",
|
||||
HttpMethod.DELETE, new HttpEntity<Foo>(foo), returnType,
|
||||
Collections.singletonMap("id", "123"));
|
||||
ResponseEntity<Map<String, Foo>> deleteResponse = rest.exchange("/proxy/{id}", HttpMethod.DELETE,
|
||||
new HttpEntity<Foo>(foo), returnType, Collections.singletonMap("id", "123"));
|
||||
assertThat(deleteResponse.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(deleteResponse.getBody().get("deleted"))
|
||||
.isEqualToComparingFieldByField(foo);
|
||||
assertThat(deleteResponse.getBody().get("deleted")).isEqualToComparingFieldByField(foo);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings({ "Duplicates", "unchecked" })
|
||||
public void headers() throws Exception {
|
||||
Map<String, List<String>> headers = rest
|
||||
.exchange(
|
||||
RequestEntity
|
||||
.get(rest.getRestTemplate().getUriTemplateHandler()
|
||||
.expand("/proxy/headers"))
|
||||
.header("foo", "bar").header("abc", "xyz")
|
||||
.header("baz", "fob").build(),
|
||||
Map.class)
|
||||
.exchange(RequestEntity.get(rest.getRestTemplate().getUriTemplateHandler().expand("/proxy/headers"))
|
||||
.header("foo", "bar").header("abc", "xyz").header("baz", "fob").build(), Map.class)
|
||||
.getBody();
|
||||
assertThat(headers).doesNotContainKey("foo").doesNotContainKey("hello")
|
||||
.containsKeys("bar", "abc");
|
||||
assertThat(headers).doesNotContainKey("foo").doesNotContainKey("hello").containsKeys("bar", "abc");
|
||||
|
||||
assertThat(headers.get("bar")).containsOnly("hello");
|
||||
assertThat(headers.get("abc")).containsOnly("123");
|
||||
@@ -292,9 +253,7 @@ public class ProductionConfigurationTests {
|
||||
@Test
|
||||
public void forwardedHeaderUsesHost() throws Exception {
|
||||
Map<String, List<String>> headers = rest
|
||||
.exchange(RequestEntity
|
||||
.get(rest.getRestTemplate().getUriTemplateHandler()
|
||||
.expand("/proxy/headers"))
|
||||
.exchange(RequestEntity.get(rest.getRestTemplate().getUriTemplateHandler().expand("/proxy/headers"))
|
||||
.header("host", "foo:1234").build(), Map.class)
|
||||
.getBody();
|
||||
|
||||
@@ -323,35 +282,32 @@ public class ProductionConfigurationTests {
|
||||
}
|
||||
|
||||
@GetMapping("/proxy/{id}")
|
||||
public ResponseEntity<?> proxyFoos(@PathVariable Integer id,
|
||||
ProxyExchange<?> proxy) throws Exception {
|
||||
public ResponseEntity<?> proxyFoos(@PathVariable Integer id, ProxyExchange<?> proxy) throws Exception {
|
||||
return proxy.uri(home.toString() + "/foos/" + id).get();
|
||||
}
|
||||
|
||||
@GetMapping("/proxy/path/**")
|
||||
public ResponseEntity<?> proxyPath(ProxyExchange<?> proxy,
|
||||
UriComponentsBuilder uri) throws Exception {
|
||||
public ResponseEntity<?> proxyPath(ProxyExchange<?> proxy, UriComponentsBuilder uri) throws Exception {
|
||||
String path = proxy.path("/proxy/path/");
|
||||
return proxy.uri(home.toString() + "/foos/" + path).get();
|
||||
}
|
||||
|
||||
@GetMapping("/proxy/html/**")
|
||||
public ResponseEntity<String> proxyHtml(ProxyExchange<String> proxy,
|
||||
UriComponentsBuilder uri) throws Exception {
|
||||
public ResponseEntity<String> proxyHtml(ProxyExchange<String> proxy, UriComponentsBuilder uri)
|
||||
throws Exception {
|
||||
String path = proxy.path("/proxy/html");
|
||||
return proxy.uri(home.toString() + path).get();
|
||||
}
|
||||
|
||||
@GetMapping("/proxy/typeless/**")
|
||||
public ResponseEntity<?> proxyTypeless(ProxyExchange<byte[]> proxy,
|
||||
UriComponentsBuilder uri) throws Exception {
|
||||
public ResponseEntity<?> proxyTypeless(ProxyExchange<byte[]> proxy, UriComponentsBuilder uri)
|
||||
throws Exception {
|
||||
String path = proxy.path("/proxy/typeless");
|
||||
return proxy.uri(home.toString() + path).get();
|
||||
}
|
||||
|
||||
@GetMapping("/proxy/missing/{id}")
|
||||
public ResponseEntity<?> proxyMissing(@PathVariable Integer id,
|
||||
ProxyExchange<?> proxy) throws Exception {
|
||||
public ResponseEntity<?> proxyMissing(@PathVariable Integer id, ProxyExchange<?> proxy) throws Exception {
|
||||
return proxy.uri(home.toString() + "/missing/" + id).get();
|
||||
}
|
||||
|
||||
@@ -361,47 +317,39 @@ public class ProductionConfigurationTests {
|
||||
}
|
||||
|
||||
@PostMapping("/proxy/{id}")
|
||||
public ResponseEntity<?> proxyBars(@PathVariable Integer id,
|
||||
@RequestBody Map<String, Object> body,
|
||||
public ResponseEntity<?> proxyBars(@PathVariable Integer id, @RequestBody Map<String, Object> body,
|
||||
ProxyExchange<List<Object>> proxy) throws Exception {
|
||||
body.put("id", id);
|
||||
return proxy.uri(home.toString() + "/bars").body(Arrays.asList(body))
|
||||
.post(this::first);
|
||||
return proxy.uri(home.toString() + "/bars").body(Arrays.asList(body)).post(this::first);
|
||||
}
|
||||
|
||||
@PostMapping("/proxy")
|
||||
public ResponseEntity<?> barsWithNoBody(ProxyExchange<?> proxy)
|
||||
throws Exception {
|
||||
public ResponseEntity<?> barsWithNoBody(ProxyExchange<?> proxy) throws Exception {
|
||||
return proxy.uri(home.toString() + "/bars").post();
|
||||
}
|
||||
|
||||
@PostMapping("/proxy/entity")
|
||||
public ResponseEntity<?> explicitEntity(@RequestBody Foo foo,
|
||||
ProxyExchange<?> proxy) throws Exception {
|
||||
return proxy.uri(home.toString() + "/bars").body(Arrays.asList(foo))
|
||||
.post();
|
||||
public ResponseEntity<?> explicitEntity(@RequestBody Foo foo, ProxyExchange<?> proxy) throws Exception {
|
||||
return proxy.uri(home.toString() + "/bars").body(Arrays.asList(foo)).post();
|
||||
}
|
||||
|
||||
@PostMapping("/proxy/type")
|
||||
public ResponseEntity<List<Bar>> explicitEntityWithType(@RequestBody Foo foo,
|
||||
ProxyExchange<List<Bar>> proxy) throws Exception {
|
||||
return proxy.uri(home.toString() + "/bars").body(Arrays.asList(foo))
|
||||
.post();
|
||||
return proxy.uri(home.toString() + "/bars").body(Arrays.asList(foo)).post();
|
||||
}
|
||||
|
||||
@PostMapping("/proxy/single")
|
||||
public ResponseEntity<?> implicitEntity(@RequestBody Foo foo,
|
||||
ProxyExchange<List<Object>> proxy) throws Exception {
|
||||
return proxy.uri(home.toString() + "/bars").body(Arrays.asList(foo))
|
||||
.post(this::first);
|
||||
public ResponseEntity<?> implicitEntity(@RequestBody Foo foo, ProxyExchange<List<Object>> proxy)
|
||||
throws Exception {
|
||||
return proxy.uri(home.toString() + "/bars").body(Arrays.asList(foo)).post(this::first);
|
||||
}
|
||||
|
||||
@PostMapping("/proxy/converter")
|
||||
public ResponseEntity<Bar> implicitEntityWithConverter(@RequestBody Foo foo,
|
||||
ProxyExchange<List<Bar>> proxy) throws Exception {
|
||||
public ResponseEntity<Bar> implicitEntityWithConverter(@RequestBody Foo foo, ProxyExchange<List<Bar>> proxy)
|
||||
throws Exception {
|
||||
return proxy.uri(home.toString() + "/bars").body(Arrays.asList(foo))
|
||||
.post(response -> ResponseEntity.status(response.getStatusCode())
|
||||
.headers(response.getHeaders())
|
||||
.post(response -> ResponseEntity.status(response.getStatusCode()).headers(response.getHeaders())
|
||||
.body(response.getBody().iterator().next()));
|
||||
}
|
||||
|
||||
@@ -411,18 +359,16 @@ public class ProductionConfigurationTests {
|
||||
}
|
||||
|
||||
@DeleteMapping("/proxy/{id}/no-body")
|
||||
public ResponseEntity<?> deleteWithoutBody(@PathVariable Integer id,
|
||||
ProxyExchange<?> proxy) throws Exception {
|
||||
public ResponseEntity<?> deleteWithoutBody(@PathVariable Integer id, ProxyExchange<?> proxy)
|
||||
throws Exception {
|
||||
return proxy.uri(home.toString() + "/foos/" + id + "/no-body").delete();
|
||||
}
|
||||
|
||||
@DeleteMapping("/proxy/{id}")
|
||||
public ResponseEntity<?> deleteWithBody(@PathVariable Integer id,
|
||||
@RequestBody Foo foo, ProxyExchange<?> proxy) throws Exception {
|
||||
return proxy.uri(home.toString() + "/foos/" + id).body(foo)
|
||||
.delete(response -> ResponseEntity
|
||||
.status(response.getStatusCode())
|
||||
.headers(response.getHeaders()).body(response.getBody()));
|
||||
public ResponseEntity<?> deleteWithBody(@PathVariable Integer id, @RequestBody Foo foo,
|
||||
ProxyExchange<?> proxy) throws Exception {
|
||||
return proxy.uri(home.toString() + "/foos/" + id).body(foo).delete(response -> ResponseEntity
|
||||
.status(response.getStatusCode()).headers(response.getHeaders()).body(response.getBody()));
|
||||
}
|
||||
|
||||
@GetMapping("/forward/**")
|
||||
@@ -446,23 +392,20 @@ public class ProductionConfigurationTests {
|
||||
}
|
||||
|
||||
@PostMapping("/forward/body/**")
|
||||
public void postForwardBody(@RequestBody byte[] body, ProxyExchange<?> proxy)
|
||||
throws Exception {
|
||||
public void postForwardBody(@RequestBody byte[] body, ProxyExchange<?> proxy) throws Exception {
|
||||
String path = proxy.path("/forward/body");
|
||||
proxy.body(body).forward(path);
|
||||
}
|
||||
|
||||
@PostMapping("/forward/forget/**")
|
||||
public void postForwardForgetBody(@RequestBody byte[] body,
|
||||
ProxyExchange<?> proxy) throws Exception {
|
||||
public void postForwardForgetBody(@RequestBody byte[] body, ProxyExchange<?> proxy) throws Exception {
|
||||
String path = proxy.path("/forward/forget");
|
||||
proxy.forward(path);
|
||||
}
|
||||
|
||||
@GetMapping("/proxy/headers")
|
||||
@SuppressWarnings("Duplicates")
|
||||
public ResponseEntity<Map<String, List<String>>> headers(
|
||||
ProxyExchange<Map<String, List<String>>> proxy) {
|
||||
public ResponseEntity<Map<String, List<String>>> headers(ProxyExchange<Map<String, List<String>>> proxy) {
|
||||
proxy.sensitive("foo");
|
||||
proxy.sensitive("hello");
|
||||
proxy.header("bar", "hello");
|
||||
@@ -472,8 +415,7 @@ public class ProductionConfigurationTests {
|
||||
}
|
||||
|
||||
private <T> ResponseEntity<T> first(ResponseEntity<List<T>> response) {
|
||||
return ResponseEntity.status(response.getStatusCode())
|
||||
.headers(response.getHeaders())
|
||||
return ResponseEntity.status(response.getStatusCode()).headers(response.getHeaders())
|
||||
.body(response.getBody().iterator().next());
|
||||
}
|
||||
|
||||
@@ -504,18 +446,15 @@ public class ProductionConfigurationTests {
|
||||
}
|
||||
|
||||
@DeleteMapping("/foos/{id}")
|
||||
public ResponseEntity<?> deleteFoo(@PathVariable Integer id,
|
||||
@RequestBody Foo foo) {
|
||||
public ResponseEntity<?> deleteFoo(@PathVariable Integer id, @RequestBody Foo foo) {
|
||||
return ResponseEntity.ok().body(Collections.singletonMap("deleted", foo));
|
||||
}
|
||||
|
||||
@PostMapping("/bars")
|
||||
public List<Bar> bars(@RequestBody List<Foo> foos,
|
||||
@RequestHeader HttpHeaders headers) {
|
||||
public List<Bar> bars(@RequestBody List<Foo> foos, @RequestHeader HttpHeaders headers) {
|
||||
String custom = headers.getFirst("X-Custom");
|
||||
custom = custom == null ? "" : custom;
|
||||
custom = headers.getFirst("forwarded") == null ? custom
|
||||
: headers.getFirst("forwarded") + ";" + custom;
|
||||
custom = headers.getFirst("forwarded") == null ? custom : headers.getFirst("forwarded") + ";" + custom;
|
||||
return Arrays.asList(new Bar(custom + foos.iterator().next().getName()));
|
||||
}
|
||||
|
||||
|
||||
@@ -157,18 +157,16 @@ public class GatewaySampleApplication {
|
||||
|
||||
@Bean
|
||||
public RouterFunction<ServerResponse> testFunRouterFunction() {
|
||||
RouterFunction<ServerResponse> route = RouterFunctions.route(
|
||||
RequestPredicates.path("/testfun"),
|
||||
RouterFunction<ServerResponse> route = RouterFunctions.route(RequestPredicates.path("/testfun"),
|
||||
request -> ServerResponse.ok().body(BodyInserters.fromValue("hello")));
|
||||
return route;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RouterFunction<ServerResponse> testWhenMetricPathIsNotMeet() {
|
||||
RouterFunction<ServerResponse> route = RouterFunctions.route(
|
||||
RequestPredicates.path("/actuator/metrics/gateway.requests"),
|
||||
request -> ServerResponse.ok().body(BodyInserters
|
||||
.fromValue(HELLO_FROM_FAKE_ACTUATOR_METRICS_GATEWAY_REQUESTS)));
|
||||
RouterFunction<ServerResponse> route = RouterFunctions
|
||||
.route(RequestPredicates.path("/actuator/metrics/gateway.requests"), request -> ServerResponse.ok()
|
||||
.body(BodyInserters.fromValue(HELLO_FROM_FAKE_ACTUATOR_METRICS_GATEWAY_REQUESTS)));
|
||||
return route;
|
||||
}
|
||||
|
||||
|
||||
@@ -84,8 +84,7 @@ public class ThrottleGatewayFilter implements GatewayFilter {
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
|
||||
TokenBucket tokenBucket = TokenBuckets.builder().withCapacity(capacity)
|
||||
.withFixedIntervalRefillStrategy(refillTokens, refillPeriod, refillUnit)
|
||||
.build();
|
||||
.withFixedIntervalRefillStrategy(refillTokens, refillPeriod, refillUnit).build();
|
||||
|
||||
// TODO: get a token bucket for a key
|
||||
log.debug("TokenBucket capacity: " + tokenBucket.getCapacity());
|
||||
|
||||
@@ -49,8 +49,8 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = { GatewaySampleApplicationTests.TestConfig.class },
|
||||
webEnvironment = RANDOM_PORT, properties = "management.server.port=${test.port}")
|
||||
@SpringBootTest(classes = { GatewaySampleApplicationTests.TestConfig.class }, webEnvironment = RANDOM_PORT,
|
||||
properties = "management.server.port=${test.port}")
|
||||
public class GatewaySampleApplicationTests {
|
||||
|
||||
protected static int managementPort;
|
||||
@@ -77,8 +77,7 @@ public class GatewaySampleApplicationTests {
|
||||
@Before
|
||||
public void setup() {
|
||||
baseUri = "http://localhost:" + port;
|
||||
this.webClient = WebTestClient.bindToServer()
|
||||
.responseTimeout(Duration.ofSeconds(10)).baseUrl(baseUri).build();
|
||||
this.webClient = WebTestClient.bindToServer().responseTimeout(Duration.ofSeconds(10)).baseUrl(baseUri).build();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -89,108 +88,88 @@ public class GatewaySampleApplicationTests {
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void readBodyPredicateStringWorks() {
|
||||
webClient.post().uri("/post").header("Host", "www.readbody.org").bodyValue("hi")
|
||||
.exchange().expectStatus().isOk().expectHeader()
|
||||
.valueEquals("X-TestHeader", "read_body_pred").expectBody(Map.class)
|
||||
.consumeWith(result -> assertThat(result.getResponseBody())
|
||||
.containsEntry("data", "hi"));
|
||||
webClient.post().uri("/post").header("Host", "www.readbody.org").bodyValue("hi").exchange().expectStatus()
|
||||
.isOk().expectHeader().valueEquals("X-TestHeader", "read_body_pred").expectBody(Map.class)
|
||||
.consumeWith(result -> assertThat(result.getResponseBody()).containsEntry("data", "hi"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void rewriteRequestBodyStringWorks() {
|
||||
webClient.post().uri("/post").header("Host", "www.rewriterequestupper.org")
|
||||
.bodyValue("hello").exchange().expectStatus().isOk().expectHeader()
|
||||
.valueEquals("X-TestHeader", "rewrite_request_upper")
|
||||
webClient.post().uri("/post").header("Host", "www.rewriterequestupper.org").bodyValue("hello").exchange()
|
||||
.expectStatus().isOk().expectHeader().valueEquals("X-TestHeader", "rewrite_request_upper")
|
||||
.expectBody(Map.class)
|
||||
.consumeWith(result -> assertThat(result.getResponseBody())
|
||||
.containsEntry("data", "HELLOHELLO"));
|
||||
.consumeWith(result -> assertThat(result.getResponseBody()).containsEntry("data", "HELLOHELLO"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void rewriteRequestBodyObjectWorks() {
|
||||
webClient.post().uri("/post").header("Host", "www.rewriterequestobj.org")
|
||||
.bodyValue("hello").exchange().expectStatus().isOk().expectHeader()
|
||||
.valueEquals("X-TestHeader", "rewrite_request").expectBody(Map.class)
|
||||
.consumeWith(result -> assertThat(result.getResponseBody())
|
||||
.containsEntry("data", "{\"message\":\"HELLO\"}"));
|
||||
webClient.post().uri("/post").header("Host", "www.rewriterequestobj.org").bodyValue("hello").exchange()
|
||||
.expectStatus().isOk().expectHeader().valueEquals("X-TestHeader", "rewrite_request")
|
||||
.expectBody(Map.class).consumeWith(result -> assertThat(result.getResponseBody()).containsEntry("data",
|
||||
"{\"message\":\"HELLO\"}"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void rewriteResponseBodyStringWorks() {
|
||||
webClient.post().uri("/post").header("Host", "www.rewriteresponseupper.org")
|
||||
.bodyValue("hello").exchange().expectStatus().isOk().expectHeader()
|
||||
.valueEquals("X-TestHeader", "rewrite_response_upper")
|
||||
webClient.post().uri("/post").header("Host", "www.rewriteresponseupper.org").bodyValue("hello").exchange()
|
||||
.expectStatus().isOk().expectHeader().valueEquals("X-TestHeader", "rewrite_response_upper")
|
||||
.expectBody(Map.class)
|
||||
.consumeWith(result -> assertThat(result.getResponseBody())
|
||||
.containsEntry("DATA", "HELLO"));
|
||||
.consumeWith(result -> assertThat(result.getResponseBody()).containsEntry("DATA", "HELLO"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void rewriteResponseEmptyBodyToStringWorks() {
|
||||
webClient.post().uri("/post/empty").header("Host", "www.rewriteemptyresponse.org")
|
||||
.exchange().expectStatus().isOk().expectHeader()
|
||||
.valueEquals("X-TestHeader", "rewrite_empty_response")
|
||||
.expectBody(String.class)
|
||||
.consumeWith(result -> assertThat(result.getResponseBody())
|
||||
.isEqualTo("emptybody"));
|
||||
webClient.post().uri("/post/empty").header("Host", "www.rewriteemptyresponse.org").exchange().expectStatus()
|
||||
.isOk().expectHeader().valueEquals("X-TestHeader", "rewrite_empty_response").expectBody(String.class)
|
||||
.consumeWith(result -> assertThat(result.getResponseBody()).isEqualTo("emptybody"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void emptyBodySupplierNotCalledWhenBodyPresent() {
|
||||
webClient.post().uri("/post")
|
||||
.header("Host", "www.rewriteresponsewithfailsupplier.org")
|
||||
.bodyValue("hello").exchange().expectStatus().isOk().expectHeader()
|
||||
.valueEquals("X-TestHeader", "rewrite_response_fail_supplier")
|
||||
.expectBody(Map.class)
|
||||
.consumeWith(result -> assertThat(result.getResponseBody())
|
||||
.containsEntry("DATA", "HELLO"));
|
||||
webClient.post().uri("/post").header("Host", "www.rewriteresponsewithfailsupplier.org").bodyValue("hello")
|
||||
.exchange().expectStatus().isOk().expectHeader()
|
||||
.valueEquals("X-TestHeader", "rewrite_response_fail_supplier").expectBody(Map.class)
|
||||
.consumeWith(result -> assertThat(result.getResponseBody()).containsEntry("DATA", "HELLO"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void rewriteResponeBodyObjectWorks() {
|
||||
webClient.post().uri("/post").header("Host", "www.rewriteresponseobj.org")
|
||||
.bodyValue("hello").exchange().expectStatus().isOk().expectHeader()
|
||||
.valueEquals("X-TestHeader", "rewrite_response_obj")
|
||||
webClient.post().uri("/post").header("Host", "www.rewriteresponseobj.org").bodyValue("hello").exchange()
|
||||
.expectStatus().isOk().expectHeader().valueEquals("X-TestHeader", "rewrite_response_obj")
|
||||
.expectBody(String.class)
|
||||
.consumeWith(result -> assertThat(result.getResponseBody())
|
||||
.isEqualTo("hello"));
|
||||
.consumeWith(result -> assertThat(result.getResponseBody()).isEqualTo("hello"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void complexPredicate() {
|
||||
webClient.get().uri("/anything/png").header("Host", "www.abc.org").exchange()
|
||||
.expectHeader().valueEquals("X-TestHeader", "foobar").expectStatus()
|
||||
.isOk();
|
||||
webClient.get().uri("/anything/png").header("Host", "www.abc.org").exchange().expectHeader()
|
||||
.valueEquals("X-TestHeader", "foobar").expectStatus().isOk();
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void actuatorManagementPort() {
|
||||
webClient.get()
|
||||
.uri("http://localhost:" + managementPort + "/actuator/gateway/routes")
|
||||
.exchange().expectStatus().isOk();
|
||||
webClient.get().uri("http://localhost:" + managementPort + "/actuator/gateway/routes").exchange().expectStatus()
|
||||
.isOk();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void actuatorMetrics() {
|
||||
contextLoads();
|
||||
webClient.get()
|
||||
.uri("http://localhost:" + managementPort
|
||||
+ "/actuator/metrics/gateway.requests")
|
||||
.exchange().expectStatus().isOk().expectBody().consumeWith(i -> {
|
||||
webClient.get().uri("http://localhost:" + managementPort + "/actuator/metrics/gateway.requests").exchange()
|
||||
.expectStatus().isOk().expectBody().consumeWith(i -> {
|
||||
String body = new String(i.getResponseBodyContent());
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
try {
|
||||
JsonNode actualObj = mapper.readTree(body);
|
||||
JsonNode findValue = actualObj.findValue("name");
|
||||
assertThat(findValue.asText())
|
||||
.as("Expected to find metric with name gateway.requests")
|
||||
assertThat(findValue.asText()).as("Expected to find metric with name gateway.requests")
|
||||
.isEqualTo("gateway.requests");
|
||||
}
|
||||
catch (IOException e) {
|
||||
@@ -218,10 +197,8 @@ public class GatewaySampleApplicationTests {
|
||||
int port;
|
||||
|
||||
@Bean
|
||||
public ServiceInstanceListSupplier fixedServiceInstanceListSupplier(
|
||||
Environment env) {
|
||||
return ServiceInstanceListSupplier.fixed(env)
|
||||
.instance("localhost", port, "httpbin").build();
|
||||
public ServiceInstanceListSupplier fixedServiceInstanceListSupplier(Environment env) {
|
||||
return ServiceInstanceListSupplier.fixed(env).instance("localhost", port, "httpbin").build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -35,8 +35,7 @@ import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import org.springframework.util.SocketUtils;
|
||||
|
||||
@RunWith(ModifiedClassPathRunner.class)
|
||||
@ClassPathExclusions({ "micrometer-*.jar", "spring-boot-actuator-*.jar",
|
||||
"spring-boot-actuator-autoconfigure-*.jar" })
|
||||
@ClassPathExclusions({ "micrometer-*.jar", "spring-boot-actuator-*.jar", "spring-boot-actuator-autoconfigure-*.jar" })
|
||||
@DirtiesContext
|
||||
public class GatewaySampleApplicationWithoutMetricsTests {
|
||||
|
||||
@@ -60,8 +59,7 @@ public class GatewaySampleApplicationWithoutMetricsTests {
|
||||
@Before
|
||||
public void setup() {
|
||||
baseUri = "http://localhost:" + port;
|
||||
this.webClient = WebTestClient.bindToServer()
|
||||
.responseTimeout(Duration.ofSeconds(10)).baseUrl(baseUri).build();
|
||||
this.webClient = WebTestClient.bindToServer().responseTimeout(Duration.ofSeconds(10)).baseUrl(baseUri).build();
|
||||
}
|
||||
|
||||
protected ConfigurableApplicationContext init(Class<?> config) {
|
||||
@@ -73,10 +71,9 @@ public class GatewaySampleApplicationWithoutMetricsTests {
|
||||
public void actuatorMetrics() {
|
||||
init(TestConfig.class);
|
||||
webClient.get().uri("/get").exchange().expectStatus().isOk();
|
||||
webClient.get()
|
||||
.uri("http://localhost:" + port + "/actuator/metrics/gateway.requests")
|
||||
.exchange().expectStatus().isOk().expectBody(String.class).isEqualTo(
|
||||
GatewaySampleApplication.HELLO_FROM_FAKE_ACTUATOR_METRICS_GATEWAY_REQUESTS);
|
||||
webClient.get().uri("http://localhost:" + port + "/actuator/metrics/gateway.requests").exchange().expectStatus()
|
||||
.isOk().expectBody(String.class)
|
||||
.isEqualTo(GatewaySampleApplication.HELLO_FROM_FAKE_ACTUATOR_METRICS_GATEWAY_REQUESTS);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -66,11 +66,10 @@ public class AbstractGatewayControllerEndpoint implements ApplicationEventPublis
|
||||
|
||||
protected ApplicationEventPublisher publisher;
|
||||
|
||||
public AbstractGatewayControllerEndpoint(
|
||||
RouteDefinitionLocator routeDefinitionLocator,
|
||||
public AbstractGatewayControllerEndpoint(RouteDefinitionLocator routeDefinitionLocator,
|
||||
List<GlobalFilter> globalFilters, List<GatewayFilterFactory> gatewayFilters,
|
||||
List<RoutePredicateFactory> routePredicates,
|
||||
RouteDefinitionWriter routeDefinitionWriter, RouteLocator routeLocator) {
|
||||
List<RoutePredicateFactory> routePredicates, RouteDefinitionWriter routeDefinitionWriter,
|
||||
RouteLocator routeLocator) {
|
||||
this.routeDefinitionLocator = routeDefinitionLocator;
|
||||
this.globalFilters = globalFilters;
|
||||
this.GatewayFilters = gatewayFilters;
|
||||
@@ -128,32 +127,25 @@ public class AbstractGatewayControllerEndpoint implements ApplicationEventPublis
|
||||
*/
|
||||
@PostMapping("/routes/{id}")
|
||||
@SuppressWarnings("unchecked")
|
||||
public Mono<ResponseEntity<Object>> save(@PathVariable String id,
|
||||
@RequestBody RouteDefinition route) {
|
||||
public Mono<ResponseEntity<Object>> save(@PathVariable String id, @RequestBody RouteDefinition route) {
|
||||
|
||||
return Mono.just(route).filter(this::validateRouteDefinition)
|
||||
.flatMap(routeDefinition -> this.routeDefinitionWriter
|
||||
.save(Mono.just(routeDefinition).map(r -> {
|
||||
r.setId(id);
|
||||
log.debug("Saving route: " + route);
|
||||
return r;
|
||||
}))
|
||||
.then(Mono.defer(() -> Mono.just(ResponseEntity
|
||||
.created(URI.create("/routes/" + id)).build()))))
|
||||
.switchIfEmpty(
|
||||
Mono.defer(() -> Mono.just(ResponseEntity.badRequest().build())));
|
||||
.flatMap(routeDefinition -> this.routeDefinitionWriter.save(Mono.just(routeDefinition).map(r -> {
|
||||
r.setId(id);
|
||||
log.debug("Saving route: " + route);
|
||||
return r;
|
||||
})).then(Mono.defer(() -> Mono.just(ResponseEntity.created(URI.create("/routes/" + id)).build()))))
|
||||
.switchIfEmpty(Mono.defer(() -> Mono.just(ResponseEntity.badRequest().build())));
|
||||
}
|
||||
|
||||
private boolean validateRouteDefinition(RouteDefinition routeDefinition) {
|
||||
boolean hasValidFilterDefinitions = routeDefinition.getFilters().stream()
|
||||
.allMatch(filterDefinition -> GatewayFilters.stream()
|
||||
.anyMatch(gatewayFilterFactory -> filterDefinition.getName()
|
||||
.equals(gatewayFilterFactory.name())));
|
||||
.allMatch(filterDefinition -> GatewayFilters.stream().anyMatch(
|
||||
gatewayFilterFactory -> filterDefinition.getName().equals(gatewayFilterFactory.name())));
|
||||
|
||||
boolean hasValidPredicateDefinitions = routeDefinition.getPredicates().stream()
|
||||
.allMatch(predicateDefinition -> routePredicates.stream()
|
||||
.anyMatch(routePredicate -> predicateDefinition.getName()
|
||||
.equals(routePredicate.name())));
|
||||
.anyMatch(routePredicate -> predicateDefinition.getName().equals(routePredicate.name())));
|
||||
log.debug("FilterDefinitions valid: " + hasValidFilterDefinitions);
|
||||
log.debug("PredicateDefinitions valid: " + hasValidPredicateDefinitions);
|
||||
return hasValidFilterDefinitions && hasValidPredicateDefinitions;
|
||||
@@ -163,15 +155,14 @@ public class AbstractGatewayControllerEndpoint implements ApplicationEventPublis
|
||||
public Mono<ResponseEntity<Object>> delete(@PathVariable String id) {
|
||||
return this.routeDefinitionWriter.delete(Mono.just(id))
|
||||
.then(Mono.defer(() -> Mono.just(ResponseEntity.ok().build())))
|
||||
.onErrorResume(t -> t instanceof NotFoundException,
|
||||
t -> Mono.just(ResponseEntity.notFound().build()));
|
||||
.onErrorResume(t -> t instanceof NotFoundException, t -> Mono.just(ResponseEntity.notFound().build()));
|
||||
}
|
||||
|
||||
@GetMapping("/routes/{id}/combinedfilters")
|
||||
public Mono<HashMap<String, Object>> combinedfilters(@PathVariable String id) {
|
||||
// TODO: missing global filters
|
||||
return this.routeLocator.getRoutes().filter(route -> route.getId().equals(id))
|
||||
.reduce(new HashMap<>(), this::putItem);
|
||||
return this.routeLocator.getRoutes().filter(route -> route.getId().equals(id)).reduce(new HashMap<>(),
|
||||
this::putItem);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -45,13 +45,11 @@ import org.springframework.web.bind.annotation.PathVariable;
|
||||
@RestControllerEndpoint(id = "gateway")
|
||||
public class GatewayControllerEndpoint extends AbstractGatewayControllerEndpoint {
|
||||
|
||||
public GatewayControllerEndpoint(List<GlobalFilter> globalFilters,
|
||||
List<GatewayFilterFactory> gatewayFilters,
|
||||
List<RoutePredicateFactory> routePredicates,
|
||||
RouteDefinitionWriter routeDefinitionWriter, RouteLocator routeLocator,
|
||||
RouteDefinitionLocator routeDefinitionLocator) {
|
||||
super(routeDefinitionLocator, globalFilters, gatewayFilters, routePredicates,
|
||||
routeDefinitionWriter, routeLocator);
|
||||
public GatewayControllerEndpoint(List<GlobalFilter> globalFilters, List<GatewayFilterFactory> gatewayFilters,
|
||||
List<RoutePredicateFactory> routePredicates, RouteDefinitionWriter routeDefinitionWriter,
|
||||
RouteLocator routeLocator, RouteDefinitionLocator routeDefinitionLocator) {
|
||||
super(routeDefinitionLocator, globalFilters, gatewayFilters, routePredicates, routeDefinitionWriter,
|
||||
routeLocator);
|
||||
}
|
||||
|
||||
@GetMapping("/routedefinitions")
|
||||
|
||||
@@ -46,16 +46,16 @@ public class GatewayLegacyControllerEndpoint extends AbstractGatewayControllerEn
|
||||
|
||||
public GatewayLegacyControllerEndpoint(RouteDefinitionLocator routeDefinitionLocator,
|
||||
List<GlobalFilter> globalFilters, List<GatewayFilterFactory> GatewayFilters,
|
||||
List<RoutePredicateFactory> routePredicates,
|
||||
RouteDefinitionWriter routeDefinitionWriter, RouteLocator routeLocator) {
|
||||
super(routeDefinitionLocator, globalFilters, GatewayFilters, routePredicates,
|
||||
routeDefinitionWriter, routeLocator);
|
||||
List<RoutePredicateFactory> routePredicates, RouteDefinitionWriter routeDefinitionWriter,
|
||||
RouteLocator routeLocator) {
|
||||
super(routeDefinitionLocator, globalFilters, GatewayFilters, routePredicates, routeDefinitionWriter,
|
||||
routeLocator);
|
||||
}
|
||||
|
||||
@GetMapping("/routes")
|
||||
public Mono<List<Map<String, Object>>> routes() {
|
||||
Mono<Map<String, RouteDefinition>> routeDefs = this.routeDefinitionLocator
|
||||
.getRouteDefinitions().collectMap(RouteDefinition::getId);
|
||||
Mono<Map<String, RouteDefinition>> routeDefs = this.routeDefinitionLocator.getRouteDefinitions()
|
||||
.collectMap(RouteDefinition::getId);
|
||||
Mono<List<Route>> routes = this.routeLocator.getRoutes().collectList();
|
||||
return Mono.zip(routeDefs, routes).map(tuple -> {
|
||||
Map<String, RouteDefinition> defs = tuple.getT1();
|
||||
@@ -102,10 +102,8 @@ public class GatewayLegacyControllerEndpoint extends AbstractGatewayControllerEn
|
||||
@GetMapping("/routes/{id}")
|
||||
public Mono<ResponseEntity<RouteDefinition>> route(@PathVariable String id) {
|
||||
// TODO: missing RouteLocator
|
||||
return this.routeDefinitionLocator.getRouteDefinitions()
|
||||
.filter(route -> route.getId().equals(id)).singleOrEmpty()
|
||||
.map(ResponseEntity::ok)
|
||||
.switchIfEmpty(Mono.just(ResponseEntity.notFound().build()));
|
||||
return this.routeDefinitionLocator.getRouteDefinitions().filter(route -> route.getId().equals(id))
|
||||
.singleOrEmpty().map(ResponseEntity::ok).switchIfEmpty(Mono.just(ResponseEntity.notFound().build()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -164,8 +164,7 @@ import static org.springframework.cloud.gateway.config.HttpClientProperties.Pool
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true)
|
||||
@EnableConfigurationProperties
|
||||
@AutoConfigureBefore({ HttpHandlerAutoConfiguration.class,
|
||||
WebFluxAutoConfiguration.class })
|
||||
@AutoConfigureBefore({ HttpHandlerAutoConfiguration.class, WebFluxAutoConfiguration.class })
|
||||
@AutoConfigureAfter({ GatewayReactiveLoadBalancerClientAutoConfiguration.class,
|
||||
GatewayClassPathWarningAutoConfiguration.class })
|
||||
@ConditionalOnClass(DispatcherHandler.class)
|
||||
@@ -177,15 +176,13 @@ public class GatewayAutoConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RouteLocatorBuilder routeLocatorBuilder(
|
||||
ConfigurableApplicationContext context) {
|
||||
public RouteLocatorBuilder routeLocatorBuilder(ConfigurableApplicationContext context) {
|
||||
return new RouteLocatorBuilder(context);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public PropertiesRouteDefinitionLocator propertiesRouteDefinitionLocator(
|
||||
GatewayProperties properties) {
|
||||
public PropertiesRouteDefinitionLocator propertiesRouteDefinitionLocator(GatewayProperties properties) {
|
||||
return new PropertiesRouteDefinitionLocator(properties);
|
||||
}
|
||||
|
||||
@@ -197,10 +194,8 @@ public class GatewayAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
public RouteDefinitionLocator routeDefinitionLocator(
|
||||
List<RouteDefinitionLocator> routeDefinitionLocators) {
|
||||
return new CompositeRouteDefinitionLocator(
|
||||
Flux.fromIterable(routeDefinitionLocators));
|
||||
public RouteDefinitionLocator routeDefinitionLocator(List<RouteDefinitionLocator> routeDefinitionLocators) {
|
||||
return new CompositeRouteDefinitionLocator(Flux.fromIterable(routeDefinitionLocators));
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -212,12 +207,10 @@ public class GatewayAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public RouteLocator routeDefinitionRouteLocator(GatewayProperties properties,
|
||||
List<GatewayFilterFactory> gatewayFilters,
|
||||
List<RoutePredicateFactory> predicates,
|
||||
RouteDefinitionLocator routeDefinitionLocator,
|
||||
ConfigurationService configurationService) {
|
||||
return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates,
|
||||
gatewayFilters, properties, configurationService);
|
||||
List<GatewayFilterFactory> gatewayFilters, List<RoutePredicateFactory> predicates,
|
||||
RouteDefinitionLocator routeDefinitionLocator, ConfigurationService configurationService) {
|
||||
return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates, gatewayFilters, properties,
|
||||
configurationService);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -225,15 +218,12 @@ public class GatewayAutoConfiguration {
|
||||
@ConditionalOnMissingBean(name = "cachedCompositeRouteLocator")
|
||||
// TODO: property to disable composite?
|
||||
public RouteLocator cachedCompositeRouteLocator(List<RouteLocator> routeLocators) {
|
||||
return new CachingRouteLocator(
|
||||
new CompositeRouteLocator(Flux.fromIterable(routeLocators)));
|
||||
return new CachingRouteLocator(new CompositeRouteLocator(Flux.fromIterable(routeLocators)));
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnClass(
|
||||
name = "org.springframework.cloud.client.discovery.event.HeartbeatMonitor")
|
||||
public RouteRefreshListener routeRefreshListener(
|
||||
ApplicationEventPublisher publisher) {
|
||||
@ConditionalOnClass(name = "org.springframework.cloud.client.discovery.event.HeartbeatMonitor")
|
||||
public RouteRefreshListener routeRefreshListener(ApplicationEventPublisher publisher) {
|
||||
return new RouteRefreshListener(publisher);
|
||||
}
|
||||
|
||||
@@ -248,11 +238,9 @@ public class GatewayAutoConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RoutePredicateHandlerMapping routePredicateHandlerMapping(
|
||||
FilteringWebHandler webHandler, RouteLocator routeLocator,
|
||||
GlobalCorsProperties globalCorsProperties, Environment environment) {
|
||||
return new RoutePredicateHandlerMapping(webHandler, routeLocator,
|
||||
globalCorsProperties, environment);
|
||||
public RoutePredicateHandlerMapping routePredicateHandlerMapping(FilteringWebHandler webHandler,
|
||||
RouteLocator routeLocator, GlobalCorsProperties globalCorsProperties, Environment environment) {
|
||||
return new RoutePredicateHandlerMapping(webHandler, routeLocator, globalCorsProperties, environment);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -268,8 +256,7 @@ public class GatewayAutoConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(name = "spring.cloud.gateway.forwarded.enabled",
|
||||
matchIfMissing = true)
|
||||
@ConditionalOnProperty(name = "spring.cloud.gateway.forwarded.enabled", matchIfMissing = true)
|
||||
public ForwardedHeadersFilter forwardedHeadersFilter() {
|
||||
return new ForwardedHeadersFilter();
|
||||
}
|
||||
@@ -282,8 +269,7 @@ public class GatewayAutoConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(name = "spring.cloud.gateway.x-forwarded.enabled",
|
||||
matchIfMissing = true)
|
||||
@ConditionalOnProperty(name = "spring.cloud.gateway.x-forwarded.enabled", matchIfMissing = true)
|
||||
public XForwardedHeadersFilter xForwardedHeadersFilter() {
|
||||
return new XForwardedHeadersFilter();
|
||||
}
|
||||
@@ -306,8 +292,7 @@ public class GatewayAutoConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ForwardRoutingFilter forwardRoutingFilter(
|
||||
ObjectProvider<DispatcherHandler> dispatcherHandler) {
|
||||
public ForwardRoutingFilter forwardRoutingFilter(ObjectProvider<DispatcherHandler> dispatcherHandler) {
|
||||
return new ForwardRoutingFilter(dispatcherHandler);
|
||||
}
|
||||
|
||||
@@ -317,22 +302,18 @@ public class GatewayAutoConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public WebSocketService webSocketService(
|
||||
RequestUpgradeStrategy requestUpgradeStrategy) {
|
||||
public WebSocketService webSocketService(RequestUpgradeStrategy requestUpgradeStrategy) {
|
||||
return new HandshakeWebSocketService(requestUpgradeStrategy);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public WebsocketRoutingFilter websocketRoutingFilter(WebSocketClient webSocketClient,
|
||||
WebSocketService webSocketService,
|
||||
ObjectProvider<List<HttpHeadersFilter>> headersFilters) {
|
||||
return new WebsocketRoutingFilter(webSocketClient, webSocketService,
|
||||
headersFilters);
|
||||
WebSocketService webSocketService, ObjectProvider<List<HttpHeadersFilter>> headersFilters) {
|
||||
return new WebsocketRoutingFilter(webSocketClient, webSocketService, headersFilters);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public WeightCalculatorWebFilter weightCalculatorWebFilter(
|
||||
ConfigurationService configurationService,
|
||||
public WeightCalculatorWebFilter weightCalculatorWebFilter(ConfigurationService configurationService,
|
||||
ObjectProvider<RouteLocator> routeLocator) {
|
||||
return new WeightCalculatorWebFilter(routeLocator, configurationService);
|
||||
}
|
||||
@@ -394,8 +375,7 @@ public class GatewayAutoConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ReadBodyPredicateFactory readBodyPredicateFactory(
|
||||
ServerCodecConfigurer codecConfigurer) {
|
||||
public ReadBodyPredicateFactory readBodyPredicateFactory(ServerCodecConfigurer codecConfigurer) {
|
||||
return new ReadBodyPredicateFactory(codecConfigurer.getReaders());
|
||||
}
|
||||
|
||||
@@ -452,8 +432,7 @@ public class GatewayAutoConfiguration {
|
||||
public ModifyResponseBodyGatewayFilterFactory modifyResponseBodyGatewayFilterFactory(
|
||||
ServerCodecConfigurer codecConfigurer, Set<MessageBodyDecoder> bodyDecoders,
|
||||
Set<MessageBodyEncoder> bodyEncoders) {
|
||||
return new ModifyResponseBodyGatewayFilterFactory(codecConfigurer.getReaders(),
|
||||
bodyDecoders, bodyEncoders);
|
||||
return new ModifyResponseBodyGatewayFilterFactory(codecConfigurer.getReaders(), bodyDecoders, bodyEncoders);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -495,8 +474,8 @@ public class GatewayAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean({ RateLimiter.class, KeyResolver.class })
|
||||
public RequestRateLimiterGatewayFilterFactory requestRateLimiterGatewayFilterFactory(
|
||||
RateLimiter rateLimiter, KeyResolver resolver) {
|
||||
public RequestRateLimiterGatewayFilterFactory requestRateLimiterGatewayFilterFactory(RateLimiter rateLimiter,
|
||||
KeyResolver resolver) {
|
||||
return new RequestRateLimiterGatewayFilterFactory(rateLimiter, resolver);
|
||||
}
|
||||
|
||||
@@ -516,8 +495,7 @@ public class GatewayAutoConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SecureHeadersGatewayFilterFactory secureHeadersGatewayFilterFactory(
|
||||
SecureHeadersProperties properties) {
|
||||
public SecureHeadersGatewayFilterFactory secureHeadersGatewayFilterFactory(SecureHeadersProperties properties) {
|
||||
return new SecureHeadersGatewayFilterFactory(properties);
|
||||
}
|
||||
|
||||
@@ -589,8 +567,8 @@ public class GatewayAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(name = "spring.cloud.gateway.httpserver.wiretap")
|
||||
public NettyWebServerFactoryCustomizer nettyServerWiretapCustomizer(
|
||||
Environment environment, ServerProperties serverProperties) {
|
||||
public NettyWebServerFactoryCustomizer nettyServerWiretapCustomizer(Environment environment,
|
||||
ServerProperties serverProperties) {
|
||||
return new NettyWebServerFactoryCustomizer(environment, serverProperties) {
|
||||
@Override
|
||||
public void customize(NettyReactiveWebServerFactory factory) {
|
||||
@@ -602,8 +580,7 @@ public class GatewayAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public HttpClient gatewayHttpClient(HttpClientProperties properties,
|
||||
List<HttpClientCustomizer> customizers) {
|
||||
public HttpClient gatewayHttpClient(HttpClientProperties properties, List<HttpClientCustomizer> customizers) {
|
||||
|
||||
// configure pool resources
|
||||
HttpClientProperties.Pool pool = properties.getPool();
|
||||
@@ -613,10 +590,9 @@ public class GatewayAutoConfiguration {
|
||||
connectionProvider = ConnectionProvider.newConnection();
|
||||
}
|
||||
else if (pool.getType() == FIXED) {
|
||||
ConnectionProvider.Builder builder = ConnectionProvider
|
||||
.builder(pool.getName()).maxConnections(pool.getMaxConnections())
|
||||
.pendingAcquireMaxCount(-1).pendingAcquireTimeout(
|
||||
Duration.ofMillis(pool.getAcquireTimeout()));
|
||||
ConnectionProvider.Builder builder = ConnectionProvider.builder(pool.getName())
|
||||
.maxConnections(pool.getMaxConnections()).pendingAcquireMaxCount(-1)
|
||||
.pendingAcquireTimeout(Duration.ofMillis(pool.getAcquireTimeout()));
|
||||
if (pool.getMaxIdleTime() != null) {
|
||||
builder.maxIdleTime(pool.getMaxIdleTime());
|
||||
}
|
||||
@@ -626,9 +602,8 @@ public class GatewayAutoConfiguration {
|
||||
connectionProvider = builder.build();
|
||||
}
|
||||
else {
|
||||
ConnectionProvider.Builder builder = ConnectionProvider
|
||||
.builder(pool.getName()).maxConnections(Integer.MAX_VALUE)
|
||||
.pendingAcquireTimeout(Duration.ofMillis(0))
|
||||
ConnectionProvider.Builder builder = ConnectionProvider.builder(pool.getName())
|
||||
.maxConnections(Integer.MAX_VALUE).pendingAcquireTimeout(Duration.ofMillis(0))
|
||||
.pendingAcquireMaxCount(-1);
|
||||
if (pool.getMaxIdleTime() != null) {
|
||||
builder.maxIdleTime(pool.getMaxIdleTime());
|
||||
@@ -644,20 +619,17 @@ public class GatewayAutoConfiguration {
|
||||
.httpResponseDecoder(spec -> {
|
||||
if (properties.getMaxHeaderSize() != null) {
|
||||
// cast to int is ok, since @Max is Integer.MAX_VALUE
|
||||
spec.maxHeaderSize(
|
||||
(int) properties.getMaxHeaderSize().toBytes());
|
||||
spec.maxHeaderSize((int) properties.getMaxHeaderSize().toBytes());
|
||||
}
|
||||
if (properties.getMaxInitialLineLength() != null) {
|
||||
// cast to int is ok, since @Max is Integer.MAX_VALUE
|
||||
spec.maxInitialLineLength(
|
||||
(int) properties.getMaxInitialLineLength().toBytes());
|
||||
spec.maxInitialLineLength((int) properties.getMaxInitialLineLength().toBytes());
|
||||
}
|
||||
return spec;
|
||||
}).tcpConfiguration(tcpClient -> {
|
||||
|
||||
if (properties.getConnectTimeout() != null) {
|
||||
tcpClient = tcpClient.option(
|
||||
ChannelOption.CONNECT_TIMEOUT_MILLIS,
|
||||
tcpClient = tcpClient.option(ChannelOption.CONNECT_TIMEOUT_MILLIS,
|
||||
properties.getConnectTimeout());
|
||||
}
|
||||
|
||||
@@ -667,19 +639,16 @@ public class GatewayAutoConfiguration {
|
||||
if (StringUtils.hasText(proxy.getHost())) {
|
||||
|
||||
tcpClient = tcpClient.proxy(proxySpec -> {
|
||||
ProxyProvider.Builder builder = proxySpec
|
||||
.type(ProxyProvider.Proxy.HTTP)
|
||||
ProxyProvider.Builder builder = proxySpec.type(ProxyProvider.Proxy.HTTP)
|
||||
.host(proxy.getHost());
|
||||
|
||||
PropertyMapper map = PropertyMapper.get();
|
||||
|
||||
map.from(proxy::getPort).whenNonNull().to(builder::port);
|
||||
map.from(proxy::getUsername).whenHasText()
|
||||
.to(builder::username);
|
||||
map.from(proxy::getUsername).whenHasText().to(builder::username);
|
||||
map.from(proxy::getPassword).whenHasText()
|
||||
.to(password -> builder.password(s -> password));
|
||||
map.from(proxy::getNonProxyHostsPattern).whenHasText()
|
||||
.to(builder::nonProxyHosts);
|
||||
map.from(proxy::getNonProxyHostsPattern).whenHasText().to(builder::nonProxyHosts);
|
||||
});
|
||||
}
|
||||
return tcpClient;
|
||||
@@ -687,33 +656,27 @@ public class GatewayAutoConfiguration {
|
||||
|
||||
HttpClientProperties.Ssl ssl = properties.getSsl();
|
||||
if ((ssl.getKeyStore() != null && ssl.getKeyStore().length() > 0)
|
||||
|| ssl.getTrustedX509CertificatesForTrustManager().length > 0
|
||||
|| ssl.isUseInsecureTrustManager()) {
|
||||
|| ssl.getTrustedX509CertificatesForTrustManager().length > 0 || ssl.isUseInsecureTrustManager()) {
|
||||
httpClient = httpClient.secure(sslContextSpec -> {
|
||||
// configure ssl
|
||||
SslContextBuilder sslContextBuilder = SslContextBuilder.forClient();
|
||||
|
||||
X509Certificate[] trustedX509Certificates = ssl
|
||||
.getTrustedX509CertificatesForTrustManager();
|
||||
X509Certificate[] trustedX509Certificates = ssl.getTrustedX509CertificatesForTrustManager();
|
||||
if (trustedX509Certificates.length > 0) {
|
||||
sslContextBuilder = sslContextBuilder
|
||||
.trustManager(trustedX509Certificates);
|
||||
sslContextBuilder = sslContextBuilder.trustManager(trustedX509Certificates);
|
||||
}
|
||||
else if (ssl.isUseInsecureTrustManager()) {
|
||||
sslContextBuilder = sslContextBuilder
|
||||
.trustManager(InsecureTrustManagerFactory.INSTANCE);
|
||||
sslContextBuilder = sslContextBuilder.trustManager(InsecureTrustManagerFactory.INSTANCE);
|
||||
}
|
||||
|
||||
try {
|
||||
sslContextBuilder = sslContextBuilder
|
||||
.keyManager(ssl.getKeyManagerFactory());
|
||||
sslContextBuilder = sslContextBuilder.keyManager(ssl.getKeyManagerFactory());
|
||||
}
|
||||
catch (Exception e) {
|
||||
logger.error(e);
|
||||
}
|
||||
|
||||
sslContextSpec.sslContext(sslContextBuilder)
|
||||
.defaultConfiguration(ssl.getDefaultConfigurationType())
|
||||
sslContextSpec.sslContext(sslContextBuilder).defaultConfiguration(ssl.getDefaultConfigurationType())
|
||||
.handshakeTimeout(ssl.getHandshakeTimeout())
|
||||
.closeNotifyFlushTimeout(ssl.getCloseNotifyFlushTimeout())
|
||||
.closeNotifyReadTimeout(ssl.getCloseNotifyReadTimeout());
|
||||
@@ -741,25 +704,21 @@ public class GatewayAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public NettyRoutingFilter routingFilter(HttpClient httpClient,
|
||||
ObjectProvider<List<HttpHeadersFilter>> headersFilters,
|
||||
HttpClientProperties properties) {
|
||||
ObjectProvider<List<HttpHeadersFilter>> headersFilters, HttpClientProperties properties) {
|
||||
return new NettyRoutingFilter(httpClient, headersFilters, properties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public NettyWriteResponseFilter nettyWriteResponseFilter(
|
||||
GatewayProperties properties) {
|
||||
public NettyWriteResponseFilter nettyWriteResponseFilter(GatewayProperties properties) {
|
||||
return new NettyWriteResponseFilter(properties.getStreamingMediaTypes());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ReactorNettyWebSocketClient reactorNettyWebSocketClient(
|
||||
HttpClientProperties properties, HttpClient httpClient) {
|
||||
ReactorNettyWebSocketClient webSocketClient = new ReactorNettyWebSocketClient(
|
||||
httpClient);
|
||||
public ReactorNettyWebSocketClient reactorNettyWebSocketClient(HttpClientProperties properties,
|
||||
HttpClient httpClient) {
|
||||
ReactorNettyWebSocketClient webSocketClient = new ReactorNettyWebSocketClient(httpClient);
|
||||
if (properties.getWebsocket().getMaxFramePayloadLength() != null) {
|
||||
webSocketClient.setMaxFramePayloadLength(
|
||||
properties.getWebsocket().getMaxFramePayloadLength());
|
||||
webSocketClient.setMaxFramePayloadLength(properties.getWebsocket().getMaxFramePayloadLength());
|
||||
}
|
||||
webSocketClient.setHandlePing(properties.getWebsocket().isProxyPing());
|
||||
return webSocketClient;
|
||||
@@ -770,8 +729,7 @@ public class GatewayAutoConfiguration {
|
||||
HttpClientProperties httpClientProperties) {
|
||||
ReactorNettyRequestUpgradeStrategy requestUpgradeStrategy = new ReactorNettyRequestUpgradeStrategy();
|
||||
|
||||
HttpClientProperties.Websocket websocket = httpClientProperties
|
||||
.getWebsocket();
|
||||
HttpClientProperties.Websocket websocket = httpClientProperties.getWebsocket();
|
||||
PropertyMapper map = PropertyMapper.get();
|
||||
map.from(websocket::getMaxFramePayloadLength).whenNonNull()
|
||||
.to(requestUpgradeStrategy::setMaxFramePayloadLength);
|
||||
@@ -786,32 +744,25 @@ public class GatewayAutoConfiguration {
|
||||
protected static class GatewayActuatorConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(name = "spring.cloud.gateway.actuator.verbose.enabled",
|
||||
matchIfMissing = true)
|
||||
@ConditionalOnProperty(name = "spring.cloud.gateway.actuator.verbose.enabled", matchIfMissing = true)
|
||||
@ConditionalOnAvailableEndpoint
|
||||
public GatewayControllerEndpoint gatewayControllerEndpoint(
|
||||
List<GlobalFilter> globalFilters,
|
||||
List<GatewayFilterFactory> gatewayFilters,
|
||||
List<RoutePredicateFactory> routePredicates,
|
||||
public GatewayControllerEndpoint gatewayControllerEndpoint(List<GlobalFilter> globalFilters,
|
||||
List<GatewayFilterFactory> gatewayFilters, List<RoutePredicateFactory> routePredicates,
|
||||
RouteDefinitionWriter routeDefinitionWriter, RouteLocator routeLocator,
|
||||
RouteDefinitionLocator routeDefinitionLocator) {
|
||||
return new GatewayControllerEndpoint(globalFilters, gatewayFilters,
|
||||
routePredicates, routeDefinitionWriter, routeLocator,
|
||||
routeDefinitionLocator);
|
||||
return new GatewayControllerEndpoint(globalFilters, gatewayFilters, routePredicates, routeDefinitionWriter,
|
||||
routeLocator, routeDefinitionLocator);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Conditional(OnVerboseDisabledCondition.class)
|
||||
@ConditionalOnAvailableEndpoint
|
||||
public GatewayLegacyControllerEndpoint gatewayLegacyControllerEndpoint(
|
||||
RouteDefinitionLocator routeDefinitionLocator,
|
||||
List<GlobalFilter> globalFilters,
|
||||
List<GatewayFilterFactory> gatewayFilters,
|
||||
List<RoutePredicateFactory> routePredicates,
|
||||
RouteDefinitionLocator routeDefinitionLocator, List<GlobalFilter> globalFilters,
|
||||
List<GatewayFilterFactory> gatewayFilters, List<RoutePredicateFactory> routePredicates,
|
||||
RouteDefinitionWriter routeDefinitionWriter, RouteLocator routeLocator) {
|
||||
return new GatewayLegacyControllerEndpoint(routeDefinitionLocator,
|
||||
globalFilters, gatewayFilters, routePredicates, routeDefinitionWriter,
|
||||
routeLocator);
|
||||
return new GatewayLegacyControllerEndpoint(routeDefinitionLocator, globalFilters, gatewayFilters,
|
||||
routePredicates, routeDefinitionWriter, routeLocator);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -822,8 +773,7 @@ public class GatewayAutoConfiguration {
|
||||
super(ConfigurationPhase.REGISTER_BEAN);
|
||||
}
|
||||
|
||||
@ConditionalOnProperty(name = "spring.cloud.gateway.actuator.verbose.enabled",
|
||||
matchIfMissing = true)
|
||||
@ConditionalOnProperty(name = "spring.cloud.gateway.actuator.verbose.enabled", matchIfMissing = true)
|
||||
static class VerboseDisabled {
|
||||
|
||||
}
|
||||
|
||||
@@ -28,8 +28,7 @@ import org.springframework.context.annotation.Configuration;
|
||||
@AutoConfigureBefore(GatewayAutoConfiguration.class)
|
||||
public class GatewayClassPathWarningAutoConfiguration {
|
||||
|
||||
private static final Log log = LogFactory
|
||||
.getLog(GatewayClassPathWarningAutoConfiguration.class);
|
||||
private static final Log log = LogFactory.getLog(GatewayClassPathWarningAutoConfiguration.class);
|
||||
|
||||
private static final String BORDER = "\n\n**********************************************************\n\n";
|
||||
|
||||
|
||||
@@ -26,11 +26,9 @@ import org.springframework.core.env.MapPropertySource;
|
||||
public class GatewayEnvironmentPostProcessor implements EnvironmentPostProcessor {
|
||||
|
||||
@Override
|
||||
public void postProcessEnvironment(ConfigurableEnvironment env,
|
||||
SpringApplication application) {
|
||||
env.getPropertySources().addFirst(
|
||||
new MapPropertySource("gateway-properties", Collections.singletonMap(
|
||||
"spring.webflux.hiddenmethod.filter.enabled", "false")));
|
||||
public void postProcessEnvironment(ConfigurableEnvironment env, SpringApplication application) {
|
||||
env.getPropertySources().addFirst(new MapPropertySource("gateway-properties",
|
||||
Collections.singletonMap("spring.webflux.hiddenmethod.filter.enabled", "false")));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -42,10 +42,8 @@ import org.springframework.web.reactive.DispatcherHandler;
|
||||
@ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true)
|
||||
@EnableConfigurationProperties(GatewayMetricsProperties.class)
|
||||
@AutoConfigureBefore(HttpHandlerAutoConfiguration.class)
|
||||
@AutoConfigureAfter({ MetricsAutoConfiguration.class,
|
||||
MeterRegistryAutoConfiguration.class })
|
||||
@ConditionalOnClass({ DispatcherHandler.class, MeterRegistry.class,
|
||||
MetricsAutoConfiguration.class })
|
||||
@AutoConfigureAfter({ MetricsAutoConfiguration.class, MeterRegistryAutoConfiguration.class })
|
||||
@ConditionalOnClass({ DispatcherHandler.class, MeterRegistry.class, MetricsAutoConfiguration.class })
|
||||
public class GatewayMetricsAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@@ -59,15 +57,13 @@ public class GatewayMetricsAutoConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PropertiesTagsProvider propertiesTagsProvider(
|
||||
GatewayMetricsProperties gatewayMetricsProperties) {
|
||||
public PropertiesTagsProvider propertiesTagsProvider(GatewayMetricsProperties gatewayMetricsProperties) {
|
||||
return new PropertiesTagsProvider(gatewayMetricsProperties.getTags());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(MeterRegistry.class)
|
||||
@ConditionalOnProperty(name = "spring.cloud.gateway.metrics.enabled",
|
||||
matchIfMissing = true)
|
||||
@ConditionalOnProperty(name = "spring.cloud.gateway.metrics.enabled", matchIfMissing = true)
|
||||
public GatewayMetricsFilter gatewayMetricFilter(MeterRegistry meterRegistry,
|
||||
List<GatewayTagsProvider> tagsProviders) {
|
||||
return new GatewayMetricsFilter(meterRegistry, tagsProviders);
|
||||
|
||||
@@ -50,8 +50,7 @@ public class GatewayNoLoadBalancerClientAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(ReactiveLoadBalancerClientFilter.class)
|
||||
public NoLoadBalancerClientFilter noLoadBalancerClientFilter(
|
||||
LoadBalancerProperties properties) {
|
||||
public NoLoadBalancerClientFilter noLoadBalancerClientFilter(LoadBalancerProperties properties) {
|
||||
return new NoLoadBalancerClientFilter(properties.isUse404());
|
||||
}
|
||||
|
||||
@@ -73,13 +72,11 @@ public class GatewayNoLoadBalancerClientAutoConfiguration {
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
|
||||
String schemePrefix = exchange.getAttribute(GATEWAY_SCHEME_PREFIX_ATTR);
|
||||
if (url == null
|
||||
|| (!"lb".equals(url.getScheme()) && !"lb".equals(schemePrefix))) {
|
||||
if (url == null || (!"lb".equals(url.getScheme()) && !"lb".equals(schemePrefix))) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
throw NotFoundException.create(use404,
|
||||
"Unable to find instance for " + url.getHost());
|
||||
throw NotFoundException.create(use404, "Unable to find instance for " + url.getHost());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -54,8 +54,8 @@ public class GatewayProperties {
|
||||
*/
|
||||
private List<FilterDefinition> defaultFilters = new ArrayList<>();
|
||||
|
||||
private List<MediaType> streamingMediaTypes = Arrays
|
||||
.asList(MediaType.TEXT_EVENT_STREAM, MediaType.APPLICATION_STREAM_JSON);
|
||||
private List<MediaType> streamingMediaTypes = Arrays.asList(MediaType.TEXT_EVENT_STREAM,
|
||||
MediaType.APPLICATION_STREAM_JSON);
|
||||
|
||||
/**
|
||||
* Option to fail on route definition errors, defaults to true. Otherwise, a warning
|
||||
@@ -100,11 +100,9 @@ public class GatewayProperties {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("routes", routes)
|
||||
.append("defaultFilters", defaultFilters)
|
||||
return new ToStringCreator(this).append("routes", routes).append("defaultFilters", defaultFilters)
|
||||
.append("streamingMediaTypes", streamingMediaTypes)
|
||||
.append("failOnRouteDefinitionError", failOnRouteDefinitionError)
|
||||
.toString();
|
||||
.append("failOnRouteDefinitionError", failOnRouteDefinitionError).toString();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -36,8 +36,7 @@ import org.springframework.web.reactive.DispatcherHandler;
|
||||
* @author Olga Maciaszek-Sharma
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass({ ReactiveLoadBalancer.class, LoadBalancerAutoConfiguration.class,
|
||||
DispatcherHandler.class })
|
||||
@ConditionalOnClass({ ReactiveLoadBalancer.class, LoadBalancerAutoConfiguration.class, DispatcherHandler.class })
|
||||
@AutoConfigureAfter(LoadBalancerAutoConfiguration.class)
|
||||
@EnableConfigurationProperties(LoadBalancerProperties.class)
|
||||
public class GatewayReactiveLoadBalancerClientAutoConfiguration {
|
||||
@@ -45,8 +44,8 @@ public class GatewayReactiveLoadBalancerClientAutoConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnBean(LoadBalancerClientFactory.class)
|
||||
@ConditionalOnMissingBean(ReactiveLoadBalancerClientFilter.class)
|
||||
public ReactiveLoadBalancerClientFilter gatewayLoadBalancerClientFilter(
|
||||
LoadBalancerClientFactory clientFactory, LoadBalancerProperties properties) {
|
||||
public ReactiveLoadBalancerClientFilter gatewayLoadBalancerClientFilter(LoadBalancerClientFactory clientFactory,
|
||||
LoadBalancerProperties properties) {
|
||||
return new ReactiveLoadBalancerClientFilter(clientFactory, properties);
|
||||
}
|
||||
|
||||
|
||||
@@ -49,8 +49,8 @@ class GatewayRedisAutoConfiguration {
|
||||
@SuppressWarnings("unchecked")
|
||||
public RedisScript redisRequestRateLimiterScript() {
|
||||
DefaultRedisScript redisScript = new DefaultRedisScript<>();
|
||||
redisScript.setScriptSource(new ResourceScriptSource(
|
||||
new ClassPathResource("META-INF/scripts/request_rate_limiter.lua")));
|
||||
redisScript.setScriptSource(
|
||||
new ResourceScriptSource(new ClassPathResource("META-INF/scripts/request_rate_limiter.lua")));
|
||||
redisScript.setResultType(List.class);
|
||||
return redisScript;
|
||||
}
|
||||
|
||||
@@ -37,9 +37,8 @@ import org.springframework.web.reactive.DispatcherHandler;
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true)
|
||||
@AutoConfigureAfter({ ReactiveResilience4JAutoConfiguration.class })
|
||||
@ConditionalOnClass({ DispatcherHandler.class,
|
||||
ReactiveResilience4JAutoConfiguration.class, ReactiveCircuitBreakerFactory.class,
|
||||
ReactiveResilience4JCircuitBreakerFactory.class })
|
||||
@ConditionalOnClass({ DispatcherHandler.class, ReactiveResilience4JAutoConfiguration.class,
|
||||
ReactiveCircuitBreakerFactory.class, ReactiveResilience4JCircuitBreakerFactory.class })
|
||||
public class GatewayResilience4JCircuitBreakerAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@@ -47,8 +46,7 @@ public class GatewayResilience4JCircuitBreakerAutoConfiguration {
|
||||
public SpringCloudCircuitBreakerResilience4JFilterFactory springCloudCircuitBreakerResilience4JFilterFactory(
|
||||
ReactiveResilience4JCircuitBreakerFactory reactiveCircuitBreakerFactory,
|
||||
ObjectProvider<DispatcherHandler> dispatcherHandler) {
|
||||
return new SpringCloudCircuitBreakerResilience4JFilterFactory(
|
||||
reactiveCircuitBreakerFactory, dispatcherHandler);
|
||||
return new SpringCloudCircuitBreakerResilience4JFilterFactory(reactiveCircuitBreakerFactory, dispatcherHandler);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -248,9 +248,8 @@ public class HttpClientProperties {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Pool{" + "type=" + type + ", name='" + name + '\''
|
||||
+ ", maxConnections=" + maxConnections + ", acquireTimeout="
|
||||
+ acquireTimeout + ", maxIdleTime=" + maxIdleTime + ", maxLifeTime="
|
||||
return "Pool{" + "type=" + type + ", name='" + name + '\'' + ", maxConnections=" + maxConnections
|
||||
+ ", acquireTimeout=" + acquireTimeout + ", maxIdleTime=" + maxIdleTime + ", maxLifeTime="
|
||||
+ maxLifeTime + '}';
|
||||
}
|
||||
|
||||
@@ -337,9 +336,8 @@ public class HttpClientProperties {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Proxy{" + "host='" + host + '\'' + ", port=" + port + ", username='"
|
||||
+ username + '\'' + ", password='" + password + '\''
|
||||
+ ", nonProxyHostsPattern='" + nonProxyHostsPattern + '\'' + '}';
|
||||
return "Proxy{" + "host='" + host + '\'' + ", port=" + port + ", username='" + username + '\''
|
||||
+ ", password='" + password + '\'' + ", nonProxyHostsPattern='" + nonProxyHostsPattern + '\'' + '}';
|
||||
}
|
||||
|
||||
}
|
||||
@@ -433,8 +431,7 @@ public class HttpClientProperties {
|
||||
|
||||
public X509Certificate[] getTrustedX509CertificatesForTrustManager() {
|
||||
try {
|
||||
CertificateFactory certificateFactory = CertificateFactory
|
||||
.getInstance("X.509");
|
||||
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
|
||||
ArrayList<Certificate> allCerts = new ArrayList<>();
|
||||
for (String trustedCert : getTrustedX509Certificates()) {
|
||||
try {
|
||||
@@ -444,15 +441,13 @@ public class HttpClientProperties {
|
||||
allCerts.addAll(certs);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new WebServerException(
|
||||
"Could not load certificate '" + trustedCert + "'", e);
|
||||
throw new WebServerException("Could not load certificate '" + trustedCert + "'", e);
|
||||
}
|
||||
}
|
||||
return allCerts.toArray(new X509Certificate[allCerts.size()]);
|
||||
}
|
||||
catch (CertificateException e1) {
|
||||
throw new WebServerException("Could not load CertificateFactory X.509",
|
||||
e1);
|
||||
throw new WebServerException("Could not load CertificateFactory X.509", e1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -461,8 +456,7 @@ public class HttpClientProperties {
|
||||
if (getKeyStore() != null && getKeyStore().length() > 0) {
|
||||
KeyManagerFactory keyManagerFactory = KeyManagerFactory
|
||||
.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
||||
char[] keyPassword = getKeyPassword() != null
|
||||
? getKeyPassword().toCharArray() : null;
|
||||
char[] keyPassword = getKeyPassword() != null ? getKeyPassword().toCharArray() : null;
|
||||
|
||||
if (keyPassword == null && getKeyStorePassword() != null) {
|
||||
keyPassword = getKeyStorePassword().toCharArray();
|
||||
@@ -487,19 +481,17 @@ public class HttpClientProperties {
|
||||
: KeyStore.getInstance(getKeyStoreType());
|
||||
try {
|
||||
URL url = ResourceUtils.getURL(getKeyStore());
|
||||
store.load(url.openStream(), getKeyStorePassword() != null
|
||||
? getKeyStorePassword().toCharArray() : null);
|
||||
store.load(url.openStream(),
|
||||
getKeyStorePassword() != null ? getKeyStorePassword().toCharArray() : null);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new WebServerException(
|
||||
"Could not load key store ' " + getKeyStore() + "'", e);
|
||||
throw new WebServerException("Could not load key store ' " + getKeyStore() + "'", e);
|
||||
}
|
||||
|
||||
return store;
|
||||
}
|
||||
catch (KeyStoreException | NoSuchProviderException e) {
|
||||
throw new WebServerException(
|
||||
"Could not load KeyStore for given type and provider", e);
|
||||
throw new WebServerException("Could not load KeyStore for given type and provider", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -541,21 +533,18 @@ public class HttpClientProperties {
|
||||
return defaultConfigurationType;
|
||||
}
|
||||
|
||||
public void setDefaultConfigurationType(
|
||||
SslProvider.DefaultConfigurationType defaultConfigurationType) {
|
||||
public void setDefaultConfigurationType(SslProvider.DefaultConfigurationType defaultConfigurationType) {
|
||||
this.defaultConfigurationType = defaultConfigurationType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this)
|
||||
.append("useInsecureTrustManager", useInsecureTrustManager)
|
||||
return new ToStringCreator(this).append("useInsecureTrustManager", useInsecureTrustManager)
|
||||
.append("trustedX509Certificates", trustedX509Certificates)
|
||||
.append("handshakeTimeout", handshakeTimeout)
|
||||
.append("closeNotifyFlushTimeout", closeNotifyFlushTimeout)
|
||||
.append("closeNotifyReadTimeout", closeNotifyReadTimeout)
|
||||
.append("defaultConfigurationType", defaultConfigurationType)
|
||||
.toString();
|
||||
.append("defaultConfigurationType", defaultConfigurationType).toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -586,8 +575,7 @@ public class HttpClientProperties {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this)
|
||||
.append("maxFramePayloadLength", maxFramePayloadLength)
|
||||
return new ToStringCreator(this).append("maxFramePayloadLength", maxFramePayloadLength)
|
||||
.append("proxyPing", proxyPing).toString();
|
||||
}
|
||||
|
||||
|
||||
@@ -32,8 +32,7 @@ import org.springframework.web.reactive.handler.SimpleUrlHandlerMapping;
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(SimpleUrlHandlerMapping.class)
|
||||
@ConditionalOnProperty(
|
||||
name = "spring.cloud.gateway.globalcors.add-to-simple-url-handler-mapping",
|
||||
@ConditionalOnProperty(name = "spring.cloud.gateway.globalcors.add-to-simple-url-handler-mapping",
|
||||
matchIfMissing = false)
|
||||
public class SimpleUrlHandlerMappingGlobalCorsAutoConfiguration {
|
||||
|
||||
@@ -45,8 +44,7 @@ public class SimpleUrlHandlerMappingGlobalCorsAutoConfiguration {
|
||||
|
||||
@PostConstruct
|
||||
void config() {
|
||||
simpleUrlHandlerMapping
|
||||
.setCorsConfigurations(globalCorsProperties.getCorsConfigurations());
|
||||
simpleUrlHandlerMapping.setCorsConfigurations(globalCorsProperties.getCorsConfigurations());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -47,8 +47,7 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
public class DiscoveryClientRouteDefinitionLocator implements RouteDefinitionLocator {
|
||||
|
||||
private static final Log log = LogFactory
|
||||
.getLog(DiscoveryClientRouteDefinitionLocator.class);
|
||||
private static final Log log = LogFactory.getLog(DiscoveryClientRouteDefinitionLocator.class);
|
||||
|
||||
private final DiscoveryLocatorProperties properties;
|
||||
|
||||
@@ -65,8 +64,7 @@ public class DiscoveryClientRouteDefinitionLocator implements RouteDefinitionLoc
|
||||
.flatMap(service -> discoveryClient.getInstances(service).collectList());
|
||||
}
|
||||
|
||||
private DiscoveryClientRouteDefinitionLocator(String discoveryClientName,
|
||||
DiscoveryLocatorProperties properties) {
|
||||
private DiscoveryClientRouteDefinitionLocator(String discoveryClientName, DiscoveryLocatorProperties properties) {
|
||||
this.properties = properties;
|
||||
if (StringUtils.hasText(properties.getRouteIdPrefix())) {
|
||||
routeIdPrefix = properties.getRouteIdPrefix();
|
||||
@@ -74,21 +72,18 @@ public class DiscoveryClientRouteDefinitionLocator implements RouteDefinitionLoc
|
||||
else {
|
||||
routeIdPrefix = discoveryClientName + "_";
|
||||
}
|
||||
evalCtxt = SimpleEvaluationContext.forReadOnlyDataBinding().withInstanceMethods()
|
||||
.build();
|
||||
evalCtxt = SimpleEvaluationContext.forReadOnlyDataBinding().withInstanceMethods().build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<RouteDefinition> getRouteDefinitions() {
|
||||
|
||||
SpelExpressionParser parser = new SpelExpressionParser();
|
||||
Expression includeExpr = parser
|
||||
.parseExpression(properties.getIncludeExpression());
|
||||
Expression includeExpr = parser.parseExpression(properties.getIncludeExpression());
|
||||
Expression urlExpr = parser.parseExpression(properties.getUrlExpression());
|
||||
|
||||
Predicate<ServiceInstance> includePredicate;
|
||||
if (properties.getIncludeExpression() == null
|
||||
|| "true".equalsIgnoreCase(properties.getIncludeExpression())) {
|
||||
if (properties.getIncludeExpression() == null || "true".equalsIgnoreCase(properties.getIncludeExpression())) {
|
||||
includePredicate = instance -> true;
|
||||
}
|
||||
else {
|
||||
@@ -101,22 +96,17 @@ public class DiscoveryClientRouteDefinitionLocator implements RouteDefinitionLoc
|
||||
};
|
||||
}
|
||||
|
||||
return serviceInstances.filter(instances -> !instances.isEmpty())
|
||||
.map(instances -> instances.get(0)).filter(includePredicate)
|
||||
.map(instance -> {
|
||||
RouteDefinition routeDefinition = buildRouteDefinition(urlExpr,
|
||||
instance);
|
||||
return serviceInstances.filter(instances -> !instances.isEmpty()).map(instances -> instances.get(0))
|
||||
.filter(includePredicate).map(instance -> {
|
||||
RouteDefinition routeDefinition = buildRouteDefinition(urlExpr, instance);
|
||||
|
||||
final ServiceInstance instanceForEval = new DelegatingServiceInstance(
|
||||
instance, properties);
|
||||
final ServiceInstance instanceForEval = new DelegatingServiceInstance(instance, properties);
|
||||
|
||||
for (PredicateDefinition original : this.properties.getPredicates()) {
|
||||
PredicateDefinition predicate = new PredicateDefinition();
|
||||
predicate.setName(original.getName());
|
||||
for (Map.Entry<String, String> entry : original.getArgs()
|
||||
.entrySet()) {
|
||||
String value = getValueFromExpr(evalCtxt, parser,
|
||||
instanceForEval, entry);
|
||||
for (Map.Entry<String, String> entry : original.getArgs().entrySet()) {
|
||||
String value = getValueFromExpr(evalCtxt, parser, instanceForEval, entry);
|
||||
predicate.addArg(entry.getKey(), value);
|
||||
}
|
||||
routeDefinition.getPredicates().add(predicate);
|
||||
@@ -125,10 +115,8 @@ public class DiscoveryClientRouteDefinitionLocator implements RouteDefinitionLoc
|
||||
for (FilterDefinition original : this.properties.getFilters()) {
|
||||
FilterDefinition filter = new FilterDefinition();
|
||||
filter.setName(original.getName());
|
||||
for (Map.Entry<String, String> entry : original.getArgs()
|
||||
.entrySet()) {
|
||||
String value = getValueFromExpr(evalCtxt, parser,
|
||||
instanceForEval, entry);
|
||||
for (Map.Entry<String, String> entry : original.getArgs().entrySet()) {
|
||||
String value = getValueFromExpr(evalCtxt, parser, instanceForEval, entry);
|
||||
filter.addArg(entry.getKey(), value);
|
||||
}
|
||||
routeDefinition.getFilters().add(filter);
|
||||
@@ -138,8 +126,7 @@ public class DiscoveryClientRouteDefinitionLocator implements RouteDefinitionLoc
|
||||
});
|
||||
}
|
||||
|
||||
protected RouteDefinition buildRouteDefinition(Expression urlExpr,
|
||||
ServiceInstance serviceInstance) {
|
||||
protected RouteDefinition buildRouteDefinition(Expression urlExpr, ServiceInstance serviceInstance) {
|
||||
String serviceId = serviceInstance.getServiceId();
|
||||
RouteDefinition routeDefinition = new RouteDefinition();
|
||||
routeDefinition.setId(this.routeIdPrefix + serviceId);
|
||||
@@ -150,8 +137,8 @@ public class DiscoveryClientRouteDefinitionLocator implements RouteDefinitionLoc
|
||||
return routeDefinition;
|
||||
}
|
||||
|
||||
String getValueFromExpr(SimpleEvaluationContext evalCtxt, SpelExpressionParser parser,
|
||||
ServiceInstance instance, Map.Entry<String, String> entry) {
|
||||
String getValueFromExpr(SimpleEvaluationContext evalCtxt, SpelExpressionParser parser, ServiceInstance instance,
|
||||
Map.Entry<String, String> entry) {
|
||||
try {
|
||||
Expression valueExpr = parser.parseExpression(entry.getValue());
|
||||
return valueExpr.getValue(evalCtxt, instance, String.class);
|
||||
@@ -170,8 +157,7 @@ public class DiscoveryClientRouteDefinitionLocator implements RouteDefinitionLoc
|
||||
|
||||
private final DiscoveryLocatorProperties properties;
|
||||
|
||||
private DelegatingServiceInstance(ServiceInstance delegate,
|
||||
DiscoveryLocatorProperties properties) {
|
||||
private DelegatingServiceInstance(ServiceInstance delegate, DiscoveryLocatorProperties properties) {
|
||||
this.delegate = delegate;
|
||||
this.properties = properties;
|
||||
}
|
||||
@@ -216,8 +202,7 @@ public class DiscoveryClientRouteDefinitionLocator implements RouteDefinitionLoc
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("delegate", delegate)
|
||||
.append("properties", properties).toString();
|
||||
return new ToStringCreator(this).append("delegate", delegate).append("properties", properties).toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -116,12 +116,10 @@ public class DiscoveryLocatorProperties {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("enabled", enabled)
|
||||
.append("routeIdPrefix", routeIdPrefix)
|
||||
.append("includeExpression", includeExpression)
|
||||
.append("urlExpression", urlExpression)
|
||||
.append("lowerCaseServiceId", lowerCaseServiceId)
|
||||
.append("predicates", predicates).append("filters", filters).toString();
|
||||
return new ToStringCreator(this).append("enabled", enabled).append("routeIdPrefix", routeIdPrefix)
|
||||
.append("includeExpression", includeExpression).append("urlExpression", urlExpression)
|
||||
.append("lowerCaseServiceId", lowerCaseServiceId).append("predicates", predicates)
|
||||
.append("filters", filters).toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -48,8 +48,7 @@ import static org.springframework.cloud.gateway.support.NameUtils.normalizeRoute
|
||||
@ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true)
|
||||
@AutoConfigureBefore(GatewayAutoConfiguration.class)
|
||||
@AutoConfigureAfter(CompositeDiscoveryClientAutoConfiguration.class)
|
||||
@ConditionalOnClass({ DispatcherHandler.class,
|
||||
CompositeDiscoveryClientAutoConfiguration.class })
|
||||
@ConditionalOnClass({ DispatcherHandler.class, CompositeDiscoveryClientAutoConfiguration.class })
|
||||
@EnableConfigurationProperties
|
||||
public class GatewayDiscoveryClientAutoConfiguration {
|
||||
|
||||
@@ -89,15 +88,13 @@ public class GatewayDiscoveryClientAutoConfiguration {
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnProperty(value = "spring.cloud.discovery.reactive.enabled",
|
||||
matchIfMissing = true)
|
||||
@ConditionalOnProperty(value = "spring.cloud.discovery.reactive.enabled", matchIfMissing = true)
|
||||
public static class ReactiveDiscoveryClientRouteDefinitionLocatorConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(name = "spring.cloud.gateway.discovery.locator.enabled")
|
||||
public DiscoveryClientRouteDefinitionLocator discoveryClientRouteDefinitionLocator(
|
||||
ReactiveDiscoveryClient discoveryClient,
|
||||
DiscoveryLocatorProperties properties) {
|
||||
ReactiveDiscoveryClient discoveryClient, DiscoveryLocatorProperties properties) {
|
||||
return new DiscoveryClientRouteDefinitionLocator(discoveryClient, properties);
|
||||
}
|
||||
|
||||
|
||||
@@ -34,8 +34,7 @@ import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.C
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.CACHED_SERVER_HTTP_REQUEST_DECORATOR_ATTR;
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR;
|
||||
|
||||
public class AdaptCachedBodyGlobalFilter
|
||||
implements GlobalFilter, Ordered, ApplicationListener<EnableBodyCachingEvent> {
|
||||
public class AdaptCachedBodyGlobalFilter implements GlobalFilter, Ordered, ApplicationListener<EnableBodyCachingEvent> {
|
||||
|
||||
private ConcurrentMap<String, Boolean> routesToCache = new ConcurrentHashMap<>();
|
||||
|
||||
@@ -49,8 +48,8 @@ public class AdaptCachedBodyGlobalFilter
|
||||
// the cached ServerHttpRequest is used when the ServerWebExchange can not be
|
||||
// mutated, for example, during a predicate where the body is read, but still
|
||||
// needs to be cached.
|
||||
ServerHttpRequest cachedRequest = exchange
|
||||
.getAttributeOrDefault(CACHED_SERVER_HTTP_REQUEST_DECORATOR_ATTR, null);
|
||||
ServerHttpRequest cachedRequest = exchange.getAttributeOrDefault(CACHED_SERVER_HTTP_REQUEST_DECORATOR_ATTR,
|
||||
null);
|
||||
if (cachedRequest != null) {
|
||||
exchange.getAttributes().remove(CACHED_SERVER_HTTP_REQUEST_DECORATOR_ATTR);
|
||||
return chain.filter(exchange.mutate().request(cachedRequest).build());
|
||||
|
||||
@@ -43,9 +43,7 @@ public class ForwardPathFilter implements GlobalFilter, Ordered {
|
||||
if (isAlreadyRouted(exchange) || !"forward".equals(scheme)) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
exchange = exchange.mutate()
|
||||
.request(exchange.getRequest().mutate().path(routeUri.getPath()).build())
|
||||
.build();
|
||||
exchange = exchange.mutate().request(exchange.getRequest().mutate().path(routeUri.getPath()).build()).build();
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
|
||||
@@ -39,8 +39,7 @@ public class ForwardRoutingFilter implements GlobalFilter, Ordered {
|
||||
// do not use this dispatcherHandler directly, use getDispatcherHandler() instead.
|
||||
private volatile DispatcherHandler dispatcherHandler;
|
||||
|
||||
public ForwardRoutingFilter(
|
||||
ObjectProvider<DispatcherHandler> dispatcherHandlerProvider) {
|
||||
public ForwardRoutingFilter(ObjectProvider<DispatcherHandler> dispatcherHandlerProvider) {
|
||||
this.dispatcherHandlerProvider = dispatcherHandlerProvider;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,11 +43,9 @@ public class GatewayMetricsFilter implements GlobalFilter, Ordered {
|
||||
|
||||
private GatewayTagsProvider compositeTagsProvider;
|
||||
|
||||
public GatewayMetricsFilter(MeterRegistry meterRegistry,
|
||||
List<GatewayTagsProvider> tagsProviders) {
|
||||
public GatewayMetricsFilter(MeterRegistry meterRegistry, List<GatewayTagsProvider> tagsProviders) {
|
||||
this.meterRegistry = meterRegistry;
|
||||
this.compositeTagsProvider = tagsProviders.stream()
|
||||
.reduce(exchange -> Tags.empty(), GatewayTagsProvider::and);
|
||||
this.compositeTagsProvider = tagsProviders.stream().reduce(exchange -> Tags.empty(), GatewayTagsProvider::and);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -61,8 +59,7 @@ public class GatewayMetricsFilter implements GlobalFilter, Ordered {
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
Sample sample = Timer.start(meterRegistry);
|
||||
|
||||
return chain.filter(exchange)
|
||||
.doOnSuccess(aVoid -> endTimerRespectingCommit(exchange, sample))
|
||||
return chain.filter(exchange).doOnSuccess(aVoid -> endTimerRespectingCommit(exchange, sample))
|
||||
.doOnError(throwable -> endTimerRespectingCommit(exchange, sample));
|
||||
}
|
||||
|
||||
|
||||
@@ -82,8 +82,7 @@ public class NettyRoutingFilter implements GlobalFilter, Ordered {
|
||||
// do not use this headersFilters directly, use getHeadersFilters() instead.
|
||||
private volatile List<HttpHeadersFilter> headersFilters;
|
||||
|
||||
public NettyRoutingFilter(HttpClient httpClient,
|
||||
ObjectProvider<List<HttpHeadersFilter>> headersFiltersProvider,
|
||||
public NettyRoutingFilter(HttpClient httpClient, ObjectProvider<List<HttpHeadersFilter>> headersFiltersProvider,
|
||||
HttpClientProperties properties) {
|
||||
this.httpClient = httpClient;
|
||||
this.headersFiltersProvider = headersFiltersProvider;
|
||||
@@ -108,8 +107,7 @@ public class NettyRoutingFilter implements GlobalFilter, Ordered {
|
||||
URI requestUrl = exchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR);
|
||||
|
||||
String scheme = requestUrl.getScheme();
|
||||
if (isAlreadyRouted(exchange)
|
||||
|| (!"http".equals(scheme) && !"https".equals(scheme))) {
|
||||
if (isAlreadyRouted(exchange) || (!"http".equals(scheme) && !"https".equals(scheme))) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
setAlreadyRouted(exchange);
|
||||
@@ -124,82 +122,72 @@ public class NettyRoutingFilter implements GlobalFilter, Ordered {
|
||||
final DefaultHttpHeaders httpHeaders = new DefaultHttpHeaders();
|
||||
filtered.forEach(httpHeaders::set);
|
||||
|
||||
boolean preserveHost = exchange
|
||||
.getAttributeOrDefault(PRESERVE_HOST_HEADER_ATTRIBUTE, false);
|
||||
boolean preserveHost = exchange.getAttributeOrDefault(PRESERVE_HOST_HEADER_ATTRIBUTE, false);
|
||||
Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
|
||||
|
||||
Flux<HttpClientResponse> responseFlux = getHttpClient(route, exchange)
|
||||
.headers(headers -> {
|
||||
headers.add(httpHeaders);
|
||||
// Will either be set below, or later by Netty
|
||||
headers.remove(HttpHeaders.HOST);
|
||||
if (preserveHost) {
|
||||
String host = request.getHeaders().getFirst(HttpHeaders.HOST);
|
||||
headers.add(HttpHeaders.HOST, host);
|
||||
}
|
||||
}).request(method).uri(url).send((req, nettyOutbound) -> {
|
||||
if (log.isTraceEnabled()) {
|
||||
nettyOutbound
|
||||
.withConnection(connection -> log.trace("outbound route: "
|
||||
+ connection.channel().id().asShortText()
|
||||
+ ", inbound: " + exchange.getLogPrefix()));
|
||||
}
|
||||
return nettyOutbound.send(request.getBody().map(this::getByteBuf));
|
||||
}).responseConnection((res, connection) -> {
|
||||
Flux<HttpClientResponse> responseFlux = getHttpClient(route, exchange).headers(headers -> {
|
||||
headers.add(httpHeaders);
|
||||
// Will either be set below, or later by Netty
|
||||
headers.remove(HttpHeaders.HOST);
|
||||
if (preserveHost) {
|
||||
String host = request.getHeaders().getFirst(HttpHeaders.HOST);
|
||||
headers.add(HttpHeaders.HOST, host);
|
||||
}
|
||||
}).request(method).uri(url).send((req, nettyOutbound) -> {
|
||||
if (log.isTraceEnabled()) {
|
||||
nettyOutbound.withConnection(connection -> log.trace("outbound route: "
|
||||
+ connection.channel().id().asShortText() + ", inbound: " + exchange.getLogPrefix()));
|
||||
}
|
||||
return nettyOutbound.send(request.getBody().map(this::getByteBuf));
|
||||
}).responseConnection((res, connection) -> {
|
||||
|
||||
// Defer committing the response until all route filters have run
|
||||
// Put client response as ServerWebExchange attribute and write
|
||||
// response later NettyWriteResponseFilter
|
||||
exchange.getAttributes().put(CLIENT_RESPONSE_ATTR, res);
|
||||
exchange.getAttributes().put(CLIENT_RESPONSE_CONN_ATTR, connection);
|
||||
// Defer committing the response until all route filters have run
|
||||
// Put client response as ServerWebExchange attribute and write
|
||||
// response later NettyWriteResponseFilter
|
||||
exchange.getAttributes().put(CLIENT_RESPONSE_ATTR, res);
|
||||
exchange.getAttributes().put(CLIENT_RESPONSE_CONN_ATTR, connection);
|
||||
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
// put headers and status so filters can modify the response
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
// put headers and status so filters can modify the response
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
|
||||
res.responseHeaders().forEach(
|
||||
entry -> headers.add(entry.getKey(), entry.getValue()));
|
||||
res.responseHeaders().forEach(entry -> headers.add(entry.getKey(), entry.getValue()));
|
||||
|
||||
String contentTypeValue = headers.getFirst(HttpHeaders.CONTENT_TYPE);
|
||||
if (StringUtils.hasLength(contentTypeValue)) {
|
||||
exchange.getAttributes().put(ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR,
|
||||
contentTypeValue);
|
||||
}
|
||||
String contentTypeValue = headers.getFirst(HttpHeaders.CONTENT_TYPE);
|
||||
if (StringUtils.hasLength(contentTypeValue)) {
|
||||
exchange.getAttributes().put(ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR, contentTypeValue);
|
||||
}
|
||||
|
||||
setResponseStatus(res, response);
|
||||
setResponseStatus(res, response);
|
||||
|
||||
// make sure headers filters run after setting status so it is
|
||||
// available in response
|
||||
HttpHeaders filteredResponseHeaders = HttpHeadersFilter.filter(
|
||||
getHeadersFilters(), headers, exchange, Type.RESPONSE);
|
||||
// make sure headers filters run after setting status so it is
|
||||
// available in response
|
||||
HttpHeaders filteredResponseHeaders = HttpHeadersFilter.filter(getHeadersFilters(), headers, exchange,
|
||||
Type.RESPONSE);
|
||||
|
||||
if (!filteredResponseHeaders
|
||||
.containsKey(HttpHeaders.TRANSFER_ENCODING)
|
||||
&& filteredResponseHeaders
|
||||
.containsKey(HttpHeaders.CONTENT_LENGTH)) {
|
||||
// It is not valid to have both the transfer-encoding header and
|
||||
// the content-length header.
|
||||
// Remove the transfer-encoding header in the response if the
|
||||
// content-length header is present.
|
||||
response.getHeaders().remove(HttpHeaders.TRANSFER_ENCODING);
|
||||
}
|
||||
if (!filteredResponseHeaders.containsKey(HttpHeaders.TRANSFER_ENCODING)
|
||||
&& filteredResponseHeaders.containsKey(HttpHeaders.CONTENT_LENGTH)) {
|
||||
// It is not valid to have both the transfer-encoding header and
|
||||
// the content-length header.
|
||||
// Remove the transfer-encoding header in the response if the
|
||||
// content-length header is present.
|
||||
response.getHeaders().remove(HttpHeaders.TRANSFER_ENCODING);
|
||||
}
|
||||
|
||||
exchange.getAttributes().put(CLIENT_RESPONSE_HEADER_NAMES,
|
||||
filteredResponseHeaders.keySet());
|
||||
exchange.getAttributes().put(CLIENT_RESPONSE_HEADER_NAMES, filteredResponseHeaders.keySet());
|
||||
|
||||
response.getHeaders().putAll(filteredResponseHeaders);
|
||||
response.getHeaders().putAll(filteredResponseHeaders);
|
||||
|
||||
return Mono.just(res);
|
||||
});
|
||||
return Mono.just(res);
|
||||
});
|
||||
|
||||
Duration responseTimeout = getResponseTimeout(route);
|
||||
if (responseTimeout != null) {
|
||||
responseFlux = responseFlux
|
||||
.timeout(responseTimeout, Mono.error(new TimeoutException(
|
||||
"Response took longer than timeout: " + responseTimeout)))
|
||||
.timeout(responseTimeout,
|
||||
Mono.error(new TimeoutException("Response took longer than timeout: " + responseTimeout)))
|
||||
.onErrorMap(TimeoutException.class,
|
||||
th -> new ResponseStatusException(HttpStatus.GATEWAY_TIMEOUT,
|
||||
th.getMessage(), th));
|
||||
th -> new ResponseStatusException(HttpStatus.GATEWAY_TIMEOUT, th.getMessage(), th));
|
||||
}
|
||||
|
||||
return responseFlux.then(chain.filter(exchange));
|
||||
@@ -215,12 +203,10 @@ public class NettyRoutingFilter implements GlobalFilter, Ordered {
|
||||
DefaultDataBuffer buffer = (DefaultDataBuffer) dataBuffer;
|
||||
return Unpooled.wrappedBuffer(buffer.getNativeBuffer());
|
||||
}
|
||||
throw new IllegalArgumentException(
|
||||
"Unable to handle DataBuffer of type " + dataBuffer.getClass());
|
||||
throw new IllegalArgumentException("Unable to handle DataBuffer of type " + dataBuffer.getClass());
|
||||
}
|
||||
|
||||
private void setResponseStatus(HttpClientResponse clientResponse,
|
||||
ServerHttpResponse response) {
|
||||
private void setResponseStatus(HttpClientResponse clientResponse, ServerHttpResponse response) {
|
||||
HttpStatus status = HttpStatus.resolve(clientResponse.status().code());
|
||||
if (status != null) {
|
||||
response.setStatusCode(status);
|
||||
@@ -230,14 +216,12 @@ public class NettyRoutingFilter implements GlobalFilter, Ordered {
|
||||
response = ((ServerHttpResponseDecorator) response).getDelegate();
|
||||
}
|
||||
if (response instanceof AbstractServerHttpResponse) {
|
||||
((AbstractServerHttpResponse) response)
|
||||
.setStatusCodeValue(clientResponse.status().code());
|
||||
((AbstractServerHttpResponse) response).setStatusCodeValue(clientResponse.status().code());
|
||||
}
|
||||
else {
|
||||
// TODO: log warning here, not throw error?
|
||||
throw new IllegalStateException("Unable to set status code "
|
||||
+ clientResponse.status().code() + " on response of type "
|
||||
+ response.getClass().getName());
|
||||
throw new IllegalStateException("Unable to set status code " + clientResponse.status().code()
|
||||
+ " on response of type " + response.getClass().getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -255,8 +239,8 @@ public class NettyRoutingFilter implements GlobalFilter, Ordered {
|
||||
Object connectTimeoutAttr = route.getMetadata().get(CONNECT_TIMEOUT_ATTR);
|
||||
if (connectTimeoutAttr != null) {
|
||||
Integer connectTimeout = getInteger(connectTimeoutAttr);
|
||||
return this.httpClient.tcpConfiguration((tcpClient) -> tcpClient
|
||||
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, connectTimeout));
|
||||
return this.httpClient.tcpConfiguration(
|
||||
(tcpClient) -> tcpClient.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, connectTimeout));
|
||||
}
|
||||
return httpClient;
|
||||
}
|
||||
@@ -283,8 +267,7 @@ public class NettyRoutingFilter implements GlobalFilter, Ordered {
|
||||
responseTimeout = Long.valueOf(responseTimeoutAttr.toString());
|
||||
}
|
||||
}
|
||||
return responseTimeout != null ? Duration.ofMillis(responseTimeout)
|
||||
: properties.getResponseTimeout();
|
||||
return responseTimeout != null ? Duration.ofMillis(responseTimeout) : properties.getResponseTimeout();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -104,18 +104,15 @@ public class NettyWriteResponseFilter implements GlobalFilter, Ordered {
|
||||
|
||||
protected DataBuffer wrap(ByteBuf byteBuf, ServerHttpResponse response) {
|
||||
if (response.bufferFactory() instanceof NettyDataBufferFactory) {
|
||||
NettyDataBufferFactory factory = (NettyDataBufferFactory) response
|
||||
.bufferFactory();
|
||||
NettyDataBufferFactory factory = (NettyDataBufferFactory) response.bufferFactory();
|
||||
return factory.wrap(byteBuf);
|
||||
}
|
||||
// MockServerHttpResponse creates these
|
||||
else if (response.bufferFactory() instanceof DefaultDataBufferFactory) {
|
||||
DefaultDataBufferFactory factory = (DefaultDataBufferFactory) response
|
||||
.bufferFactory();
|
||||
DefaultDataBufferFactory factory = (DefaultDataBufferFactory) response.bufferFactory();
|
||||
return factory.wrap(byteBuf.nioBuffer());
|
||||
}
|
||||
throw new IllegalArgumentException(
|
||||
"Unkown DataBufferFactory type " + response.bufferFactory().getClass());
|
||||
throw new IllegalArgumentException("Unkown DataBufferFactory type " + response.bufferFactory().getClass());
|
||||
}
|
||||
|
||||
private void cleanup(ServerWebExchange exchange) {
|
||||
@@ -128,8 +125,7 @@ public class NettyWriteResponseFilter implements GlobalFilter, Ordered {
|
||||
// TODO: use framework if possible
|
||||
// TODO: port to WebClientWriteResponseFilter
|
||||
private boolean isStreamingMediaType(@Nullable MediaType contentType) {
|
||||
return (contentType != null && this.streamingMediaTypes.stream()
|
||||
.anyMatch(contentType::isCompatibleWith));
|
||||
return (contentType != null && this.streamingMediaTypes.stream().anyMatch(contentType::isCompatibleWith));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -51,8 +51,7 @@ public class OrderedGatewayFilter implements GatewayFilter, Ordered {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new StringBuilder("[").append(delegate).append(", order = ").append(order)
|
||||
.append("]").toString();
|
||||
return new StringBuilder("[").append(delegate).append(", order = ").append(order).append("]").toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -50,8 +50,7 @@ import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.a
|
||||
*/
|
||||
public class ReactiveLoadBalancerClientFilter implements GlobalFilter, Ordered {
|
||||
|
||||
private static final Log log = LogFactory
|
||||
.getLog(ReactiveLoadBalancerClientFilter.class);
|
||||
private static final Log log = LogFactory.getLog(ReactiveLoadBalancerClientFilter.class);
|
||||
|
||||
/**
|
||||
* Order of filter.
|
||||
@@ -78,23 +77,20 @@ public class ReactiveLoadBalancerClientFilter implements GlobalFilter, Ordered {
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
|
||||
String schemePrefix = exchange.getAttribute(GATEWAY_SCHEME_PREFIX_ATTR);
|
||||
if (url == null
|
||||
|| (!"lb".equals(url.getScheme()) && !"lb".equals(schemePrefix))) {
|
||||
if (url == null || (!"lb".equals(url.getScheme()) && !"lb".equals(schemePrefix))) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
// preserve the original url
|
||||
addOriginalRequestUrl(exchange, url);
|
||||
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace(ReactiveLoadBalancerClientFilter.class.getSimpleName()
|
||||
+ " url before: " + url);
|
||||
log.trace(ReactiveLoadBalancerClientFilter.class.getSimpleName() + " url before: " + url);
|
||||
}
|
||||
|
||||
return choose(exchange).doOnNext(response -> {
|
||||
|
||||
if (!response.hasServer()) {
|
||||
throw NotFoundException.create(properties.isUse404(),
|
||||
"Unable to find instance for " + url.getHost());
|
||||
throw NotFoundException.create(properties.isUse404(), "Unable to find instance for " + url.getHost());
|
||||
}
|
||||
|
||||
URI uri = exchange.getRequest().getURI();
|
||||
@@ -106,8 +102,8 @@ public class ReactiveLoadBalancerClientFilter implements GlobalFilter, Ordered {
|
||||
overrideScheme = url.getScheme();
|
||||
}
|
||||
|
||||
DelegatingServiceInstance serviceInstance = new DelegatingServiceInstance(
|
||||
response.getServer(), overrideScheme);
|
||||
DelegatingServiceInstance serviceInstance = new DelegatingServiceInstance(response.getServer(),
|
||||
overrideScheme);
|
||||
|
||||
URI requestUrl = reconstructURI(serviceInstance, uri);
|
||||
|
||||
@@ -125,8 +121,8 @@ public class ReactiveLoadBalancerClientFilter implements GlobalFilter, Ordered {
|
||||
@SuppressWarnings("deprecation")
|
||||
private Mono<Response<ServiceInstance>> choose(ServerWebExchange exchange) {
|
||||
URI uri = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
|
||||
ReactorLoadBalancer<ServiceInstance> loadBalancer = this.clientFactory
|
||||
.getInstance(uri.getHost(), ReactorServiceInstanceLoadBalancer.class);
|
||||
ReactorLoadBalancer<ServiceInstance> loadBalancer = this.clientFactory.getInstance(uri.getHost(),
|
||||
ReactorServiceInstanceLoadBalancer.class);
|
||||
if (loadBalancer == null) {
|
||||
throw new NotFoundException("No loadbalancer available for " + uri.getHost());
|
||||
}
|
||||
|
||||
@@ -50,8 +50,8 @@ public class RouteToRequestUrlFilter implements GlobalFilter, Ordered {
|
||||
|
||||
/* for testing */
|
||||
static boolean hasAnotherScheme(URI uri) {
|
||||
return schemePattern.matcher(uri.getSchemeSpecificPart()).matches()
|
||||
&& uri.getHost() == null && uri.getRawPath() == null;
|
||||
return schemePattern.matcher(uri.getSchemeSpecificPart()).matches() && uri.getHost() == null
|
||||
&& uri.getRawPath() == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -73,8 +73,7 @@ public class RouteToRequestUrlFilter implements GlobalFilter, Ordered {
|
||||
if (hasAnotherScheme(routeUri)) {
|
||||
// this is a special url, save scheme to special attribute
|
||||
// replace routeUri with schemeSpecificPart
|
||||
exchange.getAttributes().put(GATEWAY_SCHEME_PREFIX_ATTR,
|
||||
routeUri.getScheme());
|
||||
exchange.getAttributes().put(GATEWAY_SCHEME_PREFIX_ATTR, routeUri.getScheme());
|
||||
routeUri = URI.create(routeUri.getSchemeSpecificPart());
|
||||
}
|
||||
|
||||
@@ -88,8 +87,7 @@ public class RouteToRequestUrlFilter implements GlobalFilter, Ordered {
|
||||
|
||||
URI mergedUrl = UriComponentsBuilder.fromUri(uri)
|
||||
// .uri(routeUri)
|
||||
.scheme(routeUri.getScheme()).host(routeUri.getHost())
|
||||
.port(routeUri.getPort()).build(encoded).toUri();
|
||||
.scheme(routeUri.getScheme()).host(routeUri.getHost()).port(routeUri.getPort()).build(encoded).toUri();
|
||||
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, mergedUrl);
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
@@ -76,8 +76,7 @@ public class WebClientHttpRoutingFilter implements GlobalFilter, Ordered {
|
||||
URI requestUrl = exchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR);
|
||||
|
||||
String scheme = requestUrl.getScheme();
|
||||
if (isAlreadyRouted(exchange)
|
||||
|| (!"http".equals(scheme) && !"https".equals(scheme))) {
|
||||
if (isAlreadyRouted(exchange) || (!"http".equals(scheme) && !"https".equals(scheme))) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
setAlreadyRouted(exchange);
|
||||
@@ -88,17 +87,15 @@ public class WebClientHttpRoutingFilter implements GlobalFilter, Ordered {
|
||||
|
||||
HttpHeaders filteredHeaders = filterRequest(getHeadersFilters(), exchange);
|
||||
|
||||
boolean preserveHost = exchange
|
||||
.getAttributeOrDefault(PRESERVE_HOST_HEADER_ATTRIBUTE, false);
|
||||
boolean preserveHost = exchange.getAttributeOrDefault(PRESERVE_HOST_HEADER_ATTRIBUTE, false);
|
||||
|
||||
RequestBodySpec bodySpec = this.webClient.method(method).uri(requestUrl)
|
||||
.headers(httpHeaders -> {
|
||||
httpHeaders.addAll(filteredHeaders);
|
||||
// TODO: can this support preserviceHostHeader?
|
||||
if (!preserveHost) {
|
||||
httpHeaders.remove(HttpHeaders.HOST);
|
||||
}
|
||||
});
|
||||
RequestBodySpec bodySpec = this.webClient.method(method).uri(requestUrl).headers(httpHeaders -> {
|
||||
httpHeaders.addAll(filteredHeaders);
|
||||
// TODO: can this support preserviceHostHeader?
|
||||
if (!preserveHost) {
|
||||
httpHeaders.remove(HttpHeaders.HOST);
|
||||
}
|
||||
});
|
||||
|
||||
RequestHeadersSpec<?> headersSpec;
|
||||
if (requiresBody(method)) {
|
||||
|
||||
@@ -49,22 +49,18 @@ public class WebClientWriteResponseFilter implements GlobalFilter, Ordered {
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
// NOTICE: nothing in "pre" filter stage as CLIENT_RESPONSE_ATTR is not added
|
||||
// until the WebHandler is run
|
||||
return chain.filter(exchange).doOnError(throwable -> cleanup(exchange))
|
||||
.then(Mono.defer(() -> {
|
||||
ClientResponse clientResponse = exchange
|
||||
.getAttribute(CLIENT_RESPONSE_ATTR);
|
||||
if (clientResponse == null) {
|
||||
return Mono.empty();
|
||||
}
|
||||
log.trace("WebClientWriteResponseFilter start");
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
return chain.filter(exchange).doOnError(throwable -> cleanup(exchange)).then(Mono.defer(() -> {
|
||||
ClientResponse clientResponse = exchange.getAttribute(CLIENT_RESPONSE_ATTR);
|
||||
if (clientResponse == null) {
|
||||
return Mono.empty();
|
||||
}
|
||||
log.trace("WebClientWriteResponseFilter start");
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
|
||||
return response
|
||||
.writeWith(
|
||||
clientResponse.body(BodyExtractors.toDataBuffers()))
|
||||
// .log("webClient response")
|
||||
.doOnCancel(() -> cleanup(exchange));
|
||||
}));
|
||||
return response.writeWith(clientResponse.body(BodyExtractors.toDataBuffers()))
|
||||
// .log("webClient response")
|
||||
.doOnCancel(() -> cleanup(exchange));
|
||||
}));
|
||||
}
|
||||
|
||||
private void cleanup(ServerWebExchange exchange) {
|
||||
|
||||
@@ -68,8 +68,7 @@ public class WebsocketRoutingFilter implements GlobalFilter, Ordered {
|
||||
// do not use this headersFilters directly, use getHeadersFilters() instead.
|
||||
private volatile List<HttpHeadersFilter> headersFilters;
|
||||
|
||||
public WebsocketRoutingFilter(WebSocketClient webSocketClient,
|
||||
WebSocketService webSocketService,
|
||||
public WebsocketRoutingFilter(WebSocketClient webSocketClient, WebSocketService webSocketService,
|
||||
ObjectProvider<List<HttpHeadersFilter>> headersFiltersProvider) {
|
||||
this.webSocketClient = webSocketClient;
|
||||
this.webSocketService = webSocketService;
|
||||
@@ -95,8 +94,7 @@ public class WebsocketRoutingFilter implements GlobalFilter, Ordered {
|
||||
URI requestUrl = exchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR);
|
||||
String scheme = requestUrl.getScheme();
|
||||
|
||||
if (isAlreadyRouted(exchange)
|
||||
|| (!"ws".equals(scheme) && !"wss".equals(scheme))) {
|
||||
if (isAlreadyRouted(exchange) || (!"ws".equals(scheme) && !"wss".equals(scheme))) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
setAlreadyRouted(exchange);
|
||||
@@ -106,27 +104,23 @@ public class WebsocketRoutingFilter implements GlobalFilter, Ordered {
|
||||
|
||||
List<String> protocols = headers.get(SEC_WEBSOCKET_PROTOCOL);
|
||||
if (protocols != null) {
|
||||
protocols = headers.get(SEC_WEBSOCKET_PROTOCOL).stream().flatMap(
|
||||
header -> Arrays.stream(commaDelimitedListToStringArray(header)))
|
||||
.map(String::trim).collect(Collectors.toList());
|
||||
protocols = headers.get(SEC_WEBSOCKET_PROTOCOL).stream()
|
||||
.flatMap(header -> Arrays.stream(commaDelimitedListToStringArray(header))).map(String::trim)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
return this.webSocketService.handleRequest(exchange, new ProxyWebSocketHandler(
|
||||
requestUrl, this.webSocketClient, filtered, protocols));
|
||||
return this.webSocketService.handleRequest(exchange,
|
||||
new ProxyWebSocketHandler(requestUrl, this.webSocketClient, filtered, protocols));
|
||||
}
|
||||
|
||||
private List<HttpHeadersFilter> getHeadersFilters() {
|
||||
if (this.headersFilters == null) {
|
||||
this.headersFilters = this.headersFiltersProvider
|
||||
.getIfAvailable(ArrayList::new);
|
||||
this.headersFilters = this.headersFiltersProvider.getIfAvailable(ArrayList::new);
|
||||
|
||||
headersFilters.add((headers, exchange) -> {
|
||||
HttpHeaders filtered = new HttpHeaders();
|
||||
headers.entrySet().stream()
|
||||
.filter(entry -> !entry.getKey().toLowerCase()
|
||||
.startsWith("sec-websocket"))
|
||||
.forEach(header -> filtered.addAll(header.getKey(),
|
||||
header.getValue()));
|
||||
headers.entrySet().stream().filter(entry -> !entry.getKey().toLowerCase().startsWith("sec-websocket"))
|
||||
.forEach(header -> filtered.addAll(header.getKey(), header.getValue()));
|
||||
return filtered;
|
||||
});
|
||||
}
|
||||
@@ -140,12 +134,10 @@ public class WebsocketRoutingFilter implements GlobalFilter, Ordered {
|
||||
String scheme = requestUrl.getScheme().toLowerCase();
|
||||
String upgrade = exchange.getRequest().getHeaders().getUpgrade();
|
||||
// change the scheme if the socket client send a "http" or "https"
|
||||
if ("WebSocket".equalsIgnoreCase(upgrade)
|
||||
&& ("http".equals(scheme) || "https".equals(scheme))) {
|
||||
if ("WebSocket".equalsIgnoreCase(upgrade) && ("http".equals(scheme) || "https".equals(scheme))) {
|
||||
String wsScheme = convertHttpToWs(scheme);
|
||||
boolean encoded = containsEncodedParts(requestUrl);
|
||||
URI wsRequestUrl = UriComponentsBuilder.fromUri(requestUrl).scheme(wsScheme)
|
||||
.build(encoded).toUri();
|
||||
URI wsRequestUrl = UriComponentsBuilder.fromUri(requestUrl).scheme(wsScheme).build(encoded).toUri();
|
||||
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, wsRequestUrl);
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("changeSchemeTo:[" + wsRequestUrl + "]");
|
||||
@@ -163,8 +155,7 @@ public class WebsocketRoutingFilter implements GlobalFilter, Ordered {
|
||||
|
||||
private final List<String> subProtocols;
|
||||
|
||||
ProxyWebSocketHandler(URI url, WebSocketClient client, HttpHeaders headers,
|
||||
List<String> protocols) {
|
||||
ProxyWebSocketHandler(URI url, WebSocketClient client, HttpHeaders headers, List<String> protocols) {
|
||||
this.client = client;
|
||||
this.url = url;
|
||||
this.headers = headers;
|
||||
@@ -191,8 +182,8 @@ public class WebsocketRoutingFilter implements GlobalFilter, Ordered {
|
||||
Mono<Void> proxySessionSend = proxySession
|
||||
.send(session.receive().doOnNext(WebSocketMessage::retain));
|
||||
// .log("proxySessionSend", Level.FINE);
|
||||
Mono<Void> serverSessionSend = session.send(
|
||||
proxySession.receive().doOnNext(WebSocketMessage::retain));
|
||||
Mono<Void> serverSessionSend = session
|
||||
.send(proxySession.receive().doOnNext(WebSocketMessage::retain));
|
||||
// .log("sessionSend", Level.FINE);
|
||||
return Mono.zip(proxySessionSend, serverSessionSend).then();
|
||||
}
|
||||
|
||||
@@ -49,8 +49,7 @@ import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.W
|
||||
* @author Spencer Gibb
|
||||
* @author Alexey Nakidkin
|
||||
*/
|
||||
public class WeightCalculatorWebFilter
|
||||
implements WebFilter, Ordered, SmartApplicationListener {
|
||||
public class WeightCalculatorWebFilter implements WebFilter, Ordered, SmartApplicationListener {
|
||||
|
||||
/**
|
||||
* Order of Weight Calculator Web filter.
|
||||
@@ -138,15 +137,13 @@ public class WeightCalculatorWebFilter
|
||||
|
||||
WeightConfig config = new WeightConfig(event.getRouteId());
|
||||
|
||||
this.configurationService.with(config).name(WeightConfig.CONFIG_PREFIX)
|
||||
.normalizedProperties(args).bind();
|
||||
this.configurationService.with(config).name(WeightConfig.CONFIG_PREFIX).normalizedProperties(args).bind();
|
||||
|
||||
addWeightConfig(config);
|
||||
}
|
||||
|
||||
private boolean hasRelevantKey(Map<String, Object> args) {
|
||||
return args.keySet().stream()
|
||||
.anyMatch(key -> key.startsWith(WeightConfig.CONFIG_PREFIX + "."));
|
||||
return args.keySet().stream().anyMatch(key -> key.startsWith(WeightConfig.CONFIG_PREFIX + "."));
|
||||
}
|
||||
|
||||
/* for testing */ void addWeightConfig(WeightConfig weightConfig) {
|
||||
@@ -227,8 +224,7 @@ public class WeightCalculatorWebFilter
|
||||
List<Double> ranges = config.ranges;
|
||||
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Weight for group: " + group + ", ranges: " + ranges + ", r: "
|
||||
+ r);
|
||||
log.trace("Weight for group: " + group + ", ranges: " + ranges + ", r: " + r);
|
||||
}
|
||||
|
||||
for (int i = 0; i < ranges.size() - 1; i++) {
|
||||
@@ -272,10 +268,8 @@ public class WeightCalculatorWebFilter
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("group", group)
|
||||
.append("weights", weights)
|
||||
.append("normalizedWeights", normalizedWeights)
|
||||
.append("rangeIndexes", rangeIndexes).toString();
|
||||
return new ToStringCreator(this).append("group", group).append("weights", weights)
|
||||
.append("normalizedWeights", normalizedWeights).append("rangeIndexes", rangeIndexes).toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,8 +33,7 @@ import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.G
|
||||
*
|
||||
* @author Toshiaki Maki
|
||||
*/
|
||||
public abstract class AbstractChangeRequestUriGatewayFilterFactory<T>
|
||||
extends AbstractGatewayFilterFactory<T> {
|
||||
public abstract class AbstractChangeRequestUriGatewayFilterFactory<T> extends AbstractGatewayFilterFactory<T> {
|
||||
|
||||
private final int order;
|
||||
|
||||
@@ -47,8 +46,7 @@ public abstract class AbstractChangeRequestUriGatewayFilterFactory<T>
|
||||
this(clazz, RouteToRequestUrlFilter.ROUTE_TO_URL_FILTER_ORDER + 1);
|
||||
}
|
||||
|
||||
protected abstract Optional<URI> determineRequestUri(ServerWebExchange exchange,
|
||||
T config);
|
||||
protected abstract Optional<URI> determineRequestUri(ServerWebExchange exchange, T config);
|
||||
|
||||
public GatewayFilter apply(T config) {
|
||||
return new OrderedGatewayFilter((exchange, chain) -> {
|
||||
|
||||
@@ -25,8 +25,8 @@ import org.springframework.cloud.gateway.filter.GatewayFilter;
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
public abstract class AbstractNameValueGatewayFilterFactory extends
|
||||
AbstractGatewayFilterFactory<AbstractNameValueGatewayFilterFactory.NameValueConfig> {
|
||||
public abstract class AbstractNameValueGatewayFilterFactory
|
||||
extends AbstractGatewayFilterFactory<AbstractNameValueGatewayFilterFactory.NameValueConfig> {
|
||||
|
||||
public AbstractNameValueGatewayFilterFactory() {
|
||||
super(NameValueConfig.class);
|
||||
@@ -65,8 +65,7 @@ public abstract class AbstractNameValueGatewayFilterFactory extends
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("name", name).append("value", value)
|
||||
.toString();
|
||||
return new ToStringCreator(this).append("name", name).append("value", value).toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -29,18 +29,15 @@ import static org.springframework.cloud.gateway.support.GatewayToStringStyler.fi
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class AddRequestHeaderGatewayFilterFactory
|
||||
extends AbstractNameValueGatewayFilterFactory {
|
||||
public class AddRequestHeaderGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {
|
||||
|
||||
@Override
|
||||
public GatewayFilter apply(NameValueConfig config) {
|
||||
return new GatewayFilter() {
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange,
|
||||
GatewayFilterChain chain) {
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
String value = ServerWebExchangeUtils.expand(exchange, config.getValue());
|
||||
ServerHttpRequest request = exchange.getRequest().mutate()
|
||||
.header(config.getName(), value).build();
|
||||
ServerHttpRequest request = exchange.getRequest().mutate().header(config.getName(), value).build();
|
||||
|
||||
return chain.filter(exchange.mutate().request(request).build());
|
||||
}
|
||||
|
||||
@@ -33,15 +33,13 @@ import static org.springframework.cloud.gateway.support.GatewayToStringStyler.fi
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class AddRequestParameterGatewayFilterFactory
|
||||
extends AbstractNameValueGatewayFilterFactory {
|
||||
public class AddRequestParameterGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {
|
||||
|
||||
@Override
|
||||
public GatewayFilter apply(NameValueConfig config) {
|
||||
return new GatewayFilter() {
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange,
|
||||
GatewayFilterChain chain) {
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
URI uri = exchange.getRequest().getURI();
|
||||
StringBuilder query = new StringBuilder();
|
||||
String originalQuery = uri.getRawQuery();
|
||||
@@ -60,17 +58,14 @@ public class AddRequestParameterGatewayFilterFactory
|
||||
query.append(value);
|
||||
|
||||
try {
|
||||
URI newUri = UriComponentsBuilder.fromUri(uri)
|
||||
.replaceQuery(query.toString()).build(true).toUri();
|
||||
URI newUri = UriComponentsBuilder.fromUri(uri).replaceQuery(query.toString()).build(true).toUri();
|
||||
|
||||
ServerHttpRequest request = exchange.getRequest().mutate().uri(newUri)
|
||||
.build();
|
||||
ServerHttpRequest request = exchange.getRequest().mutate().uri(newUri).build();
|
||||
|
||||
return chain.filter(exchange.mutate().request(request).build());
|
||||
}
|
||||
catch (RuntimeException ex) {
|
||||
throw new IllegalStateException(
|
||||
"Invalid URI query: \"" + query.toString() + "\"");
|
||||
throw new IllegalStateException("Invalid URI query: \"" + query.toString() + "\"");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,15 +28,13 @@ import static org.springframework.cloud.gateway.support.GatewayToStringStyler.fi
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class AddResponseHeaderGatewayFilterFactory
|
||||
extends AbstractNameValueGatewayFilterFactory {
|
||||
public class AddResponseHeaderGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {
|
||||
|
||||
@Override
|
||||
public GatewayFilter apply(NameValueConfig config) {
|
||||
return new GatewayFilter() {
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange,
|
||||
GatewayFilterChain chain) {
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
String value = ServerWebExchangeUtils.expand(exchange, config.getValue());
|
||||
exchange.getResponse().getHeaders().add(config.getName(), value);
|
||||
|
||||
|
||||
@@ -69,8 +69,8 @@ Modified response header Access-Control-Allow-Credentials: true
|
||||
/**
|
||||
* @author Vitaliy Pavlyuk
|
||||
*/
|
||||
public class DedupeResponseHeaderGatewayFilterFactory extends
|
||||
AbstractGatewayFilterFactory<DedupeResponseHeaderGatewayFilterFactory.Config> {
|
||||
public class DedupeResponseHeaderGatewayFilterFactory
|
||||
extends AbstractGatewayFilterFactory<DedupeResponseHeaderGatewayFilterFactory.Config> {
|
||||
|
||||
private static final String STRATEGY_KEY = "strategy";
|
||||
|
||||
@@ -87,18 +87,15 @@ public class DedupeResponseHeaderGatewayFilterFactory extends
|
||||
public GatewayFilter apply(Config config) {
|
||||
return new GatewayFilter() {
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange,
|
||||
GatewayFilterChain chain) {
|
||||
return chain.filter(exchange).then(Mono.fromRunnable(
|
||||
() -> dedupe(exchange.getResponse().getHeaders(), config)));
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
return chain.filter(exchange)
|
||||
.then(Mono.fromRunnable(() -> dedupe(exchange.getResponse().getHeaders(), config)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(
|
||||
DedupeResponseHeaderGatewayFilterFactory.this)
|
||||
.append(config.getName(), config.getStrategy())
|
||||
.toString();
|
||||
return filterToStringCreator(DedupeResponseHeaderGatewayFilterFactory.this)
|
||||
.append(config.getName(), config.getStrategy()).toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -45,8 +45,7 @@ public class FallbackHeadersGatewayFilterFactory
|
||||
@Override
|
||||
public GatewayFilter apply(Config config) {
|
||||
return (exchange, chain) -> {
|
||||
Throwable exception = exchange
|
||||
.getAttribute(CIRCUITBREAKER_EXECUTION_EXCEPTION_ATTR);
|
||||
Throwable exception = exchange.getAttribute(CIRCUITBREAKER_EXECUTION_EXCEPTION_ATTR);
|
||||
ServerWebExchange filteredExchange;
|
||||
if (exception == null) {
|
||||
filteredExchange = exchange;
|
||||
@@ -58,19 +57,15 @@ public class FallbackHeadersGatewayFilterFactory
|
||||
};
|
||||
}
|
||||
|
||||
private ServerWebExchange addFallbackHeaders(Config config,
|
||||
ServerWebExchange exchange, Throwable executionException) {
|
||||
private ServerWebExchange addFallbackHeaders(Config config, ServerWebExchange exchange,
|
||||
Throwable executionException) {
|
||||
ServerHttpRequest.Builder requestBuilder = exchange.getRequest().mutate();
|
||||
requestBuilder.header(config.executionExceptionTypeHeaderName,
|
||||
executionException.getClass().getName());
|
||||
requestBuilder.header(config.executionExceptionMessageHeaderName,
|
||||
executionException.getMessage());
|
||||
requestBuilder.header(config.executionExceptionTypeHeaderName, executionException.getClass().getName());
|
||||
requestBuilder.header(config.executionExceptionMessageHeaderName, executionException.getMessage());
|
||||
Throwable rootCause = getRootCause(executionException);
|
||||
if (rootCause != null) {
|
||||
requestBuilder.header(config.rootCauseExceptionTypeHeaderName,
|
||||
rootCause.getClass().getName());
|
||||
requestBuilder.header(config.rootCauseExceptionMessageHeaderName,
|
||||
rootCause.getMessage());
|
||||
requestBuilder.header(config.rootCauseExceptionTypeHeaderName, rootCause.getClass().getName());
|
||||
requestBuilder.header(config.rootCauseExceptionMessageHeaderName, rootCause.getMessage());
|
||||
}
|
||||
return exchange.mutate().request(requestBuilder.build()).build();
|
||||
}
|
||||
@@ -111,8 +106,7 @@ public class FallbackHeadersGatewayFilterFactory
|
||||
return executionExceptionTypeHeaderName;
|
||||
}
|
||||
|
||||
public void setExecutionExceptionTypeHeaderName(
|
||||
String executionExceptionTypeHeaderName) {
|
||||
public void setExecutionExceptionTypeHeaderName(String executionExceptionTypeHeaderName) {
|
||||
this.executionExceptionTypeHeaderName = executionExceptionTypeHeaderName;
|
||||
}
|
||||
|
||||
@@ -120,8 +114,7 @@ public class FallbackHeadersGatewayFilterFactory
|
||||
return executionExceptionMessageHeaderName;
|
||||
}
|
||||
|
||||
public void setExecutionExceptionMessageHeaderName(
|
||||
String executionExceptionMessageHeaderName) {
|
||||
public void setExecutionExceptionMessageHeaderName(String executionExceptionMessageHeaderName) {
|
||||
this.executionExceptionMessageHeaderName = executionExceptionMessageHeaderName;
|
||||
}
|
||||
|
||||
@@ -129,8 +122,7 @@ public class FallbackHeadersGatewayFilterFactory
|
||||
return rootCauseExceptionTypeHeaderName;
|
||||
}
|
||||
|
||||
public void setRootCauseExceptionTypeHeaderName(
|
||||
String rootCauseExceptionTypeHeaderName) {
|
||||
public void setRootCauseExceptionTypeHeaderName(String rootCauseExceptionTypeHeaderName) {
|
||||
this.rootCauseExceptionTypeHeaderName = rootCauseExceptionTypeHeaderName;
|
||||
}
|
||||
|
||||
@@ -138,8 +130,7 @@ public class FallbackHeadersGatewayFilterFactory
|
||||
return rootCauseExceptionMessageHeaderName;
|
||||
}
|
||||
|
||||
public void setCauseExceptionMessageHeaderName(
|
||||
String causeExceptionMessageHeaderName) {
|
||||
public void setCauseExceptionMessageHeaderName(String causeExceptionMessageHeaderName) {
|
||||
this.rootCauseExceptionMessageHeaderName = causeExceptionMessageHeaderName;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,8 +32,8 @@ import static org.springframework.cloud.gateway.support.GatewayToStringStyler.fi
|
||||
/**
|
||||
* @author Tony Clarke
|
||||
*/
|
||||
public class MapRequestHeaderGatewayFilterFactory extends
|
||||
AbstractGatewayFilterFactory<MapRequestHeaderGatewayFilterFactory.Config> {
|
||||
public class MapRequestHeaderGatewayFilterFactory
|
||||
extends AbstractGatewayFilterFactory<MapRequestHeaderGatewayFilterFactory.Config> {
|
||||
|
||||
/**
|
||||
* From Header key.
|
||||
@@ -57,18 +57,14 @@ public class MapRequestHeaderGatewayFilterFactory extends
|
||||
public GatewayFilter apply(MapRequestHeaderGatewayFilterFactory.Config config) {
|
||||
return new GatewayFilter() {
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange,
|
||||
GatewayFilterChain chain) {
|
||||
if (!exchange.getRequest().getHeaders()
|
||||
.containsKey(config.getFromHeader())) {
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
if (!exchange.getRequest().getHeaders().containsKey(config.getFromHeader())) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
List<String> headerValues = exchange.getRequest().getHeaders()
|
||||
.get(config.getFromHeader());
|
||||
List<String> headerValues = exchange.getRequest().getHeaders().get(config.getFromHeader());
|
||||
|
||||
ServerHttpRequest request = exchange.getRequest().mutate()
|
||||
.headers(i -> i.addAll(config.getToHeader(), headerValues))
|
||||
.build();
|
||||
.headers(i -> i.addAll(config.getToHeader(), headerValues)).build();
|
||||
|
||||
return chain.filter(exchange.mutate().request(request).build());
|
||||
}
|
||||
|
||||
@@ -44,8 +44,7 @@ public class PrefixPathGatewayFilterFactory
|
||||
*/
|
||||
public static final String PREFIX_KEY = "prefix";
|
||||
|
||||
private static final Log log = LogFactory
|
||||
.getLog(PrefixPathGatewayFilterFactory.class);
|
||||
private static final Log log = LogFactory.getLog(PrefixPathGatewayFilterFactory.class);
|
||||
|
||||
public PrefixPathGatewayFilterFactory() {
|
||||
super(Config.class);
|
||||
@@ -60,10 +59,8 @@ public class PrefixPathGatewayFilterFactory
|
||||
public GatewayFilter apply(Config config) {
|
||||
return new GatewayFilter() {
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange,
|
||||
GatewayFilterChain chain) {
|
||||
boolean alreadyPrefixed = exchange
|
||||
.getAttributeOrDefault(GATEWAY_ALREADY_PREFIXED_ATTR, false);
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
boolean alreadyPrefixed = exchange.getAttributeOrDefault(GATEWAY_ALREADY_PREFIXED_ATTR, false);
|
||||
if (alreadyPrefixed) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
@@ -78,8 +75,7 @@ public class PrefixPathGatewayFilterFactory
|
||||
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, request.getURI());
|
||||
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Prefixed URI with: " + config.prefix + " -> "
|
||||
+ request.getURI());
|
||||
log.trace("Prefixed URI with: " + config.prefix + " -> " + request.getURI());
|
||||
}
|
||||
|
||||
return chain.filter(exchange.mutate().request(request).build());
|
||||
@@ -87,8 +83,8 @@ public class PrefixPathGatewayFilterFactory
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(PrefixPathGatewayFilterFactory.this)
|
||||
.append("prefix", config.getPrefix()).toString();
|
||||
return filterToStringCreator(PrefixPathGatewayFilterFactory.this).append("prefix", config.getPrefix())
|
||||
.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -38,16 +38,14 @@ public class PreserveHostHeaderGatewayFilterFactory extends AbstractGatewayFilte
|
||||
public GatewayFilter apply(Object config) {
|
||||
return new GatewayFilter() {
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange,
|
||||
GatewayFilterChain chain) {
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
exchange.getAttributes().put(PRESERVE_HOST_HEADER_ATTRIBUTE, true);
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(PreserveHostHeaderGatewayFilterFactory.this)
|
||||
.toString();
|
||||
return filterToStringCreator(PreserveHostHeaderGatewayFilterFactory.this).toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -66,8 +66,7 @@ public class RedirectToGatewayFilterFactory
|
||||
|
||||
public GatewayFilter apply(String statusString, String urlString) {
|
||||
HttpStatusHolder httpStatus = HttpStatusHolder.parse(statusString);
|
||||
Assert.isTrue(httpStatus.is3xxRedirection(),
|
||||
"status must be a 3xx code, but was " + statusString);
|
||||
Assert.isTrue(httpStatus.is3xxRedirection(), "status must be a 3xx code, but was " + statusString);
|
||||
final URI url = URI.create(urlString);
|
||||
return apply(httpStatus, url);
|
||||
}
|
||||
@@ -79,8 +78,7 @@ public class RedirectToGatewayFilterFactory
|
||||
public GatewayFilter apply(HttpStatusHolder httpStatus, URI uri) {
|
||||
return new GatewayFilter() {
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange,
|
||||
GatewayFilterChain chain) {
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
if (!exchange.getResponse().isCommitted()) {
|
||||
setResponseStatus(exchange, httpStatus);
|
||||
|
||||
@@ -100,8 +98,7 @@ public class RedirectToGatewayFilterFactory
|
||||
else {
|
||||
status = httpStatus.getStatus().toString();
|
||||
}
|
||||
return filterToStringCreator(RedirectToGatewayFilterFactory.this)
|
||||
.append(status, uri).toString();
|
||||
return filterToStringCreator(RedirectToGatewayFilterFactory.this).append(status, uri).toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -47,11 +47,9 @@ public class RemoveRequestHeaderGatewayFilterFactory
|
||||
public GatewayFilter apply(NameConfig config) {
|
||||
return new GatewayFilter() {
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange,
|
||||
GatewayFilterChain chain) {
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
ServerHttpRequest request = exchange.getRequest().mutate()
|
||||
.headers(httpHeaders -> httpHeaders.remove(config.getName()))
|
||||
.build();
|
||||
.headers(httpHeaders -> httpHeaders.remove(config.getName())).build();
|
||||
|
||||
return chain.filter(exchange.mutate().request(request).build());
|
||||
}
|
||||
|
||||
@@ -52,28 +52,23 @@ public class RemoveRequestParameterGatewayFilterFactory
|
||||
public GatewayFilter apply(NameConfig config) {
|
||||
return new GatewayFilter() {
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange,
|
||||
GatewayFilterChain chain) {
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
ServerHttpRequest request = exchange.getRequest();
|
||||
MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>(
|
||||
request.getQueryParams());
|
||||
MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>(request.getQueryParams());
|
||||
queryParams.remove(config.getName());
|
||||
|
||||
URI newUri = UriComponentsBuilder.fromUri(request.getURI())
|
||||
.replaceQueryParams(unmodifiableMultiValueMap(queryParams))
|
||||
.build().toUri();
|
||||
.replaceQueryParams(unmodifiableMultiValueMap(queryParams)).build().toUri();
|
||||
|
||||
ServerHttpRequest updatedRequest = exchange.getRequest().mutate()
|
||||
.uri(newUri).build();
|
||||
ServerHttpRequest updatedRequest = exchange.getRequest().mutate().uri(newUri).build();
|
||||
|
||||
return chain.filter(exchange.mutate().request(updatedRequest).build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(
|
||||
RemoveRequestParameterGatewayFilterFactory.this)
|
||||
.append("name", config.getName()).toString();
|
||||
return filterToStringCreator(RemoveRequestParameterGatewayFilterFactory.this)
|
||||
.append("name", config.getName()).toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -46,17 +46,15 @@ public class RemoveResponseHeaderGatewayFilterFactory
|
||||
public GatewayFilter apply(NameConfig config) {
|
||||
return new GatewayFilter() {
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange,
|
||||
GatewayFilterChain chain) {
|
||||
return chain.filter(exchange).then(Mono.fromRunnable(() -> exchange
|
||||
.getResponse().getHeaders().remove(config.getName())));
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
return chain.filter(exchange)
|
||||
.then(Mono.fromRunnable(() -> exchange.getResponse().getHeaders().remove(config.getName())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(
|
||||
RemoveResponseHeaderGatewayFilterFactory.this)
|
||||
.append("name", config.getName()).toString();
|
||||
return filterToStringCreator(RemoveResponseHeaderGatewayFilterFactory.this)
|
||||
.append("name", config.getName()).toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -40,8 +40,8 @@ import static org.springframework.cloud.gateway.support.GatewayToStringStyler.fi
|
||||
* @author Sakalya Deshpande
|
||||
*/
|
||||
|
||||
public class RequestHeaderSizeGatewayFilterFactory extends
|
||||
AbstractGatewayFilterFactory<RequestHeaderSizeGatewayFilterFactory.Config> {
|
||||
public class RequestHeaderSizeGatewayFilterFactory
|
||||
extends AbstractGatewayFilterFactory<RequestHeaderSizeGatewayFilterFactory.Config> {
|
||||
|
||||
private static String ERROR = "Request Header/s size is larger than permissible limit."
|
||||
+ " Request Header/s size is %s where permissible limit is %s";
|
||||
@@ -54,8 +54,7 @@ public class RequestHeaderSizeGatewayFilterFactory extends
|
||||
public GatewayFilter apply(RequestHeaderSizeGatewayFilterFactory.Config config) {
|
||||
return new GatewayFilter() {
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange,
|
||||
GatewayFilterChain chain) {
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
ServerHttpRequest request = exchange.getRequest();
|
||||
HttpHeaders headers = request.getHeaders();
|
||||
Long headerSizeInBytes = 0L;
|
||||
@@ -68,8 +67,7 @@ public class RequestHeaderSizeGatewayFilterFactory extends
|
||||
}
|
||||
|
||||
if (headerSizeInBytes > config.getMaxSize().toBytes()) {
|
||||
exchange.getResponse()
|
||||
.setStatusCode(HttpStatus.REQUEST_HEADER_FIELDS_TOO_LARGE);
|
||||
exchange.getResponse().setStatusCode(HttpStatus.REQUEST_HEADER_FIELDS_TOO_LARGE);
|
||||
exchange.getResponse().getHeaders().add("errorMessage",
|
||||
getErrorMessage(headerSizeInBytes, config.getMaxSize()));
|
||||
return exchange.getResponse().setComplete();
|
||||
@@ -88,8 +86,7 @@ public class RequestHeaderSizeGatewayFilterFactory extends
|
||||
}
|
||||
|
||||
private static String getErrorMessage(Long currentRequestSize, DataSize maxSize) {
|
||||
return String.format(ERROR, DataSize.of(currentRequestSize, DataUnit.BYTES),
|
||||
maxSize);
|
||||
return String.format(ERROR, DataSize.of(currentRequestSize, DataUnit.BYTES), maxSize);
|
||||
}
|
||||
|
||||
public static class Config {
|
||||
|
||||
@@ -38,11 +38,10 @@ import static org.springframework.cloud.gateway.support.GatewayToStringStyler.fi
|
||||
*
|
||||
* @author Toshiaki Maki
|
||||
*/
|
||||
public class RequestHeaderToRequestUriGatewayFilterFactory extends
|
||||
AbstractChangeRequestUriGatewayFilterFactory<AbstractGatewayFilterFactory.NameConfig> {
|
||||
public class RequestHeaderToRequestUriGatewayFilterFactory
|
||||
extends AbstractChangeRequestUriGatewayFilterFactory<AbstractGatewayFilterFactory.NameConfig> {
|
||||
|
||||
private final Logger log = LoggerFactory
|
||||
.getLogger(RequestHeaderToRequestUriGatewayFilterFactory.class);
|
||||
private final Logger log = LoggerFactory.getLogger(RequestHeaderToRequestUriGatewayFilterFactory.class);
|
||||
|
||||
public RequestHeaderToRequestUriGatewayFilterFactory() {
|
||||
super(NameConfig.class);
|
||||
@@ -61,24 +60,21 @@ public class RequestHeaderToRequestUriGatewayFilterFactory extends
|
||||
return new OrderedGatewayFilter(gatewayFilter, gatewayFilter.getOrder()) {
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(
|
||||
RequestHeaderToRequestUriGatewayFilterFactory.this)
|
||||
.append("name", config.getName()).toString();
|
||||
return filterToStringCreator(RequestHeaderToRequestUriGatewayFilterFactory.this)
|
||||
.append("name", config.getName()).toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Optional<URI> determineRequestUri(ServerWebExchange exchange,
|
||||
NameConfig config) {
|
||||
protected Optional<URI> determineRequestUri(ServerWebExchange exchange, NameConfig config) {
|
||||
String requestUrl = exchange.getRequest().getHeaders().getFirst(config.getName());
|
||||
return Optional.ofNullable(requestUrl).map(url -> {
|
||||
try {
|
||||
return new URL(url).toURI();
|
||||
}
|
||||
catch (MalformedURLException | URISyntaxException e) {
|
||||
log.info("Request url is invalid : url={}, error={}", requestUrl,
|
||||
e.getMessage());
|
||||
log.info("Request url is invalid : url={}, error={}", requestUrl, e.getMessage());
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -35,8 +35,8 @@ import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.s
|
||||
* https://gist.github.com/ptarjan/e38f45f2dfe601419ca3af937fff574d#file-1-check_request_rate_limiter-rb-L11-L34.
|
||||
*/
|
||||
@ConfigurationProperties("spring.cloud.gateway.filter.request-rate-limiter")
|
||||
public class RequestRateLimiterGatewayFilterFactory extends
|
||||
AbstractGatewayFilterFactory<RequestRateLimiterGatewayFilterFactory.Config> {
|
||||
public class RequestRateLimiterGatewayFilterFactory
|
||||
extends AbstractGatewayFilterFactory<RequestRateLimiterGatewayFilterFactory.Config> {
|
||||
|
||||
/**
|
||||
* Key-Resolver key.
|
||||
@@ -57,8 +57,7 @@ public class RequestRateLimiterGatewayFilterFactory extends
|
||||
/** HttpStatus to return when denyEmptyKey is true, defaults to FORBIDDEN. */
|
||||
private String emptyKeyStatusCode = HttpStatus.FORBIDDEN.name();
|
||||
|
||||
public RequestRateLimiterGatewayFilterFactory(RateLimiter defaultRateLimiter,
|
||||
KeyResolver defaultKeyResolver) {
|
||||
public RequestRateLimiterGatewayFilterFactory(RateLimiter defaultRateLimiter, KeyResolver defaultKeyResolver) {
|
||||
super(Config.class);
|
||||
this.defaultRateLimiter = defaultRateLimiter;
|
||||
this.defaultKeyResolver = defaultKeyResolver;
|
||||
@@ -92,43 +91,38 @@ public class RequestRateLimiterGatewayFilterFactory extends
|
||||
@Override
|
||||
public GatewayFilter apply(Config config) {
|
||||
KeyResolver resolver = getOrDefault(config.keyResolver, defaultKeyResolver);
|
||||
RateLimiter<Object> limiter = getOrDefault(config.rateLimiter,
|
||||
defaultRateLimiter);
|
||||
RateLimiter<Object> limiter = getOrDefault(config.rateLimiter, defaultRateLimiter);
|
||||
boolean denyEmpty = getOrDefault(config.denyEmptyKey, this.denyEmptyKey);
|
||||
HttpStatusHolder emptyKeyStatus = HttpStatusHolder
|
||||
.parse(getOrDefault(config.emptyKeyStatus, this.emptyKeyStatusCode));
|
||||
|
||||
return (exchange, chain) -> resolver.resolve(exchange).defaultIfEmpty(EMPTY_KEY)
|
||||
.flatMap(key -> {
|
||||
if (EMPTY_KEY.equals(key)) {
|
||||
if (denyEmpty) {
|
||||
setResponseStatus(exchange, emptyKeyStatus);
|
||||
return exchange.getResponse().setComplete();
|
||||
}
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
String routeId = config.getRouteId();
|
||||
if (routeId == null) {
|
||||
Route route = exchange
|
||||
.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
|
||||
routeId = route.getId();
|
||||
}
|
||||
return limiter.isAllowed(routeId, key).flatMap(response -> {
|
||||
return (exchange, chain) -> resolver.resolve(exchange).defaultIfEmpty(EMPTY_KEY).flatMap(key -> {
|
||||
if (EMPTY_KEY.equals(key)) {
|
||||
if (denyEmpty) {
|
||||
setResponseStatus(exchange, emptyKeyStatus);
|
||||
return exchange.getResponse().setComplete();
|
||||
}
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
String routeId = config.getRouteId();
|
||||
if (routeId == null) {
|
||||
Route route = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
|
||||
routeId = route.getId();
|
||||
}
|
||||
return limiter.isAllowed(routeId, key).flatMap(response -> {
|
||||
|
||||
for (Map.Entry<String, String> header : response.getHeaders()
|
||||
.entrySet()) {
|
||||
exchange.getResponse().getHeaders().add(header.getKey(),
|
||||
header.getValue());
|
||||
}
|
||||
for (Map.Entry<String, String> header : response.getHeaders().entrySet()) {
|
||||
exchange.getResponse().getHeaders().add(header.getKey(), header.getValue());
|
||||
}
|
||||
|
||||
if (response.isAllowed()) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
if (response.isAllowed()) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
setResponseStatus(exchange, config.getStatusCode());
|
||||
return exchange.getResponse().setComplete();
|
||||
});
|
||||
});
|
||||
setResponseStatus(exchange, config.getStatusCode());
|
||||
return exchange.getResponse().setComplete();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private <T> T getOrDefault(T configValue, T defaultValue) {
|
||||
|
||||
@@ -35,8 +35,8 @@ import static org.springframework.cloud.gateway.support.GatewayToStringStyler.fi
|
||||
*
|
||||
* @author Arpan
|
||||
*/
|
||||
public class RequestSizeGatewayFilterFactory extends
|
||||
AbstractGatewayFilterFactory<RequestSizeGatewayFilterFactory.RequestSizeConfig> {
|
||||
public class RequestSizeGatewayFilterFactory
|
||||
extends AbstractGatewayFilterFactory<RequestSizeGatewayFilterFactory.RequestSizeConfig> {
|
||||
|
||||
private static String PREFIX = "kMGTPE";
|
||||
|
||||
@@ -48,8 +48,7 @@ public class RequestSizeGatewayFilterFactory extends
|
||||
}
|
||||
|
||||
private static String getErrorMessage(Long currentRequestSize, Long maxSize) {
|
||||
return String.format(ERROR, getReadableByteCount(currentRequestSize),
|
||||
getReadableByteCount(maxSize));
|
||||
return String.format(ERROR, getReadableByteCount(currentRequestSize), getReadableByteCount(maxSize));
|
||||
}
|
||||
|
||||
private static String getReadableByteCount(long bytes) {
|
||||
@@ -63,24 +62,20 @@ public class RequestSizeGatewayFilterFactory extends
|
||||
}
|
||||
|
||||
@Override
|
||||
public GatewayFilter apply(
|
||||
RequestSizeGatewayFilterFactory.RequestSizeConfig requestSizeConfig) {
|
||||
public GatewayFilter apply(RequestSizeGatewayFilterFactory.RequestSizeConfig requestSizeConfig) {
|
||||
requestSizeConfig.validate();
|
||||
return new GatewayFilter() {
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange,
|
||||
GatewayFilterChain chain) {
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
ServerHttpRequest request = exchange.getRequest();
|
||||
String contentLength = request.getHeaders().getFirst("content-length");
|
||||
if (!StringUtils.isEmpty(contentLength)) {
|
||||
Long currentRequestSize = Long.valueOf(contentLength);
|
||||
if (currentRequestSize > requestSizeConfig.getMaxSize().toBytes()) {
|
||||
exchange.getResponse()
|
||||
.setStatusCode(HttpStatus.PAYLOAD_TOO_LARGE);
|
||||
exchange.getResponse().setStatusCode(HttpStatus.PAYLOAD_TOO_LARGE);
|
||||
if (!exchange.getResponse().isCommitted()) {
|
||||
exchange.getResponse().getHeaders().add("errorMessage",
|
||||
getErrorMessage(currentRequestSize,
|
||||
requestSizeConfig.getMaxSize().toBytes()));
|
||||
getErrorMessage(currentRequestSize, requestSizeConfig.getMaxSize().toBytes()));
|
||||
}
|
||||
return exchange.getResponse().setComplete();
|
||||
}
|
||||
@@ -105,8 +100,7 @@ public class RequestSizeGatewayFilterFactory extends
|
||||
return maxSize;
|
||||
}
|
||||
|
||||
public RequestSizeGatewayFilterFactory.RequestSizeConfig setMaxSize(
|
||||
DataSize maxSize) {
|
||||
public RequestSizeGatewayFilterFactory.RequestSizeConfig setMaxSize(DataSize maxSize) {
|
||||
this.maxSize = maxSize;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -48,8 +48,7 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import static org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator;
|
||||
|
||||
public class RetryGatewayFilterFactory
|
||||
extends AbstractGatewayFilterFactory<RetryGatewayFilterFactory.RetryConfig> {
|
||||
public class RetryGatewayFilterFactory extends AbstractGatewayFilterFactory<RetryGatewayFilterFactory.RetryConfig> {
|
||||
|
||||
/**
|
||||
* Retry iteration key.
|
||||
@@ -80,8 +79,7 @@ public class RetryGatewayFilterFactory
|
||||
|
||||
HttpStatus statusCode = exchange.getResponse().getStatusCode();
|
||||
|
||||
boolean retryableStatusCode = retryConfig.getStatuses()
|
||||
.contains(statusCode);
|
||||
boolean retryableStatusCode = retryConfig.getStatuses().contains(statusCode);
|
||||
|
||||
if (!retryableStatusCode && statusCode != null) { // null status code
|
||||
// might mean a
|
||||
@@ -93,14 +91,14 @@ public class RetryGatewayFilterFactory
|
||||
|
||||
final boolean finalRetryableStatusCode = retryableStatusCode;
|
||||
trace("retryableStatusCode: %b, statusCode %s, configured statuses %s, configured series %s",
|
||||
() -> finalRetryableStatusCode, () -> statusCode,
|
||||
retryConfig::getStatuses, retryConfig::getSeries);
|
||||
() -> finalRetryableStatusCode, () -> statusCode, retryConfig::getStatuses,
|
||||
retryConfig::getSeries);
|
||||
|
||||
HttpMethod httpMethod = exchange.getRequest().getMethod();
|
||||
boolean retryableMethod = retryConfig.getMethods().contains(httpMethod);
|
||||
|
||||
trace("retryableMethod: %b, httpMethod %s, configured methods %s",
|
||||
() -> retryableMethod, () -> httpMethod, retryConfig::getMethods);
|
||||
trace("retryableMethod: %b, httpMethod %s, configured methods %s", () -> retryableMethod,
|
||||
() -> httpMethod, retryConfig::getMethods);
|
||||
return retryableMethod && finalRetryableStatusCode;
|
||||
};
|
||||
|
||||
@@ -126,54 +124,44 @@ public class RetryGatewayFilterFactory
|
||||
}
|
||||
|
||||
Throwable exception = context.exception();
|
||||
for (Class<? extends Throwable> retryableClass : retryConfig
|
||||
.getExceptions()) {
|
||||
if (retryableClass.isInstance(exception) || (exception != null
|
||||
&& retryableClass.isInstance(exception.getCause()))) {
|
||||
for (Class<? extends Throwable> retryableClass : retryConfig.getExceptions()) {
|
||||
if (retryableClass.isInstance(exception)
|
||||
|| (exception != null && retryableClass.isInstance(exception.getCause()))) {
|
||||
trace("exception or its cause is retryable %s, configured exceptions %s",
|
||||
() -> getExceptionNameWithCause(exception),
|
||||
retryConfig::getExceptions);
|
||||
() -> getExceptionNameWithCause(exception), retryConfig::getExceptions);
|
||||
|
||||
HttpMethod httpMethod = exchange.getRequest().getMethod();
|
||||
boolean retryableMethod = retryConfig.getMethods()
|
||||
.contains(httpMethod);
|
||||
trace("retryableMethod: %b, httpMethod %s, configured methods %s",
|
||||
() -> retryableMethod, () -> httpMethod,
|
||||
retryConfig::getMethods);
|
||||
boolean retryableMethod = retryConfig.getMethods().contains(httpMethod);
|
||||
trace("retryableMethod: %b, httpMethod %s, configured methods %s", () -> retryableMethod,
|
||||
() -> httpMethod, retryConfig::getMethods);
|
||||
return retryableMethod;
|
||||
}
|
||||
}
|
||||
trace("exception or its cause is not retryable %s, configured exceptions %s",
|
||||
() -> getExceptionNameWithCause(exception),
|
||||
retryConfig::getExceptions);
|
||||
() -> getExceptionNameWithCause(exception), retryConfig::getExceptions);
|
||||
return false;
|
||||
};
|
||||
exceptionRetry = Retry.onlyIf(retryContextPredicate)
|
||||
.doOnRetry(context -> reset(context.applicationContext()))
|
||||
.retryMax(retryConfig.getRetries());
|
||||
.doOnRetry(context -> reset(context.applicationContext())).retryMax(retryConfig.getRetries());
|
||||
BackoffConfig backoff = retryConfig.getBackoff();
|
||||
if (backoff != null) {
|
||||
exceptionRetry = exceptionRetry.backoff(getBackoff(backoff));
|
||||
}
|
||||
}
|
||||
|
||||
GatewayFilter gatewayFilter = apply(retryConfig.getRouteId(), statusCodeRepeat,
|
||||
exceptionRetry);
|
||||
GatewayFilter gatewayFilter = apply(retryConfig.getRouteId(), statusCodeRepeat, exceptionRetry);
|
||||
return new GatewayFilter() {
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange,
|
||||
GatewayFilterChain chain) {
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
return gatewayFilter.filter(exchange, chain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(RetryGatewayFilterFactory.this)
|
||||
.append("retries", retryConfig.getRetries())
|
||||
.append("series", retryConfig.getSeries())
|
||||
.append("statuses", retryConfig.getStatuses())
|
||||
.append("methods", retryConfig.getMethods())
|
||||
.append("exceptions", retryConfig.getExceptions()).toString();
|
||||
return filterToStringCreator(RetryGatewayFilterFactory.this).append("retries", retryConfig.getRetries())
|
||||
.append("series", retryConfig.getSeries()).append("statuses", retryConfig.getStatuses())
|
||||
.append("methods", retryConfig.getMethods()).append("exceptions", retryConfig.getExceptions())
|
||||
.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -193,18 +181,17 @@ public class RetryGatewayFilterFactory
|
||||
}
|
||||
|
||||
private Backoff getBackoff(BackoffConfig backoff) {
|
||||
return Backoff.exponential(backoff.firstBackoff, backoff.maxBackoff,
|
||||
backoff.factor, backoff.basedOnPreviousValue);
|
||||
return Backoff.exponential(backoff.firstBackoff, backoff.maxBackoff, backoff.factor,
|
||||
backoff.basedOnPreviousValue);
|
||||
}
|
||||
|
||||
public boolean exceedsMaxIterations(ServerWebExchange exchange,
|
||||
RetryConfig retryConfig) {
|
||||
public boolean exceedsMaxIterations(ServerWebExchange exchange, RetryConfig retryConfig) {
|
||||
Integer iteration = exchange.getAttribute(RETRY_ITERATION_KEY);
|
||||
|
||||
// TODO: deal with null iteration
|
||||
boolean exceeds = iteration != null && iteration >= retryConfig.getRetries();
|
||||
trace("exceedsMaxIterations %b, iteration %d, configured retries %d",
|
||||
() -> exceeds, () -> iteration, retryConfig::getRetries);
|
||||
trace("exceedsMaxIterations %b, iteration %d, configured retries %d", () -> exceeds, () -> iteration,
|
||||
retryConfig::getRetries);
|
||||
return exceeds;
|
||||
}
|
||||
|
||||
@@ -216,8 +203,7 @@ public class RetryGatewayFilterFactory
|
||||
ServerWebExchangeUtils.reset(exchange);
|
||||
}
|
||||
|
||||
public GatewayFilter apply(String routeId, Repeat<ServerWebExchange> repeat,
|
||||
Retry<ServerWebExchange> retry) {
|
||||
public GatewayFilter apply(String routeId, Repeat<ServerWebExchange> repeat, Retry<ServerWebExchange> retry) {
|
||||
if (routeId != null && getPublisher() != null) {
|
||||
// send an event to enable caching
|
||||
getPublisher().publishEvent(new EnableBodyCachingEvent(this, routeId));
|
||||
@@ -228,20 +214,18 @@ public class RetryGatewayFilterFactory
|
||||
// chain.filter returns a Mono<Void>
|
||||
Publisher<Void> publisher = chain.filter(exchange)
|
||||
// .log("retry-filter", Level.INFO)
|
||||
.doOnSuccess(aVoid -> updateIteration(exchange))
|
||||
.doOnError(throwable -> updateIteration(exchange));
|
||||
.doOnSuccess(aVoid -> updateIteration(exchange)).doOnError(throwable -> updateIteration(exchange));
|
||||
|
||||
if (retry != null) {
|
||||
// retryWhen returns a Mono<Void>
|
||||
// retry needs to go before repeat
|
||||
publisher = ((Mono<Void>) publisher).retryWhen(reactor.util.retry.Retry
|
||||
.withThrowable(retry.withApplicationContext(exchange)));
|
||||
publisher = ((Mono<Void>) publisher)
|
||||
.retryWhen(reactor.util.retry.Retry.withThrowable(retry.withApplicationContext(exchange)));
|
||||
}
|
||||
if (repeat != null) {
|
||||
// repeatWhen returns a Flux<Void>
|
||||
// so this needs to be last and the variable a Publisher<Void>
|
||||
publisher = ((Mono<Void>) publisher)
|
||||
.repeatWhen(repeat.withApplicationContext(exchange));
|
||||
publisher = ((Mono<Void>) publisher).repeatWhen(repeat.withApplicationContext(exchange));
|
||||
}
|
||||
|
||||
return Mono.fromDirect(publisher);
|
||||
@@ -281,8 +265,7 @@ public class RetryGatewayFilterFactory
|
||||
|
||||
private List<HttpMethod> methods = toList(HttpMethod.GET);
|
||||
|
||||
private List<Class<? extends Throwable>> exceptions = toList(IOException.class,
|
||||
TimeoutException.class);
|
||||
private List<Class<? extends Throwable>> exceptions = toList(IOException.class, TimeoutException.class);
|
||||
|
||||
private BackoffConfig backoff;
|
||||
|
||||
@@ -292,9 +275,7 @@ public class RetryGatewayFilterFactory
|
||||
|
||||
public void validate() {
|
||||
Assert.isTrue(this.retries > 0, "retries must be greater than 0");
|
||||
Assert.isTrue(
|
||||
!this.series.isEmpty() || !this.statuses.isEmpty()
|
||||
|| !this.exceptions.isEmpty(),
|
||||
Assert.isTrue(!this.series.isEmpty() || !this.statuses.isEmpty() || !this.exceptions.isEmpty(),
|
||||
"series, status and exceptions may not all be empty");
|
||||
Assert.notEmpty(this.methods, "methods may not be empty");
|
||||
if (this.backoff != null) {
|
||||
@@ -311,10 +292,9 @@ public class RetryGatewayFilterFactory
|
||||
return this;
|
||||
}
|
||||
|
||||
public RetryConfig setBackoff(Duration firstBackoff, Duration maxBackoff,
|
||||
int factor, boolean basedOnPreviousValue) {
|
||||
this.backoff = new BackoffConfig(firstBackoff, maxBackoff, factor,
|
||||
basedOnPreviousValue);
|
||||
public RetryConfig setBackoff(Duration firstBackoff, Duration maxBackoff, int factor,
|
||||
boolean basedOnPreviousValue) {
|
||||
this.backoff = new BackoffConfig(firstBackoff, maxBackoff, factor, basedOnPreviousValue);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -388,8 +368,7 @@ public class RetryGatewayFilterFactory
|
||||
public BackoffConfig() {
|
||||
}
|
||||
|
||||
public BackoffConfig(Duration firstBackoff, Duration maxBackoff, int factor,
|
||||
boolean basedOnPreviousValue) {
|
||||
public BackoffConfig(Duration firstBackoff, Duration maxBackoff, int factor, boolean basedOnPreviousValue) {
|
||||
this.firstBackoff = firstBackoff;
|
||||
this.maxBackoff = maxBackoff;
|
||||
this.factor = factor;
|
||||
|
||||
@@ -93,8 +93,8 @@ Modified (not) Location response header: http://object-service.prod.example.net/
|
||||
/**
|
||||
* @author Vitaliy Pavlyuk
|
||||
*/
|
||||
public class RewriteLocationResponseHeaderGatewayFilterFactory extends
|
||||
AbstractGatewayFilterFactory<RewriteLocationResponseHeaderGatewayFilterFactory.Config> {
|
||||
public class RewriteLocationResponseHeaderGatewayFilterFactory
|
||||
extends AbstractGatewayFilterFactory<RewriteLocationResponseHeaderGatewayFilterFactory.Config> {
|
||||
|
||||
private static final String STRIP_VERSION_KEY = "stripVersion";
|
||||
|
||||
@@ -108,11 +108,9 @@ public class RewriteLocationResponseHeaderGatewayFilterFactory extends
|
||||
|
||||
private static final String DEFAULT_PROTOCOLS = "https?|ftps?";
|
||||
|
||||
private static final Pattern DEFAULT_HOST_PORT = compileHostPortPattern(
|
||||
DEFAULT_PROTOCOLS);
|
||||
private static final Pattern DEFAULT_HOST_PORT = compileHostPortPattern(DEFAULT_PROTOCOLS);
|
||||
|
||||
private static final Pattern DEFAULT_HOST_PORT_VERSION = compileHostPortVersionPattern(
|
||||
DEFAULT_PROTOCOLS);
|
||||
private static final Pattern DEFAULT_HOST_PORT_VERSION = compileHostPortVersionPattern(DEFAULT_PROTOCOLS);
|
||||
|
||||
public RewriteLocationResponseHeaderGatewayFilterFactory() {
|
||||
super(Config.class);
|
||||
@@ -123,24 +121,20 @@ public class RewriteLocationResponseHeaderGatewayFilterFactory extends
|
||||
}
|
||||
|
||||
private static Pattern compileHostPortVersionPattern(String protocols) {
|
||||
return Pattern.compile(
|
||||
"(?<=^(?:" + protocols + ")://)[^:/]+(?::\\d+)?(?:/v\\d+)?(?=/)");
|
||||
return Pattern.compile("(?<=^(?:" + protocols + ")://)[^:/]+(?::\\d+)?(?:/v\\d+)?(?=/)");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> shortcutFieldOrder() {
|
||||
return Arrays.asList(STRIP_VERSION_KEY, LOCATION_HEADER_NAME_KEY, HOST_VALUE_KEY,
|
||||
PROTOCOLS_KEY);
|
||||
return Arrays.asList(STRIP_VERSION_KEY, LOCATION_HEADER_NAME_KEY, HOST_VALUE_KEY, PROTOCOLS_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GatewayFilter apply(Config config) {
|
||||
return new GatewayFilter() {
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange,
|
||||
GatewayFilterChain chain) {
|
||||
return chain.filter(exchange)
|
||||
.then(Mono.fromRunnable(() -> rewriteLocation(exchange, config)));
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
return chain.filter(exchange).then(Mono.fromRunnable(() -> rewriteLocation(exchange, config)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -159,26 +153,21 @@ public class RewriteLocationResponseHeaderGatewayFilterFactory extends
|
||||
}
|
||||
|
||||
void rewriteLocation(ServerWebExchange exchange, Config config) {
|
||||
final String location = exchange.getResponse().getHeaders()
|
||||
.getFirst(config.getLocationHeaderName());
|
||||
final String location = exchange.getResponse().getHeaders().getFirst(config.getLocationHeaderName());
|
||||
final String host = config.getHostValue() != null ? config.getHostValue()
|
||||
: exchange.getRequest().getHeaders().getFirst(HttpHeaders.HOST);
|
||||
final String path = exchange.getRequest().getURI().getPath();
|
||||
if (location != null && host != null) {
|
||||
final String fixedLocation = fixedLocation(location, host, path,
|
||||
config.getStripVersion(), config.getHostPortPattern(),
|
||||
config.getHostPortVersionPattern());
|
||||
exchange.getResponse().getHeaders().set(config.getLocationHeaderName(),
|
||||
fixedLocation);
|
||||
final String fixedLocation = fixedLocation(location, host, path, config.getStripVersion(),
|
||||
config.getHostPortPattern(), config.getHostPortVersionPattern());
|
||||
exchange.getResponse().getHeaders().set(config.getLocationHeaderName(), fixedLocation);
|
||||
}
|
||||
}
|
||||
|
||||
String fixedLocation(String location, String host, String path,
|
||||
StripVersion stripVersion, Pattern hostPortPattern,
|
||||
String fixedLocation(String location, String host, String path, StripVersion stripVersion, Pattern hostPortPattern,
|
||||
Pattern hostPortVersionPattern) {
|
||||
final boolean doStrip = StripVersion.ALWAYS_STRIP.equals(stripVersion)
|
||||
|| (StripVersion.AS_IN_REQUEST.equals(stripVersion)
|
||||
&& !VERSIONED_PATH.matcher(path).matches());
|
||||
|| (StripVersion.AS_IN_REQUEST.equals(stripVersion) && !VERSIONED_PATH.matcher(path).matches());
|
||||
final Pattern pattern = doStrip ? hostPortVersionPattern : hostPortPattern;
|
||||
return pattern.matcher(location).replaceFirst(host);
|
||||
}
|
||||
|
||||
@@ -61,8 +61,7 @@ public class RewritePathGatewayFilterFactory
|
||||
String replacement = config.replacement.replace("$\\", "$");
|
||||
return new GatewayFilter() {
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange,
|
||||
GatewayFilterChain chain) {
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
ServerHttpRequest req = exchange.getRequest();
|
||||
addOriginalRequestUrl(exchange, req.getURI());
|
||||
String path = req.getURI().getRawPath();
|
||||
|
||||
@@ -32,8 +32,8 @@ import static org.springframework.cloud.gateway.support.GatewayToStringStyler.fi
|
||||
/**
|
||||
* @author Vitaliy Pavlyuk
|
||||
*/
|
||||
public class RewriteResponseHeaderGatewayFilterFactory extends
|
||||
AbstractGatewayFilterFactory<RewriteResponseHeaderGatewayFilterFactory.Config> {
|
||||
public class RewriteResponseHeaderGatewayFilterFactory
|
||||
extends AbstractGatewayFilterFactory<RewriteResponseHeaderGatewayFilterFactory.Config> {
|
||||
|
||||
/**
|
||||
* Regexp key.
|
||||
@@ -58,20 +58,15 @@ public class RewriteResponseHeaderGatewayFilterFactory extends
|
||||
public GatewayFilter apply(Config config) {
|
||||
return new GatewayFilter() {
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange,
|
||||
GatewayFilterChain chain) {
|
||||
return chain.filter(exchange)
|
||||
.then(Mono.fromRunnable(() -> rewriteHeaders(exchange, config)));
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
return chain.filter(exchange).then(Mono.fromRunnable(() -> rewriteHeaders(exchange, config)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(
|
||||
RewriteResponseHeaderGatewayFilterFactory.this)
|
||||
.append("name", config.getName())
|
||||
.append("regexp", config.getRegexp())
|
||||
.append("replacement", config.getReplacement())
|
||||
.toString();
|
||||
return filterToStringCreator(RewriteResponseHeaderGatewayFilterFactory.this)
|
||||
.append("name", config.getName()).append("regexp", config.getRegexp())
|
||||
.append("replacement", config.getReplacement()).toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -88,8 +83,7 @@ public class RewriteResponseHeaderGatewayFilterFactory extends
|
||||
}
|
||||
|
||||
protected List<String> rewriteHeaders(Config config, List<String> headers) {
|
||||
return headers.stream()
|
||||
.map(val -> rewrite(val, config.getRegexp(), config.getReplacement()))
|
||||
return headers.stream().map(val -> rewrite(val, config.getRegexp(), config.getReplacement()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
@@ -41,16 +41,13 @@ public class SaveSessionGatewayFilterFactory extends AbstractGatewayFilterFactor
|
||||
public GatewayFilter apply(Object config) {
|
||||
return new GatewayFilter() {
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange,
|
||||
GatewayFilterChain chain) {
|
||||
return exchange.getSession().map(WebSession::save)
|
||||
.then(chain.filter(exchange));
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
return exchange.getSession().map(WebSession::save).then(chain.filter(exchange));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(SaveSessionGatewayFilterFactory.this)
|
||||
.toString();
|
||||
return filterToStringCreator(SaveSessionGatewayFilterFactory.this).toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -86,20 +86,17 @@ public class SecureHeadersGatewayFilterFactory extends AbstractGatewayFilterFact
|
||||
|
||||
return new GatewayFilter() {
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange,
|
||||
GatewayFilterChain chain) {
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
HttpHeaders headers = exchange.getResponse().getHeaders();
|
||||
|
||||
List<String> disabled = properties.getDisable();
|
||||
|
||||
if (isEnabled(disabled, X_XSS_PROTECTION_HEADER)) {
|
||||
headers.add(X_XSS_PROTECTION_HEADER,
|
||||
properties.getXssProtectionHeader());
|
||||
headers.add(X_XSS_PROTECTION_HEADER, properties.getXssProtectionHeader());
|
||||
}
|
||||
|
||||
if (isEnabled(disabled, STRICT_TRANSPORT_SECURITY_HEADER)) {
|
||||
headers.add(STRICT_TRANSPORT_SECURITY_HEADER,
|
||||
properties.getStrictTransportSecurity());
|
||||
headers.add(STRICT_TRANSPORT_SECURITY_HEADER, properties.getStrictTransportSecurity());
|
||||
}
|
||||
|
||||
if (isEnabled(disabled, X_FRAME_OPTIONS_HEADER)) {
|
||||
@@ -107,8 +104,7 @@ public class SecureHeadersGatewayFilterFactory extends AbstractGatewayFilterFact
|
||||
}
|
||||
|
||||
if (isEnabled(disabled, X_CONTENT_TYPE_OPTIONS_HEADER)) {
|
||||
headers.add(X_CONTENT_TYPE_OPTIONS_HEADER,
|
||||
properties.getContentTypeOptions());
|
||||
headers.add(X_CONTENT_TYPE_OPTIONS_HEADER, properties.getContentTypeOptions());
|
||||
}
|
||||
|
||||
if (isEnabled(disabled, REFERRER_POLICY_HEADER)) {
|
||||
@@ -116,18 +112,15 @@ public class SecureHeadersGatewayFilterFactory extends AbstractGatewayFilterFact
|
||||
}
|
||||
|
||||
if (isEnabled(disabled, CONTENT_SECURITY_POLICY_HEADER)) {
|
||||
headers.add(CONTENT_SECURITY_POLICY_HEADER,
|
||||
properties.getContentSecurityPolicy());
|
||||
headers.add(CONTENT_SECURITY_POLICY_HEADER, properties.getContentSecurityPolicy());
|
||||
}
|
||||
|
||||
if (isEnabled(disabled, X_DOWNLOAD_OPTIONS_HEADER)) {
|
||||
headers.add(X_DOWNLOAD_OPTIONS_HEADER,
|
||||
properties.getDownloadOptions());
|
||||
headers.add(X_DOWNLOAD_OPTIONS_HEADER, properties.getDownloadOptions());
|
||||
}
|
||||
|
||||
if (isEnabled(disabled, X_PERMITTED_CROSS_DOMAIN_POLICIES_HEADER)) {
|
||||
headers.add(X_PERMITTED_CROSS_DOMAIN_POLICIES_HEADER,
|
||||
properties.getPermittedCrossDomainPolicies());
|
||||
headers.add(X_PERMITTED_CROSS_DOMAIN_POLICIES_HEADER, properties.getPermittedCrossDomainPolicies());
|
||||
}
|
||||
|
||||
return chain.filter(exchange);
|
||||
@@ -135,8 +128,7 @@ public class SecureHeadersGatewayFilterFactory extends AbstractGatewayFilterFact
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(SecureHeadersGatewayFilterFactory.this)
|
||||
.toString();
|
||||
return filterToStringCreator(SecureHeadersGatewayFilterFactory.this).toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -176,15 +176,13 @@ public class SecureHeadersProperties {
|
||||
public String toString() {
|
||||
final StringBuffer sb = new StringBuffer("SecureHeadersProperties{");
|
||||
sb.append("xssProtectionHeader='").append(xssProtectionHeader).append('\'');
|
||||
sb.append(", strictTransportSecurity='").append(strictTransportSecurity)
|
||||
.append('\'');
|
||||
sb.append(", strictTransportSecurity='").append(strictTransportSecurity).append('\'');
|
||||
sb.append(", frameOptions='").append(frameOptions).append('\'');
|
||||
sb.append(", contentTypeOptions='").append(contentTypeOptions).append('\'');
|
||||
sb.append(", referrerPolicy='").append(referrerPolicy).append('\'');
|
||||
sb.append(", contentSecurityPolicy='").append(contentSecurityPolicy).append('\'');
|
||||
sb.append(", downloadOptions='").append(downloadOptions).append('\'');
|
||||
sb.append(", permittedCrossDomainPolicies='").append(permittedCrossDomainPolicies)
|
||||
.append('\'');
|
||||
sb.append(", permittedCrossDomainPolicies='").append(permittedCrossDomainPolicies).append('\'');
|
||||
sb.append(", disabled='").append(disable).append('\'');
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
|
||||
@@ -37,8 +37,7 @@ import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.g
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class SetPathGatewayFilterFactory
|
||||
extends AbstractGatewayFilterFactory<SetPathGatewayFilterFactory.Config> {
|
||||
public class SetPathGatewayFilterFactory extends AbstractGatewayFilterFactory<SetPathGatewayFilterFactory.Config> {
|
||||
|
||||
/**
|
||||
* Template key.
|
||||
@@ -60,8 +59,7 @@ public class SetPathGatewayFilterFactory
|
||||
|
||||
return new GatewayFilter() {
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange,
|
||||
GatewayFilterChain chain) {
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
ServerHttpRequest req = exchange.getRequest();
|
||||
addOriginalRequestUrl(exchange, req.getURI());
|
||||
|
||||
@@ -79,8 +77,8 @@ public class SetPathGatewayFilterFactory
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(SetPathGatewayFilterFactory.this)
|
||||
.append("template", config.getTemplate()).toString();
|
||||
return filterToStringCreator(SetPathGatewayFilterFactory.this).append("template", config.getTemplate())
|
||||
.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -29,19 +29,16 @@ import static org.springframework.cloud.gateway.support.GatewayToStringStyler.fi
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class SetRequestHeaderGatewayFilterFactory
|
||||
extends AbstractNameValueGatewayFilterFactory {
|
||||
public class SetRequestHeaderGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {
|
||||
|
||||
@Override
|
||||
public GatewayFilter apply(NameValueConfig config) {
|
||||
return new GatewayFilter() {
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange,
|
||||
GatewayFilterChain chain) {
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
String value = ServerWebExchangeUtils.expand(exchange, config.getValue());
|
||||
ServerHttpRequest request = exchange.getRequest().mutate()
|
||||
.headers(httpHeaders -> httpHeaders.set(config.name, value))
|
||||
.build();
|
||||
.headers(httpHeaders -> httpHeaders.set(config.name, value)).build();
|
||||
|
||||
return chain.filter(exchange.mutate().request(request).build());
|
||||
}
|
||||
|
||||
@@ -33,8 +33,8 @@ import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.P
|
||||
/**
|
||||
* @author Andrew Fitzgerald
|
||||
*/
|
||||
public class SetRequestHostHeaderGatewayFilterFactory extends
|
||||
AbstractGatewayFilterFactory<SetRequestHostHeaderGatewayFilterFactory.Config> {
|
||||
public class SetRequestHostHeaderGatewayFilterFactory
|
||||
extends AbstractGatewayFilterFactory<SetRequestHostHeaderGatewayFilterFactory.Config> {
|
||||
|
||||
public SetRequestHostHeaderGatewayFilterFactory() {
|
||||
super(Config.class);
|
||||
@@ -49,15 +49,13 @@ public class SetRequestHostHeaderGatewayFilterFactory extends
|
||||
public GatewayFilter apply(Config config) {
|
||||
return new GatewayFilter() {
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange,
|
||||
GatewayFilterChain chain) {
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
String value = ServerWebExchangeUtils.expand(exchange, config.getHost());
|
||||
|
||||
ServerHttpRequest request = exchange.getRequest().mutate()
|
||||
.headers(httpHeaders -> {
|
||||
httpHeaders.remove("Host");
|
||||
httpHeaders.add("Host", value);
|
||||
}).build();
|
||||
ServerHttpRequest request = exchange.getRequest().mutate().headers(httpHeaders -> {
|
||||
httpHeaders.remove("Host");
|
||||
httpHeaders.add("Host", value);
|
||||
}).build();
|
||||
|
||||
// Make sure the header we just set is preserved
|
||||
exchange.getAttributes().put(PRESERVE_HOST_HEADER_ATTRIBUTE, true);
|
||||
@@ -67,9 +65,8 @@ public class SetRequestHostHeaderGatewayFilterFactory extends
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(
|
||||
SetRequestHostHeaderGatewayFilterFactory.this)
|
||||
.append(config.getHost()).toString();
|
||||
return filterToStringCreator(SetRequestHostHeaderGatewayFilterFactory.this).append(config.getHost())
|
||||
.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -28,18 +28,16 @@ import static org.springframework.cloud.gateway.support.GatewayToStringStyler.fi
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class SetResponseHeaderGatewayFilterFactory
|
||||
extends AbstractNameValueGatewayFilterFactory {
|
||||
public class SetResponseHeaderGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {
|
||||
|
||||
@Override
|
||||
public GatewayFilter apply(NameValueConfig config) {
|
||||
return new GatewayFilter() {
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange,
|
||||
GatewayFilterChain chain) {
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
String value = ServerWebExchangeUtils.expand(exchange, config.getValue());
|
||||
return chain.filter(exchange).then(Mono.fromRunnable(() -> exchange
|
||||
.getResponse().getHeaders().set(config.name, value)));
|
||||
return chain.filter(exchange)
|
||||
.then(Mono.fromRunnable(() -> exchange.getResponse().getHeaders().set(config.name, value)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -36,8 +36,7 @@ import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.s
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@ConfigurationProperties("spring.cloud.gateway.set-status")
|
||||
public class SetStatusGatewayFilterFactory
|
||||
extends AbstractGatewayFilterFactory<SetStatusGatewayFilterFactory.Config> {
|
||||
public class SetStatusGatewayFilterFactory extends AbstractGatewayFilterFactory<SetStatusGatewayFilterFactory.Config> {
|
||||
|
||||
/**
|
||||
* Status key.
|
||||
@@ -64,8 +63,7 @@ public class SetStatusGatewayFilterFactory
|
||||
|
||||
return new GatewayFilter() {
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange,
|
||||
GatewayFilterChain chain) {
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
// option 1 (runs in filter order)
|
||||
/*
|
||||
* exchange.getResponse().beforeCommit(() -> {
|
||||
@@ -78,8 +76,7 @@ public class SetStatusGatewayFilterFactory
|
||||
// check not really needed, since it is guarded in setStatusCode,
|
||||
// but it's a good example
|
||||
HttpStatus statusCode = exchange.getResponse().getStatusCode();
|
||||
boolean isStatusCodeUpdated = setResponseStatus(exchange,
|
||||
statusHolder);
|
||||
boolean isStatusCodeUpdated = setResponseStatus(exchange, statusHolder);
|
||||
if (isStatusCodeUpdated && originalStatusHeaderName != null) {
|
||||
exchange.getResponse().getHeaders().set(originalStatusHeaderName,
|
||||
singletonList(statusCode.value()).toString());
|
||||
@@ -89,8 +86,8 @@ public class SetStatusGatewayFilterFactory
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(SetStatusGatewayFilterFactory.this)
|
||||
.append("status", config.getStatus()).toString();
|
||||
return filterToStringCreator(SetStatusGatewayFilterFactory.this).append("status", config.getStatus())
|
||||
.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -51,8 +51,8 @@ import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.r
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public abstract class SpringCloudCircuitBreakerFilterFactory extends
|
||||
AbstractGatewayFilterFactory<SpringCloudCircuitBreakerFilterFactory.Config> {
|
||||
public abstract class SpringCloudCircuitBreakerFilterFactory
|
||||
extends AbstractGatewayFilterFactory<SpringCloudCircuitBreakerFilterFactory.Config> {
|
||||
|
||||
private ReactiveCircuitBreakerFactory reactiveCircuitBreakerFactory;
|
||||
|
||||
@@ -63,8 +63,7 @@ public abstract class SpringCloudCircuitBreakerFilterFactory extends
|
||||
// do not use this dispatcherHandler directly, use getDispatcherHandler() instead.
|
||||
private volatile DispatcherHandler dispatcherHandler;
|
||||
|
||||
public SpringCloudCircuitBreakerFilterFactory(
|
||||
ReactiveCircuitBreakerFactory reactiveCircuitBreakerFactory,
|
||||
public SpringCloudCircuitBreakerFilterFactory(ReactiveCircuitBreakerFactory reactiveCircuitBreakerFactory,
|
||||
ObjectProvider<DispatcherHandler> dispatcherHandlerProvider) {
|
||||
super(Config.class);
|
||||
this.reactiveCircuitBreakerFactory = reactiveCircuitBreakerFactory;
|
||||
@@ -87,15 +86,13 @@ public abstract class SpringCloudCircuitBreakerFilterFactory extends
|
||||
@Override
|
||||
public GatewayFilter apply(Config config) {
|
||||
ReactiveCircuitBreaker cb = reactiveCircuitBreakerFactory.create(config.getId());
|
||||
Set<HttpStatus> statuses = config.getStatusCodes().stream()
|
||||
.map(HttpStatusHolder::parse)
|
||||
.filter(statusHolder -> statusHolder.getHttpStatus() != null)
|
||||
.map(HttpStatusHolder::getHttpStatus).collect(Collectors.toSet());
|
||||
Set<HttpStatus> statuses = config.getStatusCodes().stream().map(HttpStatusHolder::parse)
|
||||
.filter(statusHolder -> statusHolder.getHttpStatus() != null).map(HttpStatusHolder::getHttpStatus)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
return new GatewayFilter() {
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange,
|
||||
GatewayFilterChain chain) {
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
return cb.run(chain.filter(exchange).doOnSuccess(v -> {
|
||||
if (statuses.contains(exchange.getResponse().getStatusCode())) {
|
||||
HttpStatus status = exchange.getResponse().getStatusCode();
|
||||
@@ -112,9 +109,8 @@ public abstract class SpringCloudCircuitBreakerFilterFactory extends
|
||||
URI uri = exchange.getRequest().getURI();
|
||||
// TODO: assume always?
|
||||
boolean encoded = containsEncodedParts(uri);
|
||||
URI requestUrl = UriComponentsBuilder.fromUri(uri).host(null)
|
||||
.port(null).uri(config.getFallbackUri()).scheme(null)
|
||||
.build(encoded).toUri();
|
||||
URI requestUrl = UriComponentsBuilder.fromUri(uri).host(null).port(null)
|
||||
.uri(config.getFallbackUri()).scheme(null).build(encoded).toUri();
|
||||
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl);
|
||||
addExceptionDetails(t, exchange);
|
||||
|
||||
@@ -123,18 +119,15 @@ public abstract class SpringCloudCircuitBreakerFilterFactory extends
|
||||
// is to another route in the Gateway
|
||||
removeAlreadyRouted(exchange);
|
||||
|
||||
ServerHttpRequest request = exchange.getRequest().mutate()
|
||||
.uri(requestUrl).build();
|
||||
return getDispatcherHandler()
|
||||
.handle(exchange.mutate().request(request).build());
|
||||
ServerHttpRequest request = exchange.getRequest().mutate().uri(requestUrl).build();
|
||||
return getDispatcherHandler().handle(exchange.mutate().request(request).build());
|
||||
}).onErrorResume(t -> handleErrorWithoutFallback(t));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(SpringCloudCircuitBreakerFilterFactory.this)
|
||||
.append("name", config.getName())
|
||||
.append("fallback", config.fallbackUri).toString();
|
||||
.append("name", config.getName()).append("fallback", config.fallbackUri).toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -142,8 +135,8 @@ public abstract class SpringCloudCircuitBreakerFilterFactory extends
|
||||
protected abstract Mono<Void> handleErrorWithoutFallback(Throwable t);
|
||||
|
||||
private void addExceptionDetails(Throwable t, ServerWebExchange exchange) {
|
||||
ofNullable(t).ifPresent(exception -> exchange.getAttributes()
|
||||
.put(CIRCUITBREAKER_EXECUTION_EXCEPTION_ATTR, exception));
|
||||
ofNullable(t).ifPresent(
|
||||
exception -> exchange.getAttributes().put(CIRCUITBREAKER_EXECUTION_EXCEPTION_ATTR, exception));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -29,8 +29,7 @@ import org.springframework.web.server.ResponseStatusException;
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class SpringCloudCircuitBreakerResilience4JFilterFactory
|
||||
extends SpringCloudCircuitBreakerFilterFactory {
|
||||
public class SpringCloudCircuitBreakerResilience4JFilterFactory extends SpringCloudCircuitBreakerFilterFactory {
|
||||
|
||||
public SpringCloudCircuitBreakerResilience4JFilterFactory(
|
||||
ReactiveCircuitBreakerFactory reactiveCircuitBreakerFactory,
|
||||
@@ -41,8 +40,7 @@ public class SpringCloudCircuitBreakerResilience4JFilterFactory
|
||||
@Override
|
||||
protected Mono<Void> handleErrorWithoutFallback(Throwable t) {
|
||||
if (java.util.concurrent.TimeoutException.class.isInstance(t)) {
|
||||
return Mono.error(new ResponseStatusException(HttpStatus.GATEWAY_TIMEOUT,
|
||||
t.getMessage(), t));
|
||||
return Mono.error(new ResponseStatusException(HttpStatus.GATEWAY_TIMEOUT, t.getMessage(), t));
|
||||
}
|
||||
if (CallNotPermittedException.class.isInstance(t)) {
|
||||
return Mono.error(new ServiceUnavailableException());
|
||||
|
||||
@@ -59,27 +59,24 @@ public class StripPrefixGatewayFilterFactory
|
||||
public GatewayFilter apply(Config config) {
|
||||
return new GatewayFilter() {
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange,
|
||||
GatewayFilterChain chain) {
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
ServerHttpRequest request = exchange.getRequest();
|
||||
addOriginalRequestUrl(exchange, request.getURI());
|
||||
String path = request.getURI().getRawPath();
|
||||
String newPath = "/"
|
||||
+ Arrays.stream(StringUtils.tokenizeToStringArray(path, "/"))
|
||||
.skip(config.parts).collect(Collectors.joining("/"));
|
||||
String newPath = "/" + Arrays.stream(StringUtils.tokenizeToStringArray(path, "/")).skip(config.parts)
|
||||
.collect(Collectors.joining("/"));
|
||||
newPath += (newPath.length() > 1 && path.endsWith("/") ? "/" : "");
|
||||
ServerHttpRequest newRequest = request.mutate().path(newPath).build();
|
||||
|
||||
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR,
|
||||
newRequest.getURI());
|
||||
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, newRequest.getURI());
|
||||
|
||||
return chain.filter(exchange.mutate().request(newRequest).build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(StripPrefixGatewayFilterFactory.this)
|
||||
.append("parts", config.getParts()).toString();
|
||||
return filterToStringCreator(StripPrefixGatewayFilterFactory.this).append("parts", config.getParts())
|
||||
.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -40,8 +40,8 @@ public class CachedBodyOutputMessage implements ReactiveHttpOutputMessage {
|
||||
|
||||
private boolean cached = false;
|
||||
|
||||
private Flux<DataBuffer> body = Flux.error(new IllegalStateException(
|
||||
"The body is not set. " + "Did handling complete with success?"));
|
||||
private Flux<DataBuffer> body = Flux
|
||||
.error(new IllegalStateException("The body is not set. " + "Did handling complete with success?"));
|
||||
|
||||
public CachedBodyOutputMessage(ServerWebExchange exchange, HttpHeaders httpHeaders) {
|
||||
this.bufferFactory = exchange.getResponse().bufferFactory();
|
||||
@@ -87,8 +87,7 @@ public class CachedBodyOutputMessage implements ReactiveHttpOutputMessage {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> writeAndFlushWith(
|
||||
Publisher<? extends Publisher<? extends DataBuffer>> body) {
|
||||
public Mono<Void> writeAndFlushWith(Publisher<? extends Publisher<? extends DataBuffer>> body) {
|
||||
return writeWith(Flux.from(body).flatMap(p -> p));
|
||||
}
|
||||
|
||||
|
||||
@@ -43,8 +43,8 @@ import static org.springframework.cloud.gateway.support.GatewayToStringStyler.fi
|
||||
/**
|
||||
* GatewayFilter that modifies the request body.
|
||||
*/
|
||||
public class ModifyRequestBodyGatewayFilterFactory extends
|
||||
AbstractGatewayFilterFactory<ModifyRequestBodyGatewayFilterFactory.Config> {
|
||||
public class ModifyRequestBodyGatewayFilterFactory
|
||||
extends AbstractGatewayFilterFactory<ModifyRequestBodyGatewayFilterFactory.Config> {
|
||||
|
||||
private final List<HttpMessageReader<?>> messageReaders;
|
||||
|
||||
@@ -53,8 +53,7 @@ public class ModifyRequestBodyGatewayFilterFactory extends
|
||||
this.messageReaders = HandlerStrategies.withDefaults().messageReaders();
|
||||
}
|
||||
|
||||
public ModifyRequestBodyGatewayFilterFactory(
|
||||
List<HttpMessageReader<?>> messageReaders) {
|
||||
public ModifyRequestBodyGatewayFilterFactory(List<HttpMessageReader<?>> messageReaders) {
|
||||
super(Config.class);
|
||||
this.messageReaders = messageReaders;
|
||||
}
|
||||
@@ -64,21 +63,16 @@ public class ModifyRequestBodyGatewayFilterFactory extends
|
||||
public GatewayFilter apply(Config config) {
|
||||
return new GatewayFilter() {
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange,
|
||||
GatewayFilterChain chain) {
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
Class inClass = config.getInClass();
|
||||
ServerRequest serverRequest = ServerRequest.create(exchange,
|
||||
messageReaders);
|
||||
ServerRequest serverRequest = ServerRequest.create(exchange, messageReaders);
|
||||
|
||||
// TODO: flux or mono
|
||||
Mono<?> modifiedBody = serverRequest.bodyToMono(inClass)
|
||||
.flatMap(originalBody -> config.getRewriteFunction()
|
||||
.apply(exchange, originalBody))
|
||||
.switchIfEmpty(Mono.defer(() -> (Mono) config.getRewriteFunction()
|
||||
.apply(exchange, null)));
|
||||
.flatMap(originalBody -> config.getRewriteFunction().apply(exchange, originalBody))
|
||||
.switchIfEmpty(Mono.defer(() -> (Mono) config.getRewriteFunction().apply(exchange, null)));
|
||||
|
||||
BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody,
|
||||
config.getOutClass());
|
||||
BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, config.getOutClass());
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.putAll(exchange.getRequest().getHeaders());
|
||||
|
||||
@@ -91,35 +85,29 @@ public class ModifyRequestBodyGatewayFilterFactory extends
|
||||
if (config.getContentType() != null) {
|
||||
headers.set(HttpHeaders.CONTENT_TYPE, config.getContentType());
|
||||
}
|
||||
CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(
|
||||
exchange, headers);
|
||||
CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, headers);
|
||||
return bodyInserter.insert(outputMessage, new BodyInserterContext())
|
||||
// .log("modify_request", Level.INFO)
|
||||
.then(Mono.defer(() -> {
|
||||
ServerHttpRequest decorator = decorate(exchange, headers,
|
||||
outputMessage);
|
||||
return chain
|
||||
.filter(exchange.mutate().request(decorator).build());
|
||||
})).onErrorResume(
|
||||
(Function<Throwable, Mono<Void>>) throwable -> release(
|
||||
exchange, outputMessage, throwable));
|
||||
ServerHttpRequest decorator = decorate(exchange, headers, outputMessage);
|
||||
return chain.filter(exchange.mutate().request(decorator).build());
|
||||
})).onErrorResume((Function<Throwable, Mono<Void>>) throwable -> release(exchange,
|
||||
outputMessage, throwable));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(ModifyRequestBodyGatewayFilterFactory.this)
|
||||
.append("Content type", config.getContentType())
|
||||
.append("In class", config.getInClass())
|
||||
.append("Content type", config.getContentType()).append("In class", config.getInClass())
|
||||
.append("Out class", config.getOutClass()).toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected Mono<Void> release(ServerWebExchange exchange,
|
||||
CachedBodyOutputMessage outputMessage, Throwable throwable) {
|
||||
protected Mono<Void> release(ServerWebExchange exchange, CachedBodyOutputMessage outputMessage,
|
||||
Throwable throwable) {
|
||||
if (outputMessage.isCached()) {
|
||||
return outputMessage.getBody().map(DataBufferUtils::release)
|
||||
.then(Mono.error(throwable));
|
||||
return outputMessage.getBody().map(DataBufferUtils::release).then(Mono.error(throwable));
|
||||
}
|
||||
return Mono.error(throwable);
|
||||
}
|
||||
|
||||
@@ -52,8 +52,8 @@ import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.O
|
||||
/**
|
||||
* GatewayFilter that modifies the response body.
|
||||
*/
|
||||
public class ModifyResponseBodyGatewayFilterFactory extends
|
||||
AbstractGatewayFilterFactory<ModifyResponseBodyGatewayFilterFactory.Config> {
|
||||
public class ModifyResponseBodyGatewayFilterFactory
|
||||
extends AbstractGatewayFilterFactory<ModifyResponseBodyGatewayFilterFactory.Config> {
|
||||
|
||||
private final Map<String, MessageBodyDecoder> messageBodyDecoders;
|
||||
|
||||
@@ -61,10 +61,8 @@ public class ModifyResponseBodyGatewayFilterFactory extends
|
||||
|
||||
private final List<HttpMessageReader<?>> messageReaders;
|
||||
|
||||
public ModifyResponseBodyGatewayFilterFactory(
|
||||
List<HttpMessageReader<?>> messageReaders,
|
||||
Set<MessageBodyDecoder> messageBodyDecoders,
|
||||
Set<MessageBodyEncoder> messageBodyEncoders) {
|
||||
public ModifyResponseBodyGatewayFilterFactory(List<HttpMessageReader<?>> messageReaders,
|
||||
Set<MessageBodyDecoder> messageBodyDecoders, Set<MessageBodyEncoder> messageBodyEncoders) {
|
||||
super(Config.class);
|
||||
this.messageReaders = messageReaders;
|
||||
this.messageBodyDecoders = messageBodyDecoders.stream()
|
||||
@@ -75,8 +73,7 @@ public class ModifyResponseBodyGatewayFilterFactory extends
|
||||
|
||||
@Override
|
||||
public GatewayFilter apply(Config config) {
|
||||
ModifyResponseGatewayFilter gatewayFilter = new ModifyResponseGatewayFilter(
|
||||
config);
|
||||
ModifyResponseGatewayFilter gatewayFilter = new ModifyResponseGatewayFilter(config);
|
||||
gatewayFilter.setFactory(this);
|
||||
return gatewayFilter;
|
||||
}
|
||||
@@ -171,8 +168,7 @@ public class ModifyResponseBodyGatewayFilterFactory extends
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
return chain.filter(exchange.mutate()
|
||||
.response(new ModifiedServerHttpResponse(exchange, config)).build());
|
||||
return chain.filter(exchange.mutate().response(new ModifiedServerHttpResponse(exchange, config)).build());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -182,12 +178,9 @@ public class ModifyResponseBodyGatewayFilterFactory extends
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
Object obj = (this.gatewayFilterFactory != null) ? this.gatewayFilterFactory
|
||||
: this;
|
||||
return filterToStringCreator(obj)
|
||||
.append("New content type", config.getNewContentType())
|
||||
.append("In class", config.getInClass())
|
||||
.append("Out class", config.getOutClass()).toString();
|
||||
Object obj = (this.gatewayFilterFactory != null) ? this.gatewayFilterFactory : this;
|
||||
return filterToStringCreator(obj).append("New content type", config.getNewContentType())
|
||||
.append("In class", config.getInClass()).append("Out class", config.getOutClass()).toString();
|
||||
}
|
||||
|
||||
public void setFactory(GatewayFilterFactory<Config> gatewayFilterFactory) {
|
||||
@@ -215,8 +208,7 @@ public class ModifyResponseBodyGatewayFilterFactory extends
|
||||
Class inClass = config.getInClass();
|
||||
Class outClass = config.getOutClass();
|
||||
|
||||
String originalResponseContentType = exchange
|
||||
.getAttribute(ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR);
|
||||
String originalResponseContentType = exchange.getAttribute(ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR);
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
// explicitly add it in this way instead of
|
||||
// 'httpHeaders.setContentType(originalResponseContentType)'
|
||||
@@ -228,62 +220,48 @@ public class ModifyResponseBodyGatewayFilterFactory extends
|
||||
|
||||
// TODO: flux or mono
|
||||
Mono modifiedBody = extractBody(exchange, clientResponse, inClass)
|
||||
.flatMap(originalBody -> config.getRewriteFunction().apply(exchange,
|
||||
originalBody))
|
||||
.switchIfEmpty(Mono.defer(() -> (Mono) config.getRewriteFunction()
|
||||
.apply(exchange, null)));
|
||||
.flatMap(originalBody -> config.getRewriteFunction().apply(exchange, originalBody))
|
||||
.switchIfEmpty(Mono.defer(() -> (Mono) config.getRewriteFunction().apply(exchange, null)));
|
||||
|
||||
BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody,
|
||||
outClass);
|
||||
BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, outClass);
|
||||
CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange,
|
||||
exchange.getResponse().getHeaders());
|
||||
return bodyInserter.insert(outputMessage, new BodyInserterContext())
|
||||
.then(Mono.defer(() -> {
|
||||
Mono<DataBuffer> messageBody = writeBody(getDelegate(),
|
||||
outputMessage, outClass);
|
||||
HttpHeaders headers = getDelegate().getHeaders();
|
||||
if (!headers.containsKey(HttpHeaders.TRANSFER_ENCODING)
|
||||
|| headers.containsKey(HttpHeaders.CONTENT_LENGTH)) {
|
||||
messageBody = messageBody.doOnNext(data -> headers
|
||||
.setContentLength(data.readableByteCount()));
|
||||
}
|
||||
// TODO: fail if isStreamingMediaType?
|
||||
return getDelegate().writeWith(messageBody);
|
||||
}));
|
||||
return bodyInserter.insert(outputMessage, new BodyInserterContext()).then(Mono.defer(() -> {
|
||||
Mono<DataBuffer> messageBody = writeBody(getDelegate(), outputMessage, outClass);
|
||||
HttpHeaders headers = getDelegate().getHeaders();
|
||||
if (!headers.containsKey(HttpHeaders.TRANSFER_ENCODING)
|
||||
|| headers.containsKey(HttpHeaders.CONTENT_LENGTH)) {
|
||||
messageBody = messageBody.doOnNext(data -> headers.setContentLength(data.readableByteCount()));
|
||||
}
|
||||
// TODO: fail if isStreamingMediaType?
|
||||
return getDelegate().writeWith(messageBody);
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> writeAndFlushWith(
|
||||
Publisher<? extends Publisher<? extends DataBuffer>> body) {
|
||||
public Mono<Void> writeAndFlushWith(Publisher<? extends Publisher<? extends DataBuffer>> body) {
|
||||
return writeWith(Flux.from(body).flatMapSequential(p -> p));
|
||||
}
|
||||
|
||||
private ClientResponse prepareClientResponse(Publisher<? extends DataBuffer> body,
|
||||
HttpHeaders httpHeaders) {
|
||||
private ClientResponse prepareClientResponse(Publisher<? extends DataBuffer> body, HttpHeaders httpHeaders) {
|
||||
ClientResponse.Builder builder;
|
||||
builder = ClientResponse.create(exchange.getResponse().getStatusCode(),
|
||||
messageReaders);
|
||||
return builder.headers(headers -> headers.putAll(httpHeaders))
|
||||
.body(Flux.from(body)).build();
|
||||
builder = ClientResponse.create(exchange.getResponse().getStatusCode(), messageReaders);
|
||||
return builder.headers(headers -> headers.putAll(httpHeaders)).body(Flux.from(body)).build();
|
||||
}
|
||||
|
||||
private <T> Mono<T> extractBody(ServerWebExchange exchange,
|
||||
ClientResponse clientResponse, Class<T> inClass) {
|
||||
private <T> Mono<T> extractBody(ServerWebExchange exchange, ClientResponse clientResponse, Class<T> inClass) {
|
||||
// if inClass is byte[] then just return body, otherwise check if
|
||||
// decoding required
|
||||
if (byte[].class.isAssignableFrom(inClass)) {
|
||||
return clientResponse.bodyToMono(inClass);
|
||||
}
|
||||
|
||||
List<String> encodingHeaders = exchange.getResponse().getHeaders()
|
||||
.getOrEmpty(HttpHeaders.CONTENT_ENCODING);
|
||||
List<String> encodingHeaders = exchange.getResponse().getHeaders().getOrEmpty(HttpHeaders.CONTENT_ENCODING);
|
||||
for (String encoding : encodingHeaders) {
|
||||
MessageBodyDecoder decoder = messageBodyDecoders.get(encoding);
|
||||
if (decoder != null) {
|
||||
return clientResponse.bodyToMono(byte[].class)
|
||||
.publishOn(Schedulers.parallel()).map(decoder::decode)
|
||||
.map(bytes -> exchange.getResponse().bufferFactory()
|
||||
.wrap(bytes))
|
||||
return clientResponse.bodyToMono(byte[].class).publishOn(Schedulers.parallel()).map(decoder::decode)
|
||||
.map(bytes -> exchange.getResponse().bufferFactory().wrap(bytes))
|
||||
.map(buffer -> prepareClientResponse(Mono.just(buffer),
|
||||
exchange.getResponse().getHeaders()))
|
||||
.flatMap(response -> response.bodyToMono(inClass));
|
||||
@@ -293,15 +271,14 @@ public class ModifyResponseBodyGatewayFilterFactory extends
|
||||
return clientResponse.bodyToMono(inClass);
|
||||
}
|
||||
|
||||
private Mono<DataBuffer> writeBody(ServerHttpResponse httpResponse,
|
||||
CachedBodyOutputMessage message, Class<?> outClass) {
|
||||
private Mono<DataBuffer> writeBody(ServerHttpResponse httpResponse, CachedBodyOutputMessage message,
|
||||
Class<?> outClass) {
|
||||
Mono<DataBuffer> response = DataBufferUtils.join(message.getBody());
|
||||
if (byte[].class.isAssignableFrom(outClass)) {
|
||||
return response;
|
||||
}
|
||||
|
||||
List<String> encodingHeaders = httpResponse.getHeaders()
|
||||
.getOrEmpty(HttpHeaders.CONTENT_ENCODING);
|
||||
List<String> encodingHeaders = httpResponse.getHeaders().getOrEmpty(HttpHeaders.CONTENT_ENCODING);
|
||||
for (String encoding : encodingHeaders) {
|
||||
MessageBodyEncoder encoder = messageBodyEncoders.get(encoding);
|
||||
if (encoder != null) {
|
||||
|
||||
@@ -28,7 +28,6 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
* @param <T> the type of the first argument to the function
|
||||
* @param <R> the type of element signaled by the {@link Publisher}
|
||||
*/
|
||||
public interface RewriteFunction<T, R>
|
||||
extends BiFunction<ServerWebExchange, T, Publisher<R>> {
|
||||
public interface RewriteFunction<T, R> extends BiFunction<ServerWebExchange, T, Publisher<R>> {
|
||||
|
||||
}
|
||||
|
||||
@@ -69,8 +69,7 @@ public class ForwardedHeadersFilter implements HttpHeadersFilter, Ordered {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
/* for testing */ static LinkedCaseInsensitiveMap<String> splitIntoCaseInsensitiveMap(
|
||||
String[] pairs) {
|
||||
/* for testing */ static LinkedCaseInsensitiveMap<String> splitIntoCaseInsensitiveMap(String[] pairs) {
|
||||
if (ObjectUtils.isEmpty(pairs)) {
|
||||
return null;
|
||||
}
|
||||
@@ -98,8 +97,7 @@ public class ForwardedHeadersFilter implements HttpHeadersFilter, Ordered {
|
||||
HttpHeaders updated = new HttpHeaders();
|
||||
|
||||
// copy all headers except Forwarded
|
||||
original.entrySet().stream().filter(
|
||||
entry -> !entry.getKey().toLowerCase().equalsIgnoreCase(FORWARDED_HEADER))
|
||||
original.entrySet().stream().filter(entry -> !entry.getKey().toLowerCase().equalsIgnoreCase(FORWARDED_HEADER))
|
||||
.forEach(entry -> updated.addAll(entry.getKey(), entry.getValue()));
|
||||
|
||||
List<Forwarded> forwardeds = parse(original.get(FORWARDED_HEADER));
|
||||
@@ -111,8 +109,7 @@ public class ForwardedHeadersFilter implements HttpHeadersFilter, Ordered {
|
||||
// TODO: add new forwarded
|
||||
URI uri = request.getURI();
|
||||
String host = original.getFirst(HttpHeaders.HOST);
|
||||
Forwarded forwarded = new Forwarded().put("host", host).put("proto",
|
||||
uri.getScheme());
|
||||
Forwarded forwarded = new Forwarded().put("host", host).put("proto", uri.getScheme());
|
||||
|
||||
InetSocketAddress remoteAddress = request.getRemoteAddress();
|
||||
if (remoteAddress != null) {
|
||||
|
||||
@@ -23,23 +23,20 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
public interface HttpHeadersFilter {
|
||||
|
||||
static HttpHeaders filterRequest(List<HttpHeadersFilter> filters,
|
||||
ServerWebExchange exchange) {
|
||||
static HttpHeaders filterRequest(List<HttpHeadersFilter> filters, ServerWebExchange exchange) {
|
||||
HttpHeaders headers = exchange.getRequest().getHeaders();
|
||||
return filter(filters, headers, exchange, Type.REQUEST);
|
||||
}
|
||||
|
||||
static HttpHeaders filter(List<HttpHeadersFilter> filters, HttpHeaders input,
|
||||
ServerWebExchange exchange, Type type) {
|
||||
static HttpHeaders filter(List<HttpHeadersFilter> filters, HttpHeaders input, ServerWebExchange exchange,
|
||||
Type type) {
|
||||
HttpHeaders response = input;
|
||||
if (filters != null) {
|
||||
HttpHeaders reduce = filters.stream()
|
||||
.filter(headersFilter -> headersFilter.supports(type)).reduce(input,
|
||||
(headers, filter) -> filter.filter(headers, exchange),
|
||||
(httpHeaders, httpHeaders2) -> {
|
||||
httpHeaders.addAll(httpHeaders2);
|
||||
return httpHeaders;
|
||||
});
|
||||
HttpHeaders reduce = filters.stream().filter(headersFilter -> headersFilter.supports(type)).reduce(input,
|
||||
(headers, filter) -> filter.filter(headers, exchange), (httpHeaders, httpHeaders2) -> {
|
||||
httpHeaders.addAll(httpHeaders2);
|
||||
return httpHeaders;
|
||||
});
|
||||
return reduce;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,9 +32,8 @@ public class RemoveHopByHopHeadersFilter implements HttpHeadersFilter, Ordered {
|
||||
* Headers to remove as the result of applying the filter.
|
||||
*/
|
||||
public static final Set<String> HEADERS_REMOVED_ON_REQUEST = new HashSet<>(
|
||||
Arrays.asList("connection", "keep-alive", "transfer-encoding", "te",
|
||||
"trailer", "proxy-authorization", "proxy-authenticate",
|
||||
"x-application-context", "upgrade"
|
||||
Arrays.asList("connection", "keep-alive", "transfer-encoding", "te", "trailer", "proxy-authorization",
|
||||
"proxy-authenticate", "x-application-context", "upgrade"
|
||||
// these two are not listed in
|
||||
// https://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-14#section-7.1.3
|
||||
// "proxy-connection",
|
||||
@@ -66,8 +65,7 @@ public class RemoveHopByHopHeadersFilter implements HttpHeadersFilter, Ordered {
|
||||
public HttpHeaders filter(HttpHeaders input, ServerWebExchange exchange) {
|
||||
HttpHeaders filtered = new HttpHeaders();
|
||||
|
||||
input.entrySet().stream()
|
||||
.filter(entry -> !this.headers.contains(entry.getKey().toLowerCase()))
|
||||
input.entrySet().stream().filter(entry -> !this.headers.contains(entry.getKey().toLowerCase()))
|
||||
.forEach(entry -> filtered.addAll(entry.getKey(), entry.getValue()));
|
||||
|
||||
return filtered;
|
||||
|
||||
@@ -201,11 +201,9 @@ public class XForwardedHeadersFilter implements HttpHeadersFilter, Ordered {
|
||||
HttpHeaders original = input;
|
||||
HttpHeaders updated = new HttpHeaders();
|
||||
|
||||
original.entrySet().stream()
|
||||
.forEach(entry -> updated.addAll(entry.getKey(), entry.getValue()));
|
||||
original.entrySet().stream().forEach(entry -> updated.addAll(entry.getKey(), entry.getValue()));
|
||||
|
||||
if (isForEnabled() && request.getRemoteAddress() != null
|
||||
&& request.getRemoteAddress().getAddress() != null) {
|
||||
if (isForEnabled() && request.getRemoteAddress() != null && request.getRemoteAddress().getAddress() != null) {
|
||||
String remoteAddr = request.getRemoteAddress().getAddress().getHostAddress();
|
||||
write(updated, X_FORWARDED_FOR_HEADER, remoteAddr, isForAppend());
|
||||
}
|
||||
@@ -223,8 +221,7 @@ public class XForwardedHeadersFilter implements HttpHeadersFilter, Ordered {
|
||||
// - see XForwardedHeadersFilterTests, so first get uris, then extract paths
|
||||
// and remove one from another if it's the ending part.
|
||||
|
||||
LinkedHashSet<URI> originalUris = exchange
|
||||
.getAttribute(GATEWAY_ORIGINAL_REQUEST_URL_ATTR);
|
||||
LinkedHashSet<URI> originalUris = exchange.getAttribute(GATEWAY_ORIGINAL_REQUEST_URL_ATTR);
|
||||
URI requestUri = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
|
||||
|
||||
if (originalUris != null && requestUri != null) {
|
||||
@@ -239,8 +236,7 @@ public class XForwardedHeadersFilter implements HttpHeadersFilter, Ordered {
|
||||
String originalUriPath = stripTrailingSlash(originalUri);
|
||||
String requestUriPath = stripTrailingSlash(requestUri);
|
||||
|
||||
updateRequest(updated, originalUri, originalUriPath,
|
||||
requestUriPath);
|
||||
updateRequest(updated, originalUri, originalUriPath, requestUriPath);
|
||||
|
||||
}
|
||||
});
|
||||
@@ -263,13 +259,11 @@ public class XForwardedHeadersFilter implements HttpHeadersFilter, Ordered {
|
||||
return updated;
|
||||
}
|
||||
|
||||
private void updateRequest(HttpHeaders updated, URI originalUri,
|
||||
String originalUriPath, String requestUriPath) {
|
||||
private void updateRequest(HttpHeaders updated, URI originalUri, String originalUriPath, String requestUriPath) {
|
||||
String prefix;
|
||||
if (requestUriPath != null && (originalUriPath.endsWith(requestUriPath))) {
|
||||
prefix = substringBeforeLast(originalUriPath, requestUriPath);
|
||||
if (prefix != null && prefix.length() > 0
|
||||
&& prefix.length() <= originalUri.getPath().length()) {
|
||||
if (prefix != null && prefix.length() > 0 && prefix.length() <= originalUri.getPath().length()) {
|
||||
write(updated, X_FORWARDED_PREFIX_HEADER, prefix, isPrefixAppend());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,24 +58,20 @@ public abstract class AbstractRateLimiter<C> extends AbstractStatefulConfigurabl
|
||||
|
||||
C routeConfig = newConfig();
|
||||
if (this.configurationService != null) {
|
||||
this.configurationService.with(routeConfig)
|
||||
.name(this.configurationPropertyName).normalizedProperties(args)
|
||||
this.configurationService.with(routeConfig).name(this.configurationPropertyName).normalizedProperties(args)
|
||||
.bind();
|
||||
}
|
||||
getConfig().put(routeId, routeConfig);
|
||||
}
|
||||
|
||||
private boolean hasRelevantKey(Map<String, Object> args) {
|
||||
return args.keySet().stream()
|
||||
.anyMatch(key -> key.startsWith(configurationPropertyName + "."));
|
||||
return args.keySet().stream().anyMatch(key -> key.startsWith(configurationPropertyName + "."));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this)
|
||||
.append("configurationPropertyName", configurationPropertyName)
|
||||
.append("config", getConfig()).append("configClass", getConfigClass())
|
||||
.toString();
|
||||
return new ToStringCreator(this).append("configurationPropertyName", configurationPropertyName)
|
||||
.append("config", getConfig()).append("configClass", getConfigClass()).toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -52,8 +52,7 @@ import org.springframework.validation.annotation.Validated;
|
||||
* @author Denis Cutic
|
||||
*/
|
||||
@ConfigurationProperties("spring.cloud.gateway.redis-rate-limiter")
|
||||
public class RedisRateLimiter extends AbstractRateLimiter<RedisRateLimiter.Config>
|
||||
implements ApplicationContextAware {
|
||||
public class RedisRateLimiter extends AbstractRateLimiter<RedisRateLimiter.Config> implements ApplicationContextAware {
|
||||
|
||||
/**
|
||||
* Redis Rate Limiter property name.
|
||||
@@ -117,8 +116,8 @@ public class RedisRateLimiter extends AbstractRateLimiter<RedisRateLimiter.Confi
|
||||
/** The name of the header that returns the requested tokens configuration. */
|
||||
private String requestedTokensHeader = REQUESTED_TOKENS_HEADER;
|
||||
|
||||
public RedisRateLimiter(ReactiveStringRedisTemplate redisTemplate,
|
||||
RedisScript<List<Long>> script, ConfigurationService configurationService) {
|
||||
public RedisRateLimiter(ReactiveStringRedisTemplate redisTemplate, RedisScript<List<Long>> script,
|
||||
ConfigurationService configurationService) {
|
||||
super(Config.class, CONFIGURATION_PROPERTY_NAME, configurationService);
|
||||
this.redisTemplate = redisTemplate;
|
||||
this.script = script;
|
||||
@@ -133,8 +132,7 @@ public class RedisRateLimiter extends AbstractRateLimiter<RedisRateLimiter.Confi
|
||||
*/
|
||||
public RedisRateLimiter(int defaultReplenishRate, int defaultBurstCapacity) {
|
||||
super(Config.class, CONFIGURATION_PROPERTY_NAME, (ConfigurationService) null);
|
||||
this.defaultConfig = new Config().setReplenishRate(defaultReplenishRate)
|
||||
.setBurstCapacity(defaultBurstCapacity);
|
||||
this.defaultConfig = new Config().setReplenishRate(defaultReplenishRate).setBurstCapacity(defaultBurstCapacity);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -144,8 +142,7 @@ public class RedisRateLimiter extends AbstractRateLimiter<RedisRateLimiter.Confi
|
||||
* algorithm.
|
||||
* @param defaultRequestedTokens how many tokens are requested per request.
|
||||
*/
|
||||
public RedisRateLimiter(int defaultReplenishRate, int defaultBurstCapacity,
|
||||
int defaultRequestedTokens) {
|
||||
public RedisRateLimiter(int defaultReplenishRate, int defaultBurstCapacity, int defaultRequestedTokens) {
|
||||
this(defaultReplenishRate, defaultBurstCapacity);
|
||||
this.defaultConfig.setRequestedTokens(defaultRequestedTokens);
|
||||
}
|
||||
@@ -253,12 +250,10 @@ public class RedisRateLimiter extends AbstractRateLimiter<RedisRateLimiter.Confi
|
||||
List<String> keys = getKeys(id);
|
||||
|
||||
// The arguments to the LUA script. time() returns unixtime in seconds.
|
||||
List<String> scriptArgs = Arrays.asList(replenishRate + "",
|
||||
burstCapacity + "", Instant.now().getEpochSecond() + "",
|
||||
requestedTokens + "");
|
||||
List<String> scriptArgs = Arrays.asList(replenishRate + "", burstCapacity + "",
|
||||
Instant.now().getEpochSecond() + "", requestedTokens + "");
|
||||
// allowed, tokens_left = redis.eval(SCRIPT, keys, args)
|
||||
Flux<List<Long>> flux = this.redisTemplate.execute(this.script, keys,
|
||||
scriptArgs);
|
||||
Flux<List<Long>> flux = this.redisTemplate.execute(this.script, keys, scriptArgs);
|
||||
// .log("redisratelimiter", Level.FINER);
|
||||
return flux.onErrorResume(throwable -> {
|
||||
if (log.isDebugEnabled()) {
|
||||
@@ -272,8 +267,7 @@ public class RedisRateLimiter extends AbstractRateLimiter<RedisRateLimiter.Confi
|
||||
boolean allowed = results.get(0) == 1L;
|
||||
Long tokensLeft = results.get(1);
|
||||
|
||||
Response response = new Response(allowed,
|
||||
getHeaders(routeConfig, tokensLeft));
|
||||
Response response = new Response(allowed, getHeaders(routeConfig, tokensLeft));
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("response: " + response);
|
||||
@@ -300,8 +294,7 @@ public class RedisRateLimiter extends AbstractRateLimiter<RedisRateLimiter.Confi
|
||||
}
|
||||
|
||||
if (routeConfig == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"No Configuration found for route " + routeId + " or defaultFilters");
|
||||
throw new IllegalArgumentException("No Configuration found for route " + routeId + " or defaultFilters");
|
||||
}
|
||||
return routeConfig;
|
||||
}
|
||||
@@ -311,12 +304,9 @@ public class RedisRateLimiter extends AbstractRateLimiter<RedisRateLimiter.Confi
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
if (isIncludeHeaders()) {
|
||||
headers.put(this.remainingHeader, tokensLeft.toString());
|
||||
headers.put(this.replenishRateHeader,
|
||||
String.valueOf(config.getReplenishRate()));
|
||||
headers.put(this.burstCapacityHeader,
|
||||
String.valueOf(config.getBurstCapacity()));
|
||||
headers.put(this.requestedTokensHeader,
|
||||
String.valueOf(config.getRequestedTokens()));
|
||||
headers.put(this.replenishRateHeader, String.valueOf(config.getReplenishRate()));
|
||||
headers.put(this.burstCapacityHeader, String.valueOf(config.getBurstCapacity()));
|
||||
headers.put(this.requestedTokensHeader, String.valueOf(config.getRequestedTokens()));
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
@@ -363,8 +353,7 @@ public class RedisRateLimiter extends AbstractRateLimiter<RedisRateLimiter.Confi
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("replenishRate", replenishRate)
|
||||
.append("burstCapacity", burstCapacity)
|
||||
.append("requestedTokens", requestedTokens).toString();
|
||||
.append("burstCapacity", burstCapacity).append("requestedTokens", requestedTokens).toString();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -47,8 +47,7 @@ public interface AsyncPredicate<T> extends Function<T, Publisher<Boolean>> {
|
||||
return new OrAsyncPredicate<>(this, other);
|
||||
}
|
||||
|
||||
static AsyncPredicate<ServerWebExchange> from(
|
||||
Predicate<? super ServerWebExchange> predicate) {
|
||||
static AsyncPredicate<ServerWebExchange> from(Predicate<? super ServerWebExchange> predicate) {
|
||||
return new DefaultAsyncPredicate<>(GatewayPredicate.wrapIfNeeded(predicate));
|
||||
}
|
||||
|
||||
@@ -99,8 +98,7 @@ public interface AsyncPredicate<T> extends Function<T, Publisher<Boolean>> {
|
||||
|
||||
private final AsyncPredicate<? super T> right;
|
||||
|
||||
public AndAsyncPredicate(AsyncPredicate<? super T> left,
|
||||
AsyncPredicate<? super T> right) {
|
||||
public AndAsyncPredicate(AsyncPredicate<? super T> left, AsyncPredicate<? super T> right) {
|
||||
Assert.notNull(left, "Left AsyncPredicate must not be null");
|
||||
Assert.notNull(right, "Right AsyncPredicate must not be null");
|
||||
this.left = left;
|
||||
@@ -109,8 +107,7 @@ public interface AsyncPredicate<T> extends Function<T, Publisher<Boolean>> {
|
||||
|
||||
@Override
|
||||
public Publisher<Boolean> apply(T t) {
|
||||
return Mono.from(left.apply(t)).flatMap(
|
||||
result -> !result ? Mono.just(false) : Mono.from(right.apply(t)));
|
||||
return Mono.from(left.apply(t)).flatMap(result -> !result ? Mono.just(false) : Mono.from(right.apply(t)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -126,8 +123,7 @@ public interface AsyncPredicate<T> extends Function<T, Publisher<Boolean>> {
|
||||
|
||||
private final AsyncPredicate<? super T> right;
|
||||
|
||||
public OrAsyncPredicate(AsyncPredicate<? super T> left,
|
||||
AsyncPredicate<? super T> right) {
|
||||
public OrAsyncPredicate(AsyncPredicate<? super T> left, AsyncPredicate<? super T> right) {
|
||||
Assert.notNull(left, "Left AsyncPredicate must not be null");
|
||||
Assert.notNull(right, "Right AsyncPredicate must not be null");
|
||||
this.left = left;
|
||||
@@ -136,8 +132,7 @@ public interface AsyncPredicate<T> extends Function<T, Publisher<Boolean>> {
|
||||
|
||||
@Override
|
||||
public Publisher<Boolean> apply(T t) {
|
||||
return Mono.from(left.apply(t)).flatMap(
|
||||
result -> result ? Mono.just(true) : Mono.from(right.apply(t)));
|
||||
return Mono.from(left.apply(t)).flatMap(result -> result ? Mono.just(true) : Mono.from(right.apply(t)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -113,8 +113,7 @@ public class FilteringWebHandler implements WebHandler {
|
||||
return Mono.defer(() -> {
|
||||
if (this.index < filters.size()) {
|
||||
GatewayFilter filter = filters.get(this.index);
|
||||
DefaultGatewayFilterChain chain = new DefaultGatewayFilterChain(this,
|
||||
this.index + 1);
|
||||
DefaultGatewayFilterChain chain = new DefaultGatewayFilterChain(this, this.index + 1);
|
||||
return filter.filter(exchange, chain);
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -48,9 +48,8 @@ public class RoutePredicateHandlerMapping extends AbstractHandlerMapping {
|
||||
|
||||
private final ManagementPortType managementPortType;
|
||||
|
||||
public RoutePredicateHandlerMapping(FilteringWebHandler webHandler,
|
||||
RouteLocator routeLocator, GlobalCorsProperties globalCorsProperties,
|
||||
Environment environment) {
|
||||
public RoutePredicateHandlerMapping(FilteringWebHandler webHandler, RouteLocator routeLocator,
|
||||
GlobalCorsProperties globalCorsProperties, Environment environment) {
|
||||
this.webHandler = webHandler;
|
||||
this.routeLocator = routeLocator;
|
||||
|
||||
@@ -65,10 +64,8 @@ public class RoutePredicateHandlerMapping extends AbstractHandlerMapping {
|
||||
if (this.managementPort != null && this.managementPort < 0) {
|
||||
return DISABLED;
|
||||
}
|
||||
return ((this.managementPort == null
|
||||
|| (serverPort == null && this.managementPort.equals(8080))
|
||||
|| (this.managementPort != 0 && this.managementPort.equals(serverPort)))
|
||||
? SAME : DIFFERENT);
|
||||
return ((this.managementPort == null || (serverPort == null && this.managementPort.equals(8080))
|
||||
|| (this.managementPort != 0 && this.managementPort.equals(serverPort))) ? SAME : DIFFERENT);
|
||||
}
|
||||
|
||||
private static Integer getPortProperty(Environment environment, String prefix) {
|
||||
@@ -89,8 +86,7 @@ public class RoutePredicateHandlerMapping extends AbstractHandlerMapping {
|
||||
.flatMap((Function<Route, Mono<?>>) r -> {
|
||||
exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(
|
||||
"Mapping [" + getExchangeDesc(exchange) + "] to " + r);
|
||||
logger.debug("Mapping [" + getExchangeDesc(exchange) + "] to " + r);
|
||||
}
|
||||
|
||||
exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, r);
|
||||
@@ -98,15 +94,13 @@ public class RoutePredicateHandlerMapping extends AbstractHandlerMapping {
|
||||
}).switchIfEmpty(Mono.empty().then(Mono.fromRunnable(() -> {
|
||||
exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("No RouteDefinition found for ["
|
||||
+ getExchangeDesc(exchange) + "]");
|
||||
logger.trace("No RouteDefinition found for [" + getExchangeDesc(exchange) + "]");
|
||||
}
|
||||
})));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CorsConfiguration getCorsConfiguration(Object handler,
|
||||
ServerWebExchange exchange) {
|
||||
protected CorsConfiguration getCorsConfiguration(Object handler, ServerWebExchange exchange) {
|
||||
// TODO: support cors configuration via properties on a route see gh-229
|
||||
// see RequestMappingHandlerMapping.initCorsConfiguration()
|
||||
// also see
|
||||
@@ -135,9 +129,7 @@ public class RoutePredicateHandlerMapping extends AbstractHandlerMapping {
|
||||
})
|
||||
// instead of immediately stopping main flux due to error, log and
|
||||
// swallow it
|
||||
.doOnError(e -> logger.error(
|
||||
"Error applying predicate for route: " + route.getId(),
|
||||
e))
|
||||
.doOnError(e -> logger.error("Error applying predicate for route: " + route.getId(), e))
|
||||
.onErrorResume(e -> Mono.empty()))
|
||||
// .defaultIfEmpty() put a static Route not found
|
||||
// or .switchIfEmpty()
|
||||
|
||||
@@ -28,8 +28,7 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class AfterRoutePredicateFactory
|
||||
extends AbstractRoutePredicateFactory<AfterRoutePredicateFactory.Config> {
|
||||
public class AfterRoutePredicateFactory extends AbstractRoutePredicateFactory<AfterRoutePredicateFactory.Config> {
|
||||
|
||||
/**
|
||||
* DateTime key.
|
||||
|
||||
@@ -26,8 +26,7 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class BeforeRoutePredicateFactory
|
||||
extends AbstractRoutePredicateFactory<BeforeRoutePredicateFactory.Config> {
|
||||
public class BeforeRoutePredicateFactory extends AbstractRoutePredicateFactory<BeforeRoutePredicateFactory.Config> {
|
||||
|
||||
/**
|
||||
* DateTime key.
|
||||
|
||||
@@ -30,8 +30,7 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class BetweenRoutePredicateFactory
|
||||
extends AbstractRoutePredicateFactory<BetweenRoutePredicateFactory.Config> {
|
||||
public class BetweenRoutePredicateFactory extends AbstractRoutePredicateFactory<BetweenRoutePredicateFactory.Config> {
|
||||
|
||||
/**
|
||||
* DateTime 1 key.
|
||||
@@ -61,14 +60,12 @@ public class BetweenRoutePredicateFactory
|
||||
@Override
|
||||
public boolean test(ServerWebExchange serverWebExchange) {
|
||||
final ZonedDateTime now = ZonedDateTime.now();
|
||||
return now.isAfter(config.getDatetime1())
|
||||
&& now.isBefore(config.getDatetime2());
|
||||
return now.isAfter(config.getDatetime1()) && now.isBefore(config.getDatetime2());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("Between: %s and %s", config.getDatetime1(),
|
||||
config.getDatetime2());
|
||||
return String.format("Between: %s and %s", config.getDatetime1(), config.getDatetime2());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -28,8 +28,7 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
* @see <a href="https://docs.cloudfoundry.org/services/route-services.html">Cloud Foundry
|
||||
* Route Service documentation</a>
|
||||
*/
|
||||
public class CloudFoundryRouteServiceRoutePredicateFactory
|
||||
extends AbstractRoutePredicateFactory<Object> {
|
||||
public class CloudFoundryRouteServiceRoutePredicateFactory extends AbstractRoutePredicateFactory<Object> {
|
||||
|
||||
/**
|
||||
* Forwarded URL header name.
|
||||
@@ -54,8 +53,7 @@ public class CloudFoundryRouteServiceRoutePredicateFactory
|
||||
|
||||
@Override
|
||||
public Predicate<ServerWebExchange> apply(Object unused) {
|
||||
return headerPredicate(X_CF_FORWARDED_URL)
|
||||
.and(headerPredicate(X_CF_PROXY_SIGNATURE))
|
||||
return headerPredicate(X_CF_FORWARDED_URL).and(headerPredicate(X_CF_PROXY_SIGNATURE))
|
||||
.and(headerPredicate(X_CF_PROXY_METADATA));
|
||||
}
|
||||
|
||||
|
||||
@@ -29,8 +29,7 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class CookieRoutePredicateFactory
|
||||
extends AbstractRoutePredicateFactory<CookieRoutePredicateFactory.Config> {
|
||||
public class CookieRoutePredicateFactory extends AbstractRoutePredicateFactory<CookieRoutePredicateFactory.Config> {
|
||||
|
||||
/**
|
||||
* Name key.
|
||||
@@ -56,8 +55,7 @@ public class CookieRoutePredicateFactory
|
||||
return new GatewayPredicate() {
|
||||
@Override
|
||||
public boolean test(ServerWebExchange exchange) {
|
||||
List<HttpCookie> cookies = exchange.getRequest().getCookies()
|
||||
.get(config.name);
|
||||
List<HttpCookie> cookies = exchange.getRequest().getCookies().get(config.name);
|
||||
if (cookies == null) {
|
||||
return false;
|
||||
}
|
||||
@@ -71,8 +69,7 @@ public class CookieRoutePredicateFactory
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("Cookie: name=%s regexp=%s", config.name,
|
||||
config.regexp);
|
||||
return String.format("Cookie: name=%s regexp=%s", config.name, config.regexp);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -30,8 +30,7 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class HeaderRoutePredicateFactory
|
||||
extends AbstractRoutePredicateFactory<HeaderRoutePredicateFactory.Config> {
|
||||
public class HeaderRoutePredicateFactory extends AbstractRoutePredicateFactory<HeaderRoutePredicateFactory.Config> {
|
||||
|
||||
/**
|
||||
* Header key.
|
||||
@@ -59,16 +58,15 @@ public class HeaderRoutePredicateFactory
|
||||
return new GatewayPredicate() {
|
||||
@Override
|
||||
public boolean test(ServerWebExchange exchange) {
|
||||
List<String> values = exchange.getRequest().getHeaders()
|
||||
.getOrDefault(config.header, Collections.emptyList());
|
||||
List<String> values = exchange.getRequest().getHeaders().getOrDefault(config.header,
|
||||
Collections.emptyList());
|
||||
if (values.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
// values is now guaranteed to not be empty
|
||||
if (hasRegex) {
|
||||
// check if a header value matches
|
||||
return values.stream()
|
||||
.anyMatch(value -> value.matches(config.regexp));
|
||||
return values.stream().anyMatch(value -> value.matches(config.regexp));
|
||||
}
|
||||
|
||||
// there is a value and since regexp is empty, we only check existence.
|
||||
@@ -77,8 +75,7 @@ public class HeaderRoutePredicateFactory
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("Header: %s regexp=%s", config.header,
|
||||
config.regexp);
|
||||
return String.format("Header: %s regexp=%s", config.header, config.regexp);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -33,8 +33,7 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class HostRoutePredicateFactory
|
||||
extends AbstractRoutePredicateFactory<HostRoutePredicateFactory.Config> {
|
||||
public class HostRoutePredicateFactory extends AbstractRoutePredicateFactory<HostRoutePredicateFactory.Config> {
|
||||
|
||||
private PathMatcher pathMatcher = new AntPathMatcher(".");
|
||||
|
||||
@@ -66,8 +65,8 @@ public class HostRoutePredicateFactory
|
||||
.filter(pattern -> pathMatcher.match(pattern, host)).findFirst();
|
||||
|
||||
if (optionalPattern.isPresent()) {
|
||||
Map<String, String> variables = pathMatcher
|
||||
.extractUriTemplateVariables(optionalPattern.get(), host);
|
||||
Map<String, String> variables = pathMatcher.extractUriTemplateVariables(optionalPattern.get(),
|
||||
host);
|
||||
ServerWebExchangeUtils.putUriTemplateVariables(exchange, variables);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -30,8 +30,7 @@ import static java.util.Arrays.stream;
|
||||
* @author Spencer Gibb
|
||||
* @author Dennis Menge
|
||||
*/
|
||||
public class MethodRoutePredicateFactory
|
||||
extends AbstractRoutePredicateFactory<MethodRoutePredicateFactory.Config> {
|
||||
public class MethodRoutePredicateFactory extends AbstractRoutePredicateFactory<MethodRoutePredicateFactory.Config> {
|
||||
|
||||
/**
|
||||
* Methods key.
|
||||
@@ -58,8 +57,7 @@ public class MethodRoutePredicateFactory
|
||||
@Override
|
||||
public boolean test(ServerWebExchange exchange) {
|
||||
HttpMethod requestMethod = exchange.getRequest().getMethod();
|
||||
return stream(config.getMethods())
|
||||
.anyMatch(httpMethod -> httpMethod == requestMethod);
|
||||
return stream(config.getMethods()).anyMatch(httpMethod -> httpMethod == requestMethod);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -40,8 +40,7 @@ import static org.springframework.http.server.PathContainer.parsePath;
|
||||
* @author Spencer Gibb
|
||||
* @author Dhawal Kapil
|
||||
*/
|
||||
public class PathRoutePredicateFactory
|
||||
extends AbstractRoutePredicateFactory<PathRoutePredicateFactory.Config> {
|
||||
public class PathRoutePredicateFactory extends AbstractRoutePredicateFactory<PathRoutePredicateFactory.Config> {
|
||||
|
||||
private static final Log log = LogFactory.getLog(PathRoutePredicateFactory.class);
|
||||
|
||||
@@ -53,11 +52,10 @@ public class PathRoutePredicateFactory
|
||||
super(Config.class);
|
||||
}
|
||||
|
||||
private static void traceMatch(String prefix, Object desired, Object actual,
|
||||
boolean match) {
|
||||
private static void traceMatch(String prefix, Object desired, Object actual, boolean match) {
|
||||
if (log.isTraceEnabled()) {
|
||||
String message = String.format("%s \"%s\" %s against value \"%s\"", prefix,
|
||||
desired, match ? "matches" : "does not match", actual);
|
||||
String message = String.format("%s \"%s\" %s against value \"%s\"", prefix, desired,
|
||||
match ? "matches" : "does not match", actual);
|
||||
log.trace(message);
|
||||
}
|
||||
}
|
||||
@@ -80,8 +78,7 @@ public class PathRoutePredicateFactory
|
||||
public Predicate<ServerWebExchange> apply(Config config) {
|
||||
final ArrayList<PathPattern> pathPatterns = new ArrayList<>();
|
||||
synchronized (this.pathPatternParser) {
|
||||
pathPatternParser
|
||||
.setMatchOptionalTrailingSeparator(config.isMatchTrailingSlash());
|
||||
pathPatternParser.setMatchOptionalTrailingSeparator(config.isMatchTrailingSlash());
|
||||
config.getPatterns().forEach(pattern -> {
|
||||
PathPattern pathPattern = this.pathPatternParser.parse(pattern);
|
||||
pathPatterns.add(pathPattern);
|
||||
@@ -90,8 +87,7 @@ public class PathRoutePredicateFactory
|
||||
return new GatewayPredicate() {
|
||||
@Override
|
||||
public boolean test(ServerWebExchange exchange) {
|
||||
PathContainer path = parsePath(
|
||||
exchange.getRequest().getURI().getRawPath());
|
||||
PathContainer path = parsePath(exchange.getRequest().getURI().getRawPath());
|
||||
|
||||
Optional<PathPattern> optionalPathPattern = pathPatterns.stream()
|
||||
.filter(pattern -> pattern.matches(path)).findFirst();
|
||||
@@ -111,8 +107,8 @@ public class PathRoutePredicateFactory
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("Paths: %s, match trailing slash: %b",
|
||||
config.getPatterns(), config.isMatchTrailingSlash());
|
||||
return String.format("Paths: %s, match trailing slash: %b", config.getPatterns(),
|
||||
config.isMatchTrailingSlash());
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -145,8 +141,7 @@ public class PathRoutePredicateFactory
|
||||
* @deprecated use {@link #setMatchTrailingSlash(boolean)}
|
||||
*/
|
||||
@Deprecated
|
||||
public Config setMatchOptionalTrailingSeparator(
|
||||
boolean matchOptionalTrailingSeparator) {
|
||||
public Config setMatchOptionalTrailingSeparator(boolean matchOptionalTrailingSeparator) {
|
||||
setMatchTrailingSlash(matchOptionalTrailingSeparator);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -45,8 +45,8 @@ public class PredicateDefinition {
|
||||
public PredicateDefinition(String text) {
|
||||
int eqIdx = text.indexOf('=');
|
||||
if (eqIdx <= 0) {
|
||||
throw new ValidationException("Unable to parse PredicateDefinition text '"
|
||||
+ text + "'" + ", must be of the form name=value");
|
||||
throw new ValidationException(
|
||||
"Unable to parse PredicateDefinition text '" + text + "'" + ", must be of the form name=value");
|
||||
}
|
||||
setName(text.substring(0, eqIdx));
|
||||
|
||||
|
||||
@@ -29,8 +29,7 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class QueryRoutePredicateFactory
|
||||
extends AbstractRoutePredicateFactory<QueryRoutePredicateFactory.Config> {
|
||||
public class QueryRoutePredicateFactory extends AbstractRoutePredicateFactory<QueryRoutePredicateFactory.Config> {
|
||||
|
||||
/**
|
||||
* Param key.
|
||||
@@ -58,12 +57,10 @@ public class QueryRoutePredicateFactory
|
||||
public boolean test(ServerWebExchange exchange) {
|
||||
if (!StringUtils.hasText(config.regexp)) {
|
||||
// check existence of header
|
||||
return exchange.getRequest().getQueryParams()
|
||||
.containsKey(config.param);
|
||||
return exchange.getRequest().getQueryParams().containsKey(config.param);
|
||||
}
|
||||
|
||||
List<String> values = exchange.getRequest().getQueryParams()
|
||||
.get(config.param);
|
||||
List<String> values = exchange.getRequest().getQueryParams().get(config.param);
|
||||
if (values == null) {
|
||||
return false;
|
||||
}
|
||||
@@ -77,8 +74,7 @@ public class QueryRoutePredicateFactory
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("Query: param=%s regexp=%s", config.getParam(),
|
||||
config.getRegexp());
|
||||
return String.format("Query: param=%s regexp=%s", config.getParam(), config.getRegexp());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -37,8 +37,7 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
* The body is cached in memory so that possible subsequent calls to the predicate do not
|
||||
* need to deserialize again.
|
||||
*/
|
||||
public class ReadBodyPredicateFactory
|
||||
extends AbstractRoutePredicateFactory<ReadBodyPredicateFactory.Config> {
|
||||
public class ReadBodyPredicateFactory extends AbstractRoutePredicateFactory<ReadBodyPredicateFactory.Config> {
|
||||
|
||||
protected static final Log log = LogFactory.getLog(ReadBodyPredicateFactory.class);
|
||||
|
||||
@@ -91,13 +90,10 @@ public class ReadBodyPredicateFactory
|
||||
else {
|
||||
return ServerWebExchangeUtils.cacheRequestBodyAndRequest(exchange,
|
||||
(serverHttpRequest) -> ServerRequest
|
||||
.create(exchange.mutate().request(serverHttpRequest)
|
||||
.build(), messageReaders)
|
||||
.bodyToMono(inClass)
|
||||
.doOnNext(objectValue -> exchange.getAttributes().put(
|
||||
CACHE_REQUEST_BODY_OBJECT_KEY, objectValue))
|
||||
.map(objectValue -> config.getPredicate()
|
||||
.test(objectValue)));
|
||||
.create(exchange.mutate().request(serverHttpRequest).build(), messageReaders)
|
||||
.bodyToMono(inClass).doOnNext(objectValue -> exchange.getAttributes()
|
||||
.put(CACHE_REQUEST_BODY_OBJECT_KEY, objectValue))
|
||||
.map(objectValue -> config.getPredicate().test(objectValue)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,8 +107,7 @@ public class ReadBodyPredicateFactory
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Predicate<ServerWebExchange> apply(Config config) {
|
||||
throw new UnsupportedOperationException(
|
||||
"ReadBodyPredicateFactory is only async.");
|
||||
throw new UnsupportedOperationException("ReadBodyPredicateFactory is only async.");
|
||||
}
|
||||
|
||||
public static class Config {
|
||||
|
||||
@@ -43,8 +43,7 @@ import static org.springframework.cloud.gateway.support.ShortcutConfigurable.Sho
|
||||
public class RemoteAddrRoutePredicateFactory
|
||||
extends AbstractRoutePredicateFactory<RemoteAddrRoutePredicateFactory.Config> {
|
||||
|
||||
private static final Log log = LogFactory
|
||||
.getLog(RemoteAddrRoutePredicateFactory.class);
|
||||
private static final Log log = LogFactory.getLog(RemoteAddrRoutePredicateFactory.class);
|
||||
|
||||
public RemoteAddrRoutePredicateFactory() {
|
||||
super(Config.class);
|
||||
@@ -76,15 +75,13 @@ public class RemoteAddrRoutePredicateFactory
|
||||
return new GatewayPredicate() {
|
||||
@Override
|
||||
public boolean test(ServerWebExchange exchange) {
|
||||
InetSocketAddress remoteAddress = config.remoteAddressResolver
|
||||
.resolve(exchange);
|
||||
InetSocketAddress remoteAddress = config.remoteAddressResolver.resolve(exchange);
|
||||
if (remoteAddress != null && remoteAddress.getAddress() != null) {
|
||||
String hostAddress = remoteAddress.getAddress().getHostAddress();
|
||||
String host = exchange.getRequest().getURI().getHost();
|
||||
|
||||
if (log.isDebugEnabled() && !hostAddress.equals(host)) {
|
||||
log.debug("Remote addresses didn't match " + hostAddress + " != "
|
||||
+ host);
|
||||
log.debug("Remote addresses didn't match " + hostAddress + " != " + host);
|
||||
}
|
||||
|
||||
for (IpSubnetFilterRule source : sources) {
|
||||
@@ -113,8 +110,7 @@ public class RemoteAddrRoutePredicateFactory
|
||||
String ipAddress = ipAddressCidrPrefix[0];
|
||||
int cidrPrefix = Integer.parseInt(ipAddressCidrPrefix[1]);
|
||||
|
||||
sources.add(
|
||||
new IpSubnetFilterRule(ipAddress, cidrPrefix, IpFilterRuleType.ACCEPT));
|
||||
sources.add(new IpSubnetFilterRule(ipAddress, cidrPrefix, IpFilterRuleType.ACCEPT));
|
||||
}
|
||||
|
||||
@Validated
|
||||
@@ -141,8 +137,7 @@ public class RemoteAddrRoutePredicateFactory
|
||||
return this;
|
||||
}
|
||||
|
||||
public Config setRemoteAddressResolver(
|
||||
RemoteAddressResolver remoteAddressResolver) {
|
||||
public Config setRemoteAddressResolver(RemoteAddressResolver remoteAddressResolver) {
|
||||
this.remoteAddressResolver = remoteAddressResolver;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -38,8 +38,7 @@ import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.W
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
// TODO: make this a generic Choose out of group predicate?
|
||||
public class WeightRoutePredicateFactory
|
||||
extends AbstractRoutePredicateFactory<WeightConfig>
|
||||
public class WeightRoutePredicateFactory extends AbstractRoutePredicateFactory<WeightConfig>
|
||||
implements ApplicationEventPublisherAware {
|
||||
|
||||
/**
|
||||
@@ -87,8 +86,7 @@ public class WeightRoutePredicateFactory
|
||||
return new GatewayPredicate() {
|
||||
@Override
|
||||
public boolean test(ServerWebExchange exchange) {
|
||||
Map<String, String> weights = exchange.getAttributeOrDefault(WEIGHT_ATTR,
|
||||
Collections.emptyMap());
|
||||
Map<String, String> weights = exchange.getAttributeOrDefault(WEIGHT_ATTR, Collections.emptyMap());
|
||||
|
||||
String routeId = exchange.getAttribute(GATEWAY_PREDICATE_ROUTE_ATTR);
|
||||
|
||||
@@ -99,15 +97,14 @@ public class WeightRoutePredicateFactory
|
||||
|
||||
String chosenRoute = weights.get(group);
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("in group weight: " + group + ", current route: "
|
||||
+ routeId + ", chosen route: " + chosenRoute);
|
||||
log.trace("in group weight: " + group + ", current route: " + routeId + ", chosen route: "
|
||||
+ chosenRoute);
|
||||
}
|
||||
|
||||
return routeId.equals(chosenRoute);
|
||||
}
|
||||
else if (log.isTraceEnabled()) {
|
||||
log.trace("no weights found for group: " + group + ", current route: "
|
||||
+ routeId);
|
||||
log.trace("no weights found for group: " + group + ", current route: " + routeId);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -115,8 +112,7 @@ public class WeightRoutePredicateFactory
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("Weight: %s %s", config.getGroup(),
|
||||
config.getWeight());
|
||||
return String.format("Weight: %s %s", config.getGroup(), config.getWeight());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -30,8 +30,7 @@ import org.springframework.context.ApplicationListener;
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class CachingRouteDefinitionLocator
|
||||
implements RouteDefinitionLocator, ApplicationListener<RefreshRoutesEvent> {
|
||||
public class CachingRouteDefinitionLocator implements RouteDefinitionLocator, ApplicationListener<RefreshRoutesEvent> {
|
||||
|
||||
private static final String CACHE_KEY = "routeDefs";
|
||||
|
||||
@@ -43,8 +42,7 @@ public class CachingRouteDefinitionLocator
|
||||
|
||||
public CachingRouteDefinitionLocator(RouteDefinitionLocator delegate) {
|
||||
this.delegate = delegate;
|
||||
routeDefinitions = CacheFlux.lookup(cache, CACHE_KEY, RouteDefinition.class)
|
||||
.onCacheMissResume(this::fetch);
|
||||
routeDefinitions = CacheFlux.lookup(cache, CACHE_KEY, RouteDefinition.class).onCacheMissResume(this::fetch);
|
||||
}
|
||||
|
||||
private Flux<RouteDefinition> fetch() {
|
||||
@@ -67,8 +65,7 @@ public class CachingRouteDefinitionLocator
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(RefreshRoutesEvent event) {
|
||||
fetch().materialize().collect(Collectors.toList())
|
||||
.doOnNext(routes -> cache.put(CACHE_KEY, routes)).subscribe();
|
||||
fetch().materialize().collect(Collectors.toList()).doOnNext(routes -> cache.put(CACHE_KEY, routes)).subscribe();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -37,8 +37,8 @@ import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class CachingRouteLocator implements Ordered, RouteLocator,
|
||||
ApplicationListener<RefreshRoutesEvent>, ApplicationEventPublisherAware {
|
||||
public class CachingRouteLocator
|
||||
implements Ordered, RouteLocator, ApplicationListener<RefreshRoutesEvent>, ApplicationEventPublisherAware {
|
||||
|
||||
private static final Log log = LogFactory.getLog(CachingRouteLocator.class);
|
||||
|
||||
@@ -54,8 +54,7 @@ public class CachingRouteLocator implements Ordered, RouteLocator,
|
||||
|
||||
public CachingRouteLocator(RouteLocator delegate) {
|
||||
this.delegate = delegate;
|
||||
routes = CacheFlux.lookup(cache, CACHE_KEY, Route.class)
|
||||
.onCacheMissResume(this::fetch);
|
||||
routes = CacheFlux.lookup(cache, CACHE_KEY, Route.class).onCacheMissResume(this::fetch);
|
||||
}
|
||||
|
||||
private Flux<Route> fetch() {
|
||||
@@ -79,10 +78,9 @@ public class CachingRouteLocator implements Ordered, RouteLocator,
|
||||
@Override
|
||||
public void onApplicationEvent(RefreshRoutesEvent event) {
|
||||
try {
|
||||
fetch().collect(Collectors.toList()).subscribe(list -> Flux.fromIterable(list)
|
||||
.materialize().collect(Collectors.toList()).subscribe(signals -> {
|
||||
applicationEventPublisher
|
||||
.publishEvent(new RefreshRoutesResultEvent(this));
|
||||
fetch().collect(Collectors.toList()).subscribe(
|
||||
list -> Flux.fromIterable(list).materialize().collect(Collectors.toList()).subscribe(signals -> {
|
||||
applicationEventPublisher.publishEvent(new RefreshRoutesResultEvent(this));
|
||||
cache.put(CACHE_KEY, signals);
|
||||
}, throwable -> handleRefreshError(throwable)));
|
||||
}
|
||||
@@ -95,8 +93,7 @@ public class CachingRouteLocator implements Ordered, RouteLocator,
|
||||
if (log.isErrorEnabled()) {
|
||||
log.error("Refresh routes error !!!", throwable);
|
||||
}
|
||||
applicationEventPublisher
|
||||
.publishEvent(new RefreshRoutesResultEvent(this, throwable));
|
||||
applicationEventPublisher.publishEvent(new RefreshRoutesResultEvent(this, throwable));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -105,8 +102,7 @@ public class CachingRouteLocator implements Ordered, RouteLocator,
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationEventPublisher(
|
||||
ApplicationEventPublisher applicationEventPublisher) {
|
||||
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
|
||||
this.applicationEventPublisher = applicationEventPublisher;
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user