Bumping versions
This commit is contained in:
@@ -107,7 +107,7 @@ public class GRPCApplication {
|
||||
public void hello(HelloRequest request, StreamObserver<HelloResponse> responseObserver) {
|
||||
if ("failWithRuntimeException!".equals(request.getFirstName())) {
|
||||
StatusRuntimeException exception = Status.FAILED_PRECONDITION.withDescription("Invalid firstName")
|
||||
.asRuntimeException();
|
||||
.asRuntimeException();
|
||||
responseObserver.onError(exception);
|
||||
responseObserver.onCompleted();
|
||||
return;
|
||||
|
||||
@@ -59,7 +59,7 @@ public class GRPCApplicationTests {
|
||||
ManagedChannel channel = createSecuredChannel(gatewayPort);
|
||||
|
||||
final HelloResponse response = HelloServiceGrpc.newBlockingStub(channel)
|
||||
.hello(HelloRequest.newBuilder().setFirstName("Sir").setLastName("FromClient").build());
|
||||
.hello(HelloRequest.newBuilder().setFirstName("Sir").setLastName("FromClient").build());
|
||||
|
||||
Assertions.assertThat(response.getGreeting()).isEqualTo("Hello, Sir FromClient");
|
||||
}
|
||||
@@ -67,9 +67,11 @@ public class GRPCApplicationTests {
|
||||
private ManagedChannel createSecuredChannel(int port) throws SSLException {
|
||||
TrustManager[] trustAllCerts = createTrustAllTrustManager();
|
||||
|
||||
return NettyChannelBuilder.forAddress("localhost", port).useTransportSecurity()
|
||||
.sslContext(GrpcSslContexts.forClient().trustManager(trustAllCerts[0]).build()).negotiationType(TLS)
|
||||
.build();
|
||||
return NettyChannelBuilder.forAddress("localhost", port)
|
||||
.useTransportSecurity()
|
||||
.sslContext(GrpcSslContexts.forClient().trustManager(trustAllCerts[0]).build())
|
||||
.negotiationType(TLS)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -78,7 +80,7 @@ public class GRPCApplicationTests {
|
||||
|
||||
try {
|
||||
HelloServiceGrpc.newBlockingStub(channel)
|
||||
.hello(HelloRequest.newBuilder().setFirstName("failWithRuntimeException!").build());
|
||||
.hello(HelloRequest.newBuilder().setFirstName("failWithRuntimeException!").build());
|
||||
}
|
||||
catch (StatusRuntimeException e) {
|
||||
Assertions.assertThat(FAILED_PRECONDITION.getCode()).isEqualTo(e.getStatus().getCode());
|
||||
|
||||
@@ -74,8 +74,10 @@ public class JsonToGrpcApplicationTests {
|
||||
configurer.addRoute(grpcServerPort, "/json/hello",
|
||||
"JsonToGrpc=file:src/main/proto/hello.pb,file:src/main/proto/hello.proto,HelloService,hello");
|
||||
|
||||
String response = restTemplate.postForEntity("https://localhost:" + this.gatewayPort + "/json/hello",
|
||||
"{\"firstName\":\"Duff\", \"lastName\":\"McKagan\"}", String.class).getBody();
|
||||
String response = restTemplate
|
||||
.postForEntity("https://localhost:" + this.gatewayPort + "/json/hello",
|
||||
"{\"firstName\":\"Duff\", \"lastName\":\"McKagan\"}", String.class)
|
||||
.getBody();
|
||||
|
||||
Assertions.assertThat(response).isNotNull();
|
||||
Assertions.assertThat(response).contains("{\"greeting\":\"Hello, Duff McKagan\"}");
|
||||
@@ -94,7 +96,9 @@ public class JsonToGrpcApplicationTests {
|
||||
NoopHostnameVerifier.INSTANCE);
|
||||
|
||||
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
|
||||
.register("https", sslSocketFactory).register("http", new PlainConnectionSocketFactory()).build();
|
||||
.register("https", sslSocketFactory)
|
||||
.register("http", new PlainConnectionSocketFactory())
|
||||
.build();
|
||||
|
||||
HttpClientConnectionManager connectionManager = new BasicHttpClientConnectionManager(socketFactoryRegistry);
|
||||
CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(connectionManager).build();
|
||||
|
||||
@@ -101,7 +101,9 @@ public class RouteConfigurer {
|
||||
NoopHostnameVerifier.INSTANCE);
|
||||
|
||||
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
|
||||
.register("https", sslSocketFactory).register("http", new PlainConnectionSocketFactory()).build();
|
||||
.register("https", sslSocketFactory)
|
||||
.register("http", new PlainConnectionSocketFactory())
|
||||
.build();
|
||||
|
||||
HttpClientConnectionManager connectionManager = new BasicHttpClientConnectionManager(socketFactoryRegistry);
|
||||
CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(connectionManager).build();
|
||||
|
||||
@@ -54,10 +54,12 @@ public class Http2Application {
|
||||
|
||||
@Bean
|
||||
public RouteLocator myRouteLocator(RouteLocatorBuilder builder) {
|
||||
return builder.routes().route(r -> r.path("/myprefix/**").filters(f -> f.stripPrefix(1)).uri("lb://myservice"))
|
||||
.route(r -> r.path("/nossl/**").filters(f -> f.stripPrefix(1)).uri("lb://nossl"))
|
||||
.route(r -> r.path("/neverssl/**").filters(f -> f.stripPrefix(1)).uri("http://neverssl.com"))
|
||||
.route(r -> r.path("/httpbin/**").uri("https://nghttp2.org")).build();
|
||||
return builder.routes()
|
||||
.route(r -> r.path("/myprefix/**").filters(f -> f.stripPrefix(1)).uri("lb://myservice"))
|
||||
.route(r -> r.path("/nossl/**").filters(f -> f.stripPrefix(1)).uri("lb://nossl"))
|
||||
.route(r -> r.path("/neverssl/**").filters(f -> f.stripPrefix(1)).uri("http://neverssl.com"))
|
||||
.route(r -> r.path("/httpbin/**").uri("https://nghttp2.org"))
|
||||
.build();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
@@ -74,13 +74,17 @@ public class Http2ApplicationTests {
|
||||
|
||||
static HttpClient getHttpClient() {
|
||||
return HttpClient
|
||||
.create(ConnectionProvider.builder("test").maxConnections(100)
|
||||
.pendingAcquireTimeout(Duration.ofMillis(0)).pendingAcquireMaxCount(-1).build())
|
||||
.protocol(HttpProtocol.HTTP11, HttpProtocol.H2).secure(sslContextSpec -> {
|
||||
Http2SslContextSpec clientSslCtxt = Http2SslContextSpec.forClient()
|
||||
.configure(builder -> builder.trustManager(InsecureTrustManagerFactory.INSTANCE));
|
||||
sslContextSpec.sslContext(clientSslCtxt);
|
||||
});
|
||||
.create(ConnectionProvider.builder("test")
|
||||
.maxConnections(100)
|
||||
.pendingAcquireTimeout(Duration.ofMillis(0))
|
||||
.pendingAcquireMaxCount(-1)
|
||||
.build())
|
||||
.protocol(HttpProtocol.HTTP11, HttpProtocol.H2)
|
||||
.secure(sslContextSpec -> {
|
||||
Http2SslContextSpec clientSslCtxt = Http2SslContextSpec.forClient()
|
||||
.configure(builder -> builder.trustManager(InsecureTrustManagerFactory.INSTANCE));
|
||||
sslContextSpec.sslContext(clientSslCtxt);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -64,7 +64,9 @@ public class NosslTests {
|
||||
System.err.println("nossl.port = " + nosslPort);
|
||||
Hooks.onOperatorDebug();
|
||||
try (ConfigurableApplicationContext context = new SpringApplicationBuilder(NosslConfiguration.class)
|
||||
.properties("server.port=" + nosslPort).profiles("nossl").run()) {
|
||||
.properties("server.port=" + nosslPort)
|
||||
.profiles("nossl")
|
||||
.run()) {
|
||||
String uri = "https://localhost:" + port + "/nossl";
|
||||
String expected = "nossl";
|
||||
assertResponse(uri, expected);
|
||||
|
||||
@@ -47,8 +47,9 @@ public class MvcFailureAnalyzerApplication {
|
||||
@Bean
|
||||
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
|
||||
public RouteLocator myRouteLocator(RouteLocatorBuilder builder) {
|
||||
return builder.routes().route(r -> r.path("/myprefix/**").filters(f -> f.stripPrefix(1)).uri("lb://myservice"))
|
||||
.build();
|
||||
return builder.routes()
|
||||
.route(r -> r.path("/myprefix/**").filters(f -> f.stripPrefix(1)).uri("lb://myservice"))
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ public class MvcFailureAnalyzerApplicationTests {
|
||||
@Test
|
||||
public void exceptionThrown(CapturedOutput output) {
|
||||
assertThatThrownBy(() -> new SpringApplication(MvcFailureAnalyzerApplication.class).run("--server.port=0"))
|
||||
.hasRootCauseInstanceOf(MvcFoundOnClasspathException.class);
|
||||
.hasRootCauseInstanceOf(MvcFoundOnClasspathException.class);
|
||||
assertThat(output).contains(MvcFoundOnClasspathFailureAnalyzer.MESSAGE,
|
||||
MvcFoundOnClasspathFailureAnalyzer.ACTION);
|
||||
}
|
||||
@@ -48,7 +48,7 @@ public class MvcFailureAnalyzerApplicationTests {
|
||||
@Test
|
||||
public void exceptionNotThrownWhenDisabled(CapturedOutput output) {
|
||||
assertThatCode(() -> new SpringApplication(MvcFailureAnalyzerApplication.class)
|
||||
.run("--spring.cloud.gateway.enabled=false", "--server.port=0")).doesNotThrowAnyException();
|
||||
.run("--spring.cloud.gateway.enabled=false", "--server.port=0")).doesNotThrowAnyException();
|
||||
assertThat(output).doesNotContain(MvcFoundOnClasspathFailureAnalyzer.MESSAGE,
|
||||
MvcFoundOnClasspathFailureAnalyzer.ACTION);
|
||||
}
|
||||
@@ -57,11 +57,16 @@ public class MvcFailureAnalyzerApplicationTests {
|
||||
public void exceptionNotThrownWhenReactiveTypeSet(CapturedOutput output) {
|
||||
assertThatCode(() -> {
|
||||
ConfigurableApplicationContext context = new SpringApplication(MvcFailureAnalyzerApplication.class)
|
||||
.run("--spring.main.web-application-type=reactive", "--server.port=0", "--debug=true");
|
||||
.run("--spring.main.web-application-type=reactive", "--server.port=0", "--debug=true");
|
||||
Integer port = context.getEnvironment().getProperty("local.server.port", Integer.class);
|
||||
WebTestClient client = WebTestClient.bindToServer().baseUrl("http://localhost:" + port).build();
|
||||
client.get().uri("/myprefix/hello").exchange().expectStatus().isOk().expectBody(String.class)
|
||||
.isEqualTo("Hello");
|
||||
client.get()
|
||||
.uri("/myprefix/hello")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(String.class)
|
||||
.isEqualTo("Hello");
|
||||
context.close();
|
||||
}).doesNotThrowAnyException();
|
||||
assertThat(output).doesNotContain(MvcFoundOnClasspathFailureAnalyzer.MESSAGE,
|
||||
|
||||
@@ -280,8 +280,8 @@ public class ProxyExchange<T> {
|
||||
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);
|
||||
@@ -363,8 +363,9 @@ public class ProxyExchange<T> {
|
||||
ArrayList<String> headerNames = new ArrayList<>();
|
||||
webRequest.getHeaderNames().forEachRemaining(headerNames::add);
|
||||
Set<String> filteredKeys = filterHeaderKeys(headerNames);
|
||||
filteredKeys.stream().filter(key -> !headers.containsKey(key))
|
||||
.forEach(header -> headers.addAll(header, Arrays.asList(webRequest.getHeaderValues(header))));
|
||||
filteredKeys.stream()
|
||||
.filter(key -> !headers.containsKey(key))
|
||||
.forEach(header -> headers.addAll(header, Arrays.asList(webRequest.getHeaderValues(header))));
|
||||
}
|
||||
|
||||
private BodyBuilder headers(BodyBuilder builder) {
|
||||
@@ -382,8 +383,9 @@ public class ProxyExchange<T> {
|
||||
|
||||
private Set<String> filterHeaderKeys(Collection<String> headerNames) {
|
||||
final Set<String> excludedHeaders = this.excluded != null ? this.excluded : Collections.emptySet();
|
||||
return headerNames.stream().filter(header -> !excludedHeaders.contains(header.toLowerCase()))
|
||||
.collect(Collectors.toSet());
|
||||
return headerNames.stream()
|
||||
.filter(header -> !excludedHeaders.contains(header.toLowerCase()))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
private void proxy() {
|
||||
|
||||
@@ -109,7 +109,7 @@ public class ProductionConfigurationTests {
|
||||
@Test
|
||||
public void post() {
|
||||
assertThat(rest.postForObject("/proxy/0", Collections.singletonMap("name", "foo"), Bar.class).getName())
|
||||
.isEqualTo("host=localhost:" + port + ";foo");
|
||||
.isEqualTo("host=localhost:" + port + ";foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -124,7 +124,7 @@ public class ProductionConfigurationTests {
|
||||
headers.setContentLength(json.length());
|
||||
var request = new HttpEntity<>(json, headers);
|
||||
assertThat(rest.postForEntity("/proxy/checkContentLength", request, Void.class).getStatusCode())
|
||||
.isEqualTo(HttpStatus.OK);
|
||||
.isEqualTo(HttpStatus.OK);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -143,7 +143,7 @@ public class ProductionConfigurationTests {
|
||||
public void postForwardHeader() {
|
||||
ResponseEntity<List<Bar>> result = rest.exchange(
|
||||
RequestEntity.post(rest.getRestTemplate().getUriTemplateHandler().expand("/forward/special/bars"))
|
||||
.body(Collections.singletonList(Collections.singletonMap("name", "foo"))),
|
||||
.body(Collections.singletonList(Collections.singletonMap("name", "foo"))),
|
||||
new ParameterizedTypeReference<List<Bar>>() {
|
||||
});
|
||||
assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
@@ -153,20 +153,17 @@ public class ProductionConfigurationTests {
|
||||
@Test
|
||||
public void postForwardBody() {
|
||||
ResponseEntity<String> result = rest
|
||||
.exchange(
|
||||
RequestEntity.post(rest.getRestTemplate().getUriTemplateHandler().expand("/forward/body/bars"))
|
||||
.body(Collections.singletonList(Collections.singletonMap("name", "foo"))),
|
||||
String.class);
|
||||
.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");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postForwardForgetBody() {
|
||||
ResponseEntity<String> result = rest.exchange(
|
||||
RequestEntity.post(rest.getRestTemplate().getUriTemplateHandler().expand("/forward/forget/bars"))
|
||||
.body(Collections.singletonList(Collections.singletonMap("name", "foo"))),
|
||||
String.class);
|
||||
ResponseEntity<String> result = rest
|
||||
.exchange(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");
|
||||
}
|
||||
@@ -175,7 +172,7 @@ public class ProductionConfigurationTests {
|
||||
public void postForwardBodyFoo() {
|
||||
ResponseEntity<List<Bar>> result = rest.exchange(
|
||||
RequestEntity.post(rest.getRestTemplate().getUriTemplateHandler().expand("/forward/body/bars"))
|
||||
.body(Collections.singletonList(Collections.singletonMap("name", "foo"))),
|
||||
.body(Collections.singletonList(Collections.singletonMap("name", "foo"))),
|
||||
new ParameterizedTypeReference<List<Bar>>() {
|
||||
});
|
||||
assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
@@ -186,44 +183,62 @@ public class ProductionConfigurationTests {
|
||||
public void list() {
|
||||
assertThat(rest.exchange(
|
||||
RequestEntity.post(rest.getRestTemplate().getUriTemplateHandler().expand("/proxy"))
|
||||
.body(Collections.singletonList(Collections.singletonMap("name", "foo"))),
|
||||
.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() {
|
||||
assertThat(rest.postForObject("/proxy/0", Collections.singletonMap("name", "foo"), Bar.class).getName())
|
||||
.isEqualTo("host=localhost:" + port + ";foo");
|
||||
.isEqualTo("host=localhost:" + port + ";foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void entity() {
|
||||
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");
|
||||
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() {
|
||||
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");
|
||||
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() {
|
||||
assertThat(rest.postForObject("/proxy/single", Collections.singletonMap("name", "foobar"), Bar.class).getName())
|
||||
.isEqualTo("host=localhost:" + port + ";foobar");
|
||||
.isEqualTo("host=localhost:" + port + ";foobar");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void converter() {
|
||||
assertThat(
|
||||
rest.postForObject("/proxy/converter", Collections.singletonMap("name", "foobar"), Bar.class).getName())
|
||||
.isEqualTo("host=localhost:" + port + ";foobar");
|
||||
.isEqualTo("host=localhost:" + port + ";foobar");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -254,8 +269,11 @@ public class ProductionConfigurationTests {
|
||||
@SuppressWarnings({ "Duplicates", "unchecked" })
|
||||
public void testSensitiveHeadersOverride() {
|
||||
RequestEntity<Void> request = RequestEntity
|
||||
.get(rest.getRestTemplate().getUriTemplateHandler().expand("/proxy/headers")).header("foo", "bar")
|
||||
.header("abc", "xyz").header("cookie", "monster").build();
|
||||
.get(rest.getRestTemplate().getUriTemplateHandler().expand("/proxy/headers"))
|
||||
.header("foo", "bar")
|
||||
.header("abc", "xyz")
|
||||
.header("cookie", "monster")
|
||||
.build();
|
||||
Map<String, List<String>> headers = rest.exchange(request, Map.class).getBody();
|
||||
assertThat(headers).doesNotContainKey("foo").doesNotContainKey("hello").containsKeys("bar", "abc");
|
||||
|
||||
@@ -265,9 +283,12 @@ public class ProductionConfigurationTests {
|
||||
@Test
|
||||
@SuppressWarnings({ "Duplicates", "unchecked" })
|
||||
public void testSensitiveHeadersDefault() {
|
||||
Map<String, List<String>> headers = rest.exchange(RequestEntity
|
||||
Map<String, List<String>> headers = rest
|
||||
.exchange(RequestEntity
|
||||
.get(rest.getRestTemplate().getUriTemplateHandler().expand("/proxy/sensitive-headers-default"))
|
||||
.header("cookie", "monster").build(), Map.class).getBody();
|
||||
.header("cookie", "monster")
|
||||
.build(), Map.class)
|
||||
.getBody();
|
||||
|
||||
assertThat(headers).doesNotContainKey("cookie");
|
||||
}
|
||||
@@ -276,9 +297,12 @@ public class ProductionConfigurationTests {
|
||||
@SuppressWarnings({ "Duplicates", "unchecked" })
|
||||
public void headers() {
|
||||
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)
|
||||
.getBody();
|
||||
.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.get("bar")).containsOnly("hello");
|
||||
@@ -289,9 +313,10 @@ public class ProductionConfigurationTests {
|
||||
@Test
|
||||
public void forwardedHeaderUsesHost() {
|
||||
Map<String, List<String>> headers = rest
|
||||
.exchange(RequestEntity.get(rest.getRestTemplate().getUriTemplateHandler().expand("/proxy/headers"))
|
||||
.header("host", "foo:1234").build(), Map.class)
|
||||
.getBody();
|
||||
.exchange(RequestEntity.get(rest.getRestTemplate().getUriTemplateHandler().expand("/proxy/headers"))
|
||||
.header("host", "foo:1234")
|
||||
.build(), Map.class)
|
||||
.getBody();
|
||||
|
||||
assertThat(headers).containsKey("forwarded");
|
||||
assertThat(headers.get("forwarded").size()).isEqualTo(1);
|
||||
@@ -381,9 +406,11 @@ public class ProductionConfigurationTests {
|
||||
@PostMapping("/proxy/converter")
|
||||
public ResponseEntity<Bar> implicitEntityWithConverter(@RequestBody Foo foo,
|
||||
ProxyExchange<List<Bar>> proxy) {
|
||||
return proxy.uri(home.toString() + "/bars").body(Arrays.asList(foo))
|
||||
.post(response -> ResponseEntity.status(response.getStatusCode()).headers(response.getHeaders())
|
||||
.body(response.getBody().iterator().next()));
|
||||
return proxy.uri(home.toString() + "/bars")
|
||||
.body(Arrays.asList(foo))
|
||||
.post(response -> ResponseEntity.status(response.getStatusCode())
|
||||
.headers(response.getHeaders())
|
||||
.body(response.getBody().iterator().next()));
|
||||
}
|
||||
|
||||
@PostMapping("/proxy/no-body")
|
||||
@@ -399,8 +426,11 @@ public class ProductionConfigurationTests {
|
||||
@DeleteMapping("/proxy/{id}")
|
||||
public ResponseEntity<?> deleteWithBody(@PathVariable Integer id, @RequestBody Foo foo,
|
||||
ProxyExchange<?> proxy) {
|
||||
return proxy.uri(home.toString() + "/foos/" + id).body(foo).delete(response -> ResponseEntity
|
||||
.status(response.getStatusCode()).headers(response.getHeaders()).body(response.getBody()));
|
||||
return proxy.uri(home.toString() + "/foos/" + id)
|
||||
.body(foo)
|
||||
.delete(response -> ResponseEntity.status(response.getStatusCode())
|
||||
.headers(response.getHeaders())
|
||||
.body(response.getBody()));
|
||||
}
|
||||
|
||||
@GetMapping("/forward/**")
|
||||
@@ -461,8 +491,9 @@ public class ProductionConfigurationTests {
|
||||
}
|
||||
|
||||
private <T> ResponseEntity<T> first(ResponseEntity<List<T>> response) {
|
||||
return ResponseEntity.status(response.getStatusCode()).headers(response.getHeaders())
|
||||
.body(response.getBody().iterator().next());
|
||||
return ResponseEntity.status(response.getStatusCode())
|
||||
.headers(response.getHeaders())
|
||||
.body(response.getBody().iterator().next());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -165,8 +165,9 @@ public class GatewaySampleApplication {
|
||||
@Bean
|
||||
public RouterFunction<ServerResponse> testWhenMetricPathIsNotMeet() {
|
||||
RouterFunction<ServerResponse> route = RouterFunctions.route(
|
||||
RequestPredicates.path("/actuator/metrics/spring.cloud.gateway.requests"), request -> ServerResponse
|
||||
.ok().body(BodyInserters.fromValue(HELLO_FROM_FAKE_ACTUATOR_METRICS_GATEWAY_REQUESTS)));
|
||||
RequestPredicates.path("/actuator/metrics/spring.cloud.gateway.requests"),
|
||||
request -> ServerResponse.ok()
|
||||
.body(BodyInserters.fromValue(HELLO_FROM_FAKE_ACTUATOR_METRICS_GATEWAY_REQUESTS)));
|
||||
return route;
|
||||
}
|
||||
|
||||
|
||||
@@ -52,8 +52,10 @@ public class ThrottleGatewayFilter implements GatewayFilter {
|
||||
}
|
||||
synchronized (this) {
|
||||
if (tokenBucket == null) {
|
||||
tokenBucket = TokenBuckets.builder().withCapacity(capacity)
|
||||
.withFixedIntervalRefillStrategy(refillTokens, refillPeriod, refillUnit).build();
|
||||
tokenBucket = TokenBuckets.builder()
|
||||
.withCapacity(capacity)
|
||||
.withFixedIntervalRefillStrategy(refillTokens, refillPeriod, refillUnit)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
return tokenBucket;
|
||||
|
||||
@@ -92,100 +92,171 @@ 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")
|
||||
.expectBody(Map.class)
|
||||
.consumeWith(result -> assertThat(result.getResponseBody()).containsEntry("data", "HELLOHELLO"));
|
||||
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"));
|
||||
}
|
||||
|
||||
@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")
|
||||
.expectBody(Map.class)
|
||||
.consumeWith(result -> assertThat(result.getResponseBody()).containsEntry("DATA", "HELLO"));
|
||||
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"));
|
||||
}
|
||||
|
||||
@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")
|
||||
.expectBody(String.class)
|
||||
.consumeWith(result -> assertThat(result.getResponseBody()).isEqualTo("hello"));
|
||||
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"));
|
||||
}
|
||||
|
||||
@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 routeFromKotlin() {
|
||||
webClient.get().uri("/anything/kotlinroute").header("Host", "kotlin.abc.org").exchange().expectHeader()
|
||||
.valueEquals("X-TestHeader", "foobar").expectStatus().isOk();
|
||||
webClient.get()
|
||||
.uri("/anything/kotlinroute")
|
||||
.header("Host", "kotlin.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();
|
||||
String metricName = metricsProperties.getPrefix() + ".requests";
|
||||
webClient.get().uri("http://localhost:" + managementPort + "/actuator/metrics/" + metricName).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")
|
||||
.isEqualTo(metricName);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
});
|
||||
webClient.get()
|
||||
.uri("http://localhost:" + managementPort + "/actuator/metrics/" + metricName)
|
||||
.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")
|
||||
.isEqualTo(metricName);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
|
||||
@@ -62,16 +62,21 @@ public class GatewaySampleApplicationWithoutMetricsTests {
|
||||
|
||||
protected ConfigurableApplicationContext init(Class<?> config) {
|
||||
return new SpringApplicationBuilder().web(WebApplicationType.REACTIVE)
|
||||
.sources(GatewaySampleApplication.class, config).run();
|
||||
.sources(GatewaySampleApplication.class, config)
|
||||
.run();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void actuatorMetrics() {
|
||||
init(TestConfig.class);
|
||||
webClient.get().uri("/get").exchange().expectStatus().isOk();
|
||||
webClient.get().uri("http://localhost:" + port + "/actuator/metrics/spring.cloud.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/spring.cloud.gateway.requests")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(String.class)
|
||||
.isEqualTo(GatewaySampleApplication.HELLO_FROM_FAKE_ACTUATOR_METRICS_GATEWAY_REQUESTS);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ public class ArgumentSupplierBeanPostProcessor implements BeanPostProcessor {
|
||||
routerFunction.accept(routerFunctionVisitor);
|
||||
if (predicateVisitor.argumentSupplier != null) {
|
||||
ArgumentSuppliedEvent<?> argumentSuppliedEvent = predicateVisitor.argumentSupplier
|
||||
.getArgumentSuppliedEvent();
|
||||
.getArgumentSuppliedEvent();
|
||||
if (predicateVisitor.attributes != null) {
|
||||
argumentSuppliedEvent = new AttributedArugmentSuppliedEvent<>(argumentSuppliedEvent,
|
||||
predicateVisitor.attributes);
|
||||
|
||||
@@ -144,7 +144,7 @@ public abstract class MvcUtils {
|
||||
|
||||
public static ApplicationContext getApplicationContext(ServerRequest request) {
|
||||
WebApplicationContext webApplicationContext = RequestContextUtils
|
||||
.findWebApplicationContext(request.servletRequest());
|
||||
.findWebApplicationContext(request.servletRequest());
|
||||
if (webApplicationContext == null) {
|
||||
throw new IllegalStateException("No Application Context in request attributes");
|
||||
}
|
||||
@@ -165,16 +165,16 @@ public abstract class MvcUtils {
|
||||
// attribute resetting in RequestPredicates
|
||||
// computeIfAbsent if the used vanilla RouterFunctions.route()
|
||||
Map<String, Object> attributes = (Map<String, Object>) request.attributes()
|
||||
.computeIfAbsent(GATEWAY_ATTRIBUTES_ATTR, s -> new HashMap<String, Object>());
|
||||
.computeIfAbsent(GATEWAY_ATTRIBUTES_ATTR, s -> new HashMap<String, Object>());
|
||||
return attributes;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Map<String, Object> getUriTemplateVariables(ServerRequest request) {
|
||||
Map<String, Object> reqUriTemplateVars = (Map<String, Object>) request.attributes()
|
||||
.get(URI_TEMPLATE_VARIABLES_ATTRIBUTE);
|
||||
.get(URI_TEMPLATE_VARIABLES_ATTRIBUTE);
|
||||
Map<String, Object> gatewayUriTemplateVars = (Map<String, Object>) getGatewayAttributes(request)
|
||||
.get(URI_TEMPLATE_VARIABLES_ATTRIBUTE);
|
||||
.get(URI_TEMPLATE_VARIABLES_ATTRIBUTE);
|
||||
Map<String, Object> merged = mergeMaps(reqUriTemplateVars, gatewayUriTemplateVars);
|
||||
return merged;
|
||||
}
|
||||
|
||||
@@ -80,8 +80,10 @@ public class WeightConfig {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("routeId", routeId).append("group", group).append("weight", weight)
|
||||
.toString();
|
||||
return new ToStringCreator(this).append("routeId", routeId)
|
||||
.append("group", group)
|
||||
.append("weight", weight)
|
||||
.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ public class GatewayMvcAotRuntimeHintsRegistrar implements RuntimeHintsRegistrar
|
||||
return; // safety net
|
||||
}
|
||||
Arrays.stream(clazz.getMethods())
|
||||
.forEach(method -> reflectionHints.registerMethod(method, ExecutableMode.INVOKE));
|
||||
.forEach(method -> reflectionHints.registerMethod(method, ExecutableMode.INVOKE));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -73,8 +73,10 @@ public class GatewayMvcProperties {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("httpClient", httpClient).append("routes", routes)
|
||||
.append("routesMap", routesMap).toString();
|
||||
return new ToStringCreator(this).append("httpClient", httpClient)
|
||||
.append("routes", routes)
|
||||
.append("routesMap", routesMap)
|
||||
.toString();
|
||||
}
|
||||
|
||||
public static class HttpClient {
|
||||
@@ -125,8 +127,11 @@ public class GatewayMvcProperties {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("connectTimeout", connectTimeout).append("readTimeout", readTimeout)
|
||||
.append("sslBundle", sslBundle).append("type", type).toString();
|
||||
return new ToStringCreator(this).append("connectTimeout", connectTimeout)
|
||||
.append("readTimeout", readTimeout)
|
||||
.append("sslBundle", sslBundle)
|
||||
.append("type", type)
|
||||
.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -53,9 +53,9 @@ public class GatewayMvcPropertiesBeanDefinitionRegistrar implements ImportBeanDe
|
||||
// Registers RouterFunctionHolderFactory::routerFunctionHolderSupplier so when the
|
||||
// bean is refreshed, that method is called again.
|
||||
AbstractBeanDefinition routerFnProviderBeanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(RouterFunctionHolder.class)
|
||||
.setFactoryMethodOnBean("routerFunctionHolderSupplier", "routerFunctionHolderFactory")
|
||||
.getBeanDefinition();
|
||||
.rootBeanDefinition(RouterFunctionHolder.class)
|
||||
.setFactoryMethodOnBean("routerFunctionHolderSupplier", "routerFunctionHolderFactory")
|
||||
.getBeanDefinition();
|
||||
BeanDefinitionHolder holder = new BeanDefinitionHolder(routerFnProviderBeanDefinition,
|
||||
"gatewayRouterFunctionHolder");
|
||||
BeanDefinitionHolder proxy = ScopedProxyUtils.createScopedProxy(holder, registry, true);
|
||||
@@ -73,7 +73,8 @@ public class GatewayMvcPropertiesBeanDefinitionRegistrar implements ImportBeanDe
|
||||
// holder can be refreshed and all config based routes will be reloaded.
|
||||
|
||||
AbstractBeanDefinition routerFunctionBeanDefinition = BeanDefinitionBuilder
|
||||
.genericBeanDefinition(DelegatingRouterFunction.class).getBeanDefinition();
|
||||
.genericBeanDefinition(DelegatingRouterFunction.class)
|
||||
.getBeanDefinition();
|
||||
registry.registerBeanDefinition("gatewayCompositeRouterFunction", routerFunctionBeanDefinition);
|
||||
}
|
||||
|
||||
|
||||
@@ -109,8 +109,8 @@ public class RouterFunctionHolderFactory {
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
private GatewayMvcPropertiesBeanDefinitionRegistrar.RouterFunctionHolder routerFunctionHolderSupplier() {
|
||||
GatewayMvcProperties properties = Binder.get(env).bindOrCreate(GatewayMvcProperties.PREFIX,
|
||||
GatewayMvcProperties.class);
|
||||
GatewayMvcProperties properties = Binder.get(env)
|
||||
.bindOrCreate(GatewayMvcProperties.PREFIX, GatewayMvcProperties.class);
|
||||
log.trace(LogMessage.format("RouterFunctionHolder initializing with %d map routes and %d list routes",
|
||||
properties.getRoutesMap().size(), properties.getRoutes().size()));
|
||||
|
||||
@@ -242,9 +242,11 @@ public class RouterFunctionHolderFactory {
|
||||
|
||||
private Optional<NormalizedOperationMethod> findOperation(MultiValueMap<String, OperationMethod> operations,
|
||||
String operationName, Map<String, Object> operationArgs) {
|
||||
return operations.getOrDefault(operationName, Collections.emptyList()).stream()
|
||||
.map(operationMethod -> new NormalizedOperationMethod(operationMethod, operationArgs))
|
||||
.filter(opeMethod -> matchOperation(opeMethod, operationArgs)).findFirst();
|
||||
return operations.getOrDefault(operationName, Collections.emptyList())
|
||||
.stream()
|
||||
.map(operationMethod -> new NormalizedOperationMethod(operationMethod, operationArgs))
|
||||
.filter(opeMethod -> matchOperation(opeMethod, operationArgs))
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
private static boolean matchOperation(NormalizedOperationMethod operationMethod, Map<String, Object> args) {
|
||||
@@ -291,7 +293,7 @@ public class RouterFunctionHolderFactory {
|
||||
}
|
||||
Bindable<?> bindable = Bindable.of(configurableType);
|
||||
List<ConfigurationPropertySource> propertySources = Collections
|
||||
.singletonList(new MapConfigurationPropertySource(args));
|
||||
.singletonList(new MapConfigurationPropertySource(args));
|
||||
// TODO: potentially deal with conversion service
|
||||
Binder binder = new Binder(propertySources, null, DefaultConversionService.getSharedInstance());
|
||||
Object config = binder.bindOrCreate("", bindable, new IgnoreTopLevelConverterNotFoundBindHandler());
|
||||
|
||||
@@ -116,7 +116,8 @@ public abstract class AfterFilterFunctions {
|
||||
return (request, response) -> {
|
||||
response.headers().computeIfPresent(name, (key, values) -> {
|
||||
List<String> rewrittenValues = values.stream()
|
||||
.map(value -> pattern.matcher(value).replaceAll(replacement)).toList();
|
||||
.map(value -> pattern.matcher(value).replaceAll(replacement))
|
||||
.toList();
|
||||
return new ArrayList<>(rewrittenValues);
|
||||
});
|
||||
return response;
|
||||
|
||||
@@ -105,8 +105,11 @@ public abstract class BeforeFilterFunctions {
|
||||
return request -> {
|
||||
ServerRequest.Builder requestBuilder = ServerRequest.from(request);
|
||||
newHeaders.forEach((newHeaderName, newHeaderValues) -> {
|
||||
boolean headerIsMissingOrBlank = request.headers().asHttpHeaders().getOrEmpty(newHeaderName).stream()
|
||||
.allMatch(h -> !StringUtils.hasText(h));
|
||||
boolean headerIsMissingOrBlank = request.headers()
|
||||
.asHttpHeaders()
|
||||
.getOrEmpty(newHeaderName)
|
||||
.stream()
|
||||
.allMatch(h -> !StringUtils.hasText(h));
|
||||
if (headerIsMissingOrBlank) {
|
||||
requestBuilder.headers(httpHeaders -> {
|
||||
List<String> expandedValues = MvcUtils.expandMultiple(request, newHeaderValues);
|
||||
@@ -134,20 +137,22 @@ public abstract class BeforeFilterFunctions {
|
||||
Consumer<FallbackHeadersConfig> configConsumer) {
|
||||
FallbackHeadersConfig config = new FallbackHeadersConfig();
|
||||
configConsumer.accept(config);
|
||||
return request -> request.attribute(CIRCUITBREAKER_EXECUTION_EXCEPTION_ATTR).map(Throwable.class::cast)
|
||||
.map(throwable -> ServerRequest.from(request).headers(httpHeaders -> {
|
||||
httpHeaders.add(config.getExecutionExceptionTypeHeaderName(), throwable.getClass().getName());
|
||||
if (throwable.getMessage() != null) {
|
||||
httpHeaders.add(config.getExecutionExceptionMessageHeaderName(), throwable.getMessage());
|
||||
return request -> request.attribute(CIRCUITBREAKER_EXECUTION_EXCEPTION_ATTR)
|
||||
.map(Throwable.class::cast)
|
||||
.map(throwable -> ServerRequest.from(request).headers(httpHeaders -> {
|
||||
httpHeaders.add(config.getExecutionExceptionTypeHeaderName(), throwable.getClass().getName());
|
||||
if (throwable.getMessage() != null) {
|
||||
httpHeaders.add(config.getExecutionExceptionMessageHeaderName(), throwable.getMessage());
|
||||
}
|
||||
Throwable rootCause = getRootCause(throwable);
|
||||
if (rootCause != null) {
|
||||
httpHeaders.add(config.getRootCauseExceptionTypeHeaderName(), rootCause.getClass().getName());
|
||||
if (rootCause.getMessage() != null) {
|
||||
httpHeaders.add(config.getRootCauseExceptionMessageHeaderName(), rootCause.getMessage());
|
||||
}
|
||||
Throwable rootCause = getRootCause(throwable);
|
||||
if (rootCause != null) {
|
||||
httpHeaders.add(config.getRootCauseExceptionTypeHeaderName(), rootCause.getClass().getName());
|
||||
if (rootCause.getMessage() != null) {
|
||||
httpHeaders.add(config.getRootCauseExceptionMessageHeaderName(), rootCause.getMessage());
|
||||
}
|
||||
}
|
||||
}).build()).orElse(request);
|
||||
}
|
||||
}).build())
|
||||
.orElse(request);
|
||||
}
|
||||
|
||||
private static Throwable getRootCause(Throwable throwable) {
|
||||
@@ -211,7 +216,9 @@ public abstract class BeforeFilterFunctions {
|
||||
|
||||
// remove from uri
|
||||
URI newUri = UriComponentsBuilder.fromUri(request.uri())
|
||||
.replaceQueryParams(unmodifiableMultiValueMap(queryParams)).build().toUri();
|
||||
.replaceQueryParams(unmodifiableMultiValueMap(queryParams))
|
||||
.build()
|
||||
.toUri();
|
||||
|
||||
// remove resolved params from request
|
||||
return ServerRequest.from(request).params(params -> params.remove(name)).uri(newUri).build();
|
||||
@@ -252,7 +259,7 @@ public abstract class BeforeFilterFunctions {
|
||||
StringBuilder errorMessage = new StringBuilder(
|
||||
String.format(REQUEST_HEADER_SIZE_ERROR_PREFIX, maxSize));
|
||||
longHeaders.forEach((header, size) -> errorMessage
|
||||
.append(String.format(REQUEST_HEADER_SIZE_ERROR, header, DataSize.of(size, DataUnit.BYTES))));
|
||||
.append(String.format(REQUEST_HEADER_SIZE_ERROR, header, DataSize.of(size, DataUnit.BYTES))));
|
||||
|
||||
throw new ResponseStatusException(HttpStatus.REQUEST_HEADER_FIELDS_TOO_LARGE, errorMessage.toString()) {
|
||||
@Override
|
||||
@@ -398,8 +405,10 @@ public abstract class BeforeFilterFunctions {
|
||||
}
|
||||
// TODO: end duplicate code from StripPrefixGatewayFilterFactory
|
||||
|
||||
URI prefixedUri = UriComponentsBuilder.fromUri(request.uri()).replacePath(newPath.toString()).build()
|
||||
.toUri();
|
||||
URI prefixedUri = UriComponentsBuilder.fromUri(request.uri())
|
||||
.replacePath(newPath.toString())
|
||||
.build()
|
||||
.toUri();
|
||||
return ServerRequest.from(request).uri(prefixedUri).build();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -128,7 +128,8 @@ public abstract class BodyFilterFunctions {
|
||||
ByteArrayHttpOutputMessage outputMessage = new ByteArrayHttpOutputMessage(headers);
|
||||
((HttpMessageConverter<R>) messageConverter).write(convertedBody, contentType, outputMessage);
|
||||
ServerRequest modified = ServerRequest.from(request)
|
||||
.headers(httpHeaders -> httpHeaders.putAll(headers)).build();
|
||||
.headers(httpHeaders -> httpHeaders.putAll(headers))
|
||||
.build();
|
||||
return wrapRequest(modified, outputMessage.getBytes());
|
||||
}
|
||||
catch (IOException e) {
|
||||
|
||||
@@ -45,9 +45,12 @@ public abstract class Bucket4jFilterFunctions {
|
||||
public static final String DEFAULT_HEADER_NAME = "X-RateLimit-Remaining";
|
||||
|
||||
private static final Function<RateLimitConfig, BucketConfiguration> DEFAULT_CONFIGURATION_BUILDER = config -> BucketConfiguration
|
||||
.builder().addLimit(Bandwidth.builder().capacity(config.getCapacity())
|
||||
.refillGreedy(config.getCapacity(), config.getPeriod()).build())
|
||||
.build();
|
||||
.builder()
|
||||
.addLimit(Bandwidth.builder()
|
||||
.capacity(config.getCapacity())
|
||||
.refillGreedy(config.getCapacity(), config.getPeriod())
|
||||
.build())
|
||||
.build();
|
||||
|
||||
private Bucket4jFilterFunctions() {
|
||||
}
|
||||
@@ -87,7 +90,8 @@ public abstract class Bucket4jFilterFunctions {
|
||||
return serverResponse;
|
||||
}
|
||||
return ServerResponse.status(config.getStatusCode())
|
||||
.header(config.getHeaderName(), String.valueOf(remainingTokens)).build();
|
||||
.header(config.getHeaderName(), String.valueOf(remainingTokens))
|
||||
.build();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -71,11 +71,13 @@ public abstract class CircuitBreakerFilterFunctions {
|
||||
@Shortcut
|
||||
@Configurable
|
||||
public static HandlerFilterFunction<ServerResponse, ServerResponse> circuitBreaker(CircuitBreakerConfig config) {
|
||||
Set<HttpStatusCode> failureStatuses = config.getStatusCodes().stream()
|
||||
.map(status -> HttpStatusHolder.valueOf(status).resolve()).collect(Collectors.toSet());
|
||||
Set<HttpStatusCode> failureStatuses = config.getStatusCodes()
|
||||
.stream()
|
||||
.map(status -> HttpStatusHolder.valueOf(status).resolve())
|
||||
.collect(Collectors.toSet());
|
||||
return (request, next) -> {
|
||||
CircuitBreakerFactory<?, ?> circuitBreakerFactory = MvcUtils.getApplicationContext(request)
|
||||
.getBean(CircuitBreakerFactory.class);
|
||||
.getBean(CircuitBreakerFactory.class);
|
||||
// TODO: cache
|
||||
CircuitBreaker circuitBreaker = circuitBreakerFactory.create(config.getId());
|
||||
return circuitBreaker.run(() -> {
|
||||
@@ -117,8 +119,10 @@ public abstract class CircuitBreakerFilterFunctions {
|
||||
return GatewayServerResponse.ok().build((httpServletRequest, httpServletResponse) -> {
|
||||
try {
|
||||
String expandedFallback = MvcUtils.expand(request, config.getFallbackPath());
|
||||
request.servletRequest().getServletContext().getRequestDispatcher(expandedFallback)
|
||||
.forward(httpServletRequest, httpServletResponse);
|
||||
request.servletRequest()
|
||||
.getServletContext()
|
||||
.getRequestDispatcher(expandedFallback)
|
||||
.forward(httpServletRequest, httpServletResponse);
|
||||
return null;
|
||||
}
|
||||
catch (ServletException | IOException e) {
|
||||
|
||||
@@ -117,8 +117,8 @@ public interface FilterFunctions {
|
||||
static HandlerFilterFunction<ServerResponse, ServerResponse> redirectTo(HttpStatusHolder status, URI uri) {
|
||||
Assert.isTrue(status.is3xxRedirection(), "status must be a 3xx code, but was " + status);
|
||||
|
||||
return (request, next) -> ServerResponse.status(status.resolve()).header(HttpHeaders.LOCATION, uri.toString())
|
||||
.build();
|
||||
return (request,
|
||||
next) -> ServerResponse.status(status.resolve()).header(HttpHeaders.LOCATION, uri.toString()).build();
|
||||
}
|
||||
|
||||
@Shortcut
|
||||
@@ -162,9 +162,11 @@ public interface FilterFunctions {
|
||||
@Shortcut
|
||||
static HandlerFilterFunction<ServerResponse, ServerResponse> rewriteLocationResponseHeader(String stripVersion,
|
||||
String locationHeaderName, String hostValue, String protocolsRegex) {
|
||||
return ofResponseProcessor(RewriteLocationResponseHeaderFilterFunctions.rewriteLocationResponseHeader(
|
||||
config -> config.setStripVersion(stripVersion).setLocationHeaderName(locationHeaderName)
|
||||
.setHostValue(hostValue).setProtocolsRegex(protocolsRegex)));
|
||||
return ofResponseProcessor(RewriteLocationResponseHeaderFilterFunctions
|
||||
.rewriteLocationResponseHeader(config -> config.setStripVersion(stripVersion)
|
||||
.setLocationHeaderName(locationHeaderName)
|
||||
.setHostValue(hostValue)
|
||||
.setProtocolsRegex(protocolsRegex)));
|
||||
}
|
||||
|
||||
@Shortcut
|
||||
|
||||
@@ -121,7 +121,7 @@ public class FormFilter implements Filter, Ordered {
|
||||
UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUriString(requestURL.toString());
|
||||
MultiValueMap<String, String> queryParams = uriComponentsBuilder.build().getQueryParams();
|
||||
for (Iterator<Map.Entry<String, String[]>> entryIterator = form.entrySet().iterator(); entryIterator
|
||||
.hasNext();) {
|
||||
.hasNext();) {
|
||||
Map.Entry<String, String[]> entry = entryIterator.next();
|
||||
String name = entry.getKey();
|
||||
List<String> values = Arrays.asList(entry.getValue());
|
||||
|
||||
@@ -64,10 +64,10 @@ public abstract class LoadBalancerFilterFunctions {
|
||||
BiFunction<ServiceInstance, URI, URI> reconstructUriFunction) {
|
||||
return (request, next) -> {
|
||||
LoadBalancerClientFactory clientFactory = getApplicationContext(request)
|
||||
.getBean(LoadBalancerClientFactory.class);
|
||||
.getBean(LoadBalancerClientFactory.class);
|
||||
Set<LoadBalancerLifecycle> supportedLifecycleProcessors = LoadBalancerLifecycleValidator
|
||||
.getSupportedLifecycleProcessors(clientFactory.getInstances(serviceId, LoadBalancerLifecycle.class),
|
||||
RequestDataContext.class, ResponseData.class, ServiceInstance.class);
|
||||
.getSupportedLifecycleProcessors(clientFactory.getInstances(serviceId, LoadBalancerLifecycle.class),
|
||||
RequestDataContext.class, ResponseData.class, ServiceInstance.class);
|
||||
RequestData requestData = new RequestData(request.method(), request.uri(),
|
||||
request.headers().asHttpHeaders(), buildCookies(request.cookies()), request.attributes());
|
||||
DefaultRequest<RequestDataContext> lbRequest = new DefaultRequest<>(
|
||||
@@ -82,7 +82,7 @@ public abstract class LoadBalancerFilterFunctions {
|
||||
ServiceInstance retrievedInstance = loadBalancerClient.choose(serviceId, lbRequest);
|
||||
if (retrievedInstance == null) {
|
||||
supportedLifecycleProcessors.forEach(lifecycle -> lifecycle
|
||||
.onComplete(new CompletionContext<>(CompletionContext.Status.DISCARD, lbRequest)));
|
||||
.onComplete(new CompletionContext<>(CompletionContext.Status.DISCARD, lbRequest)));
|
||||
throw new HttpServerErrorException(HttpStatus.SERVICE_UNAVAILABLE,
|
||||
"Unable to find instance for " + serviceId);
|
||||
// throw NotFoundException.create(properties.isUse404(), "Unable to find
|
||||
@@ -110,9 +110,9 @@ public abstract class LoadBalancerFilterFunctions {
|
||||
|
||||
try {
|
||||
ServerResponse serverResponse = next.handle(request);
|
||||
supportedLifecycleProcessors.forEach(
|
||||
lifecycle -> lifecycle.onComplete(new CompletionContext<>(CompletionContext.Status.SUCCESS,
|
||||
lbRequest, defaultResponse, serverResponse)));
|
||||
supportedLifecycleProcessors
|
||||
.forEach(lifecycle -> lifecycle.onComplete(new CompletionContext<>(CompletionContext.Status.SUCCESS,
|
||||
lbRequest, defaultResponse, serverResponse)));
|
||||
return serverResponse;
|
||||
}
|
||||
catch (Exception e) {
|
||||
@@ -136,7 +136,7 @@ public abstract class LoadBalancerFilterFunctions {
|
||||
HttpHeaders newCookies = new HttpHeaders();
|
||||
if (cookies != null) {
|
||||
cookies.forEach((key, value) -> value
|
||||
.forEach(cookie -> newCookies.put(cookie.getName(), Collections.singletonList(cookie.getValue()))));
|
||||
.forEach(cookie -> newCookies.put(cookie.getName(), Collections.singletonList(cookie.getValue()))));
|
||||
}
|
||||
return newCookies;
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ public abstract class RetryFilterFunctions {
|
||||
config.getExceptions().forEach(exception -> retryableExceptions.put(exception, true));
|
||||
SimpleRetryPolicy simpleRetryPolicy = new SimpleRetryPolicy(config.getRetries(), retryableExceptions);
|
||||
compositeRetryPolicy
|
||||
.setPolicies(Arrays.asList(simpleRetryPolicy, new HttpRetryPolicy(config)).toArray(new RetryPolicy[0]));
|
||||
.setPolicies(Arrays.asList(simpleRetryPolicy, new HttpRetryPolicy(config)).toArray(new RetryPolicy[0]));
|
||||
RetryTemplate retryTemplate = retryTemplateBuilder.customPolicy(compositeRetryPolicy).build();
|
||||
return (request, next) -> retryTemplate.execute(context -> {
|
||||
ServerResponse serverResponse = next.handle(request);
|
||||
|
||||
@@ -42,13 +42,16 @@ public abstract class TokenRelayFilterFunctions {
|
||||
if (principle instanceof OAuth2AuthenticationToken token) {
|
||||
String clientRegistrationId = token.getAuthorizedClientRegistrationId();
|
||||
OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest
|
||||
.withClientRegistrationId(clientRegistrationId).principal(token).build();
|
||||
.withClientRegistrationId(clientRegistrationId)
|
||||
.principal(token)
|
||||
.build();
|
||||
OAuth2AuthorizedClientManager clientManager = getApplicationContext(request)
|
||||
.getBean(OAuth2AuthorizedClientManager.class);
|
||||
.getBean(OAuth2AuthorizedClientManager.class);
|
||||
OAuth2AuthorizedClient authorizedClient = clientManager.authorize(authorizeRequest);
|
||||
OAuth2AccessToken accessToken = authorizedClient.getAccessToken();
|
||||
ServerRequest modified = ServerRequest.from(request)
|
||||
.headers(httpHeaders -> httpHeaders.setBearerAuth(accessToken.getTokenValue())).build();
|
||||
.headers(httpHeaders -> httpHeaders.setBearerAuth(accessToken.getTokenValue()))
|
||||
.build();
|
||||
return next.handle(modified);
|
||||
}
|
||||
return next.handle(request);
|
||||
|
||||
@@ -254,8 +254,11 @@ public class WeightCalculatorFilter implements Filter, Ordered, SmartApplication
|
||||
|
||||
@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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -47,20 +47,20 @@ public class ClientHttpRequestFactoryProxyExchange implements ProxyExchange {
|
||||
// put the body input stream in a request attribute so filters can read it.
|
||||
MvcUtils.putAttribute(request.getServerRequest(), MvcUtils.CLIENT_RESPONSE_INPUT_STREAM_ATTR, body);
|
||||
ServerResponse serverResponse = GatewayServerResponse.status(clientHttpResponse.getStatusCode())
|
||||
.build((req, httpServletResponse) -> {
|
||||
try (clientHttpResponse) {
|
||||
// get input stream from request attribute in case it was
|
||||
// modified.
|
||||
InputStream inputStream = MvcUtils.getAttribute(request.getServerRequest(),
|
||||
MvcUtils.CLIENT_RESPONSE_INPUT_STREAM_ATTR);
|
||||
// copy body from request to clientHttpRequest
|
||||
StreamUtils.copy(inputStream, httpServletResponse.getOutputStream());
|
||||
}
|
||||
return null;
|
||||
});
|
||||
.build((req, httpServletResponse) -> {
|
||||
try (clientHttpResponse) {
|
||||
// get input stream from request attribute in case it was
|
||||
// modified.
|
||||
InputStream inputStream = MvcUtils.getAttribute(request.getServerRequest(),
|
||||
MvcUtils.CLIENT_RESPONSE_INPUT_STREAM_ATTR);
|
||||
// copy body from request to clientHttpRequest
|
||||
StreamUtils.copy(inputStream, httpServletResponse.getOutputStream());
|
||||
}
|
||||
return null;
|
||||
});
|
||||
ClientHttpResponseAdapter proxyExchangeResponse = new ClientHttpResponseAdapter(clientHttpResponse);
|
||||
request.getResponseConsumers()
|
||||
.forEach(responseConsumer -> responseConsumer.accept(proxyExchangeResponse, serverResponse));
|
||||
.forEach(responseConsumer -> responseConsumer.accept(proxyExchangeResponse, serverResponse));
|
||||
return serverResponse;
|
||||
}
|
||||
catch (IOException e) {
|
||||
|
||||
@@ -268,10 +268,10 @@ final class GatewayEntityResponseBuilder<T> implements EntityResponse.Builder<T>
|
||||
entityType = RESOURCE_REGION_LIST_TYPE;
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
serverResponse.getHeaders().set(HttpHeaders.CONTENT_RANGE,
|
||||
"bytes */" + resource.contentLength());
|
||||
serverResponse.getHeaders()
|
||||
.set(HttpHeaders.CONTENT_RANGE, "bytes */" + resource.contentLength());
|
||||
serverResponse.getServletResponse()
|
||||
.setStatus(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE.value());
|
||||
.setStatus(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -316,9 +316,10 @@ final class GatewayEntityResponseBuilder<T> implements EntityResponse.Builder<T>
|
||||
private static List<MediaType> producibleMediaTypes(List<HttpMessageConverter<?>> messageConverters,
|
||||
Class<?> entityClass) {
|
||||
|
||||
return messageConverters.stream().filter(messageConverter -> messageConverter.canWrite(entityClass, null))
|
||||
.flatMap(messageConverter -> messageConverter.getSupportedMediaTypes(entityClass).stream())
|
||||
.toList();
|
||||
return messageConverters.stream()
|
||||
.filter(messageConverter -> messageConverter.canWrite(entityClass, null))
|
||||
.flatMap(messageConverter -> messageConverter.getSupportedMediaTypes(entityClass).stream())
|
||||
.toList();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ abstract class GatewayErrorHandlingServerResponse implements ServerResponse {
|
||||
for (ErrorHandler<?> errorHandler : this.errorHandlers) {
|
||||
if (errorHandler.test(t)) {
|
||||
ServerRequest serverRequest = (ServerRequest) servletRequest
|
||||
.getAttribute(RouterFunctions.REQUEST_ATTRIBUTE);
|
||||
.getAttribute(RouterFunctions.REQUEST_ATTRIBUTE);
|
||||
return errorHandler.handle(t, serverRequest);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ public interface GatewayServerResponse extends ServerResponse {
|
||||
*/
|
||||
static ServerResponse from(ErrorResponse response) {
|
||||
return status(response.getStatusCode()).headers(headers -> headers.putAll(response.getHeaders()))
|
||||
.body(response.getBody());
|
||||
.body(response.getBody());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -173,30 +173,38 @@ class GatewayServerResponseBuilder implements ServerResponse.BodyBuilder {
|
||||
|
||||
@Override
|
||||
public ServerResponse body(Object body) {
|
||||
return GatewayEntityResponseBuilder.fromObject(body).status(this.statusCode)
|
||||
.headers(headers -> headers.putAll(this.headers)).cookies(cookies -> cookies.addAll(this.cookies))
|
||||
.build();
|
||||
return GatewayEntityResponseBuilder.fromObject(body)
|
||||
.status(this.statusCode)
|
||||
.headers(headers -> headers.putAll(this.headers))
|
||||
.cookies(cookies -> cookies.addAll(this.cookies))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ServerResponse body(T body, ParameterizedTypeReference<T> bodyType) {
|
||||
return GatewayEntityResponseBuilder.fromObject(body, bodyType).status(this.statusCode)
|
||||
.headers(headers -> headers.putAll(this.headers)).cookies(cookies -> cookies.addAll(this.cookies))
|
||||
.build();
|
||||
return GatewayEntityResponseBuilder.fromObject(body, bodyType)
|
||||
.status(this.statusCode)
|
||||
.headers(headers -> headers.putAll(this.headers))
|
||||
.cookies(cookies -> cookies.addAll(this.cookies))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerResponse render(String name, Object... modelAttributes) {
|
||||
return new GatewayRenderingResponseBuilder(name).status(this.statusCode)
|
||||
.headers(headers -> headers.putAll(this.headers)).cookies(cookies -> cookies.addAll(this.cookies))
|
||||
.modelAttributes(modelAttributes).build();
|
||||
.headers(headers -> headers.putAll(this.headers))
|
||||
.cookies(cookies -> cookies.addAll(this.cookies))
|
||||
.modelAttributes(modelAttributes)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerResponse render(String name, Map<String, ?> model) {
|
||||
return new GatewayRenderingResponseBuilder(name).status(this.statusCode)
|
||||
.headers(headers -> headers.putAll(this.headers)).cookies(cookies -> cookies.addAll(this.cookies))
|
||||
.modelAttributes(model).build();
|
||||
.headers(headers -> headers.putAll(this.headers))
|
||||
.cookies(cookies -> cookies.addAll(this.cookies))
|
||||
.modelAttributes(model)
|
||||
.build();
|
||||
}
|
||||
|
||||
private static class WriteFunctionResponse extends AbstractGatewayServerResponse {
|
||||
|
||||
@@ -46,8 +46,10 @@ public abstract class HandlerFunctions {
|
||||
return request -> GatewayServerResponse.ok().build((httpServletRequest, httpServletResponse) -> {
|
||||
try {
|
||||
String expandedFallback = MvcUtils.expand(request, path);
|
||||
request.servletRequest().getServletContext().getRequestDispatcher(expandedFallback)
|
||||
.forward(httpServletRequest, httpServletResponse);
|
||||
request.servletRequest()
|
||||
.getServletContext()
|
||||
.getRequestDispatcher(expandedFallback)
|
||||
.forward(httpServletRequest, httpServletResponse);
|
||||
return null;
|
||||
}
|
||||
catch (ServletException | IOException e) {
|
||||
|
||||
@@ -59,7 +59,7 @@ public class ProxyExchangeHandlerFunction
|
||||
ObjectProvider<ResponseHttpHeadersFilter> responseHttpHeadersFilters) {
|
||||
this(proxyExchange, requestHttpHeadersFilters, responseHttpHeadersFilters,
|
||||
request -> (URI) request.attribute(MvcUtils.GATEWAY_REQUEST_URL_ATTR)
|
||||
.orElseThrow(() -> new IllegalStateException("No routeUri resolved")));
|
||||
.orElseThrow(() -> new IllegalStateException("No routeUri resolved")));
|
||||
}
|
||||
|
||||
public ProxyExchangeHandlerFunction(ProxyExchange proxyExchange,
|
||||
@@ -99,7 +99,7 @@ public class ProxyExchangeHandlerFunction
|
||||
serverRequest.headers().asHttpHeaders(), serverRequest);
|
||||
|
||||
boolean preserveHost = (boolean) serverRequest.attributes()
|
||||
.getOrDefault(MvcUtils.PRESERVE_HOST_HEADER_ATTRIBUTE, false);
|
||||
.getOrDefault(MvcUtils.PRESERVE_HOST_HEADER_ATTRIBUTE, false);
|
||||
if (preserveHost) {
|
||||
filteredRequestHeaders.set(HttpHeaders.HOST, serverRequest.headers().firstHeader(HttpHeaders.HOST));
|
||||
}
|
||||
|
||||
@@ -36,10 +36,11 @@ public class RestClientProxyExchange implements ProxyExchange {
|
||||
|
||||
@Override
|
||||
public ServerResponse exchange(Request request) {
|
||||
return restClient.method(request.getMethod()).uri(request.getUri())
|
||||
.headers(httpHeaders -> httpHeaders.putAll(request.getHeaders()))
|
||||
.body(outputStream -> copyBody(request, outputStream))
|
||||
.exchange((clientRequest, clientResponse) -> doExchange(request, clientResponse), false);
|
||||
return restClient.method(request.getMethod())
|
||||
.uri(request.getUri())
|
||||
.headers(httpHeaders -> httpHeaders.putAll(request.getHeaders()))
|
||||
.body(outputStream -> copyBody(request, outputStream))
|
||||
.exchange((clientRequest, clientResponse) -> doExchange(request, clientResponse), false);
|
||||
}
|
||||
|
||||
private static int copyBody(Request request, OutputStream outputStream) throws IOException {
|
||||
@@ -51,20 +52,20 @@ public class RestClientProxyExchange implements ProxyExchange {
|
||||
// put the body input stream in a request attribute so filters can read it.
|
||||
MvcUtils.putAttribute(request.getServerRequest(), MvcUtils.CLIENT_RESPONSE_INPUT_STREAM_ATTR, body);
|
||||
ServerResponse serverResponse = GatewayServerResponse.status(clientResponse.getStatusCode())
|
||||
.build((req, httpServletResponse) -> {
|
||||
try (clientResponse) {
|
||||
// get input stream from request attribute in case it was
|
||||
// modified.
|
||||
InputStream inputStream = MvcUtils.getAttribute(request.getServerRequest(),
|
||||
MvcUtils.CLIENT_RESPONSE_INPUT_STREAM_ATTR);
|
||||
// copy body from request to clientHttpRequest
|
||||
StreamUtils.copy(inputStream, httpServletResponse.getOutputStream());
|
||||
}
|
||||
return null;
|
||||
});
|
||||
.build((req, httpServletResponse) -> {
|
||||
try (clientResponse) {
|
||||
// get input stream from request attribute in case it was
|
||||
// modified.
|
||||
InputStream inputStream = MvcUtils.getAttribute(request.getServerRequest(),
|
||||
MvcUtils.CLIENT_RESPONSE_INPUT_STREAM_ATTR);
|
||||
// copy body from request to clientHttpRequest
|
||||
StreamUtils.copy(inputStream, httpServletResponse.getOutputStream());
|
||||
}
|
||||
return null;
|
||||
});
|
||||
ClientHttpResponseAdapter proxyExchangeResponse = new ClientHttpResponseAdapter(clientResponse);
|
||||
request.getResponseConsumers()
|
||||
.forEach(responseConsumer -> responseConsumer.accept(proxyExchangeResponse, serverResponse));
|
||||
.forEach(responseConsumer -> responseConsumer.accept(proxyExchangeResponse, serverResponse));
|
||||
return serverResponse;
|
||||
}
|
||||
|
||||
|
||||
@@ -84,10 +84,10 @@ class OperationMethodParameter implements OperationParameter {
|
||||
|
||||
boolean isMandatory(Parameter parameter) {
|
||||
MergedAnnotation<Nonnull> annotation = MergedAnnotations.from(parameter).get(Nonnull.class);
|
||||
return !annotation.isPresent()/*
|
||||
* || annotation.getEnum("when", When.class)
|
||||
* == When.ALWAYS
|
||||
*/;
|
||||
return !annotation
|
||||
.isPresent()/*
|
||||
* || annotation.getEnum("when", When.class) == When.ALWAYS
|
||||
*/;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -88,8 +88,10 @@ public class ReflectiveOperationInvoker implements OperationInvoker {
|
||||
}
|
||||
|
||||
private void validateRequiredParameters(InvocationContext context) {
|
||||
Set<OperationParameter> missing = this.operationMethod.getParameters().stream()
|
||||
.filter((parameter) -> isMissing(context, parameter)).collect(Collectors.toSet());
|
||||
Set<OperationParameter> missing = this.operationMethod.getParameters()
|
||||
.stream()
|
||||
.filter((parameter) -> isMissing(context, parameter))
|
||||
.collect(Collectors.toSet());
|
||||
if (!missing.isEmpty()) {
|
||||
throw new MissingParametersException(missing);
|
||||
}
|
||||
@@ -106,8 +108,10 @@ public class ReflectiveOperationInvoker implements OperationInvoker {
|
||||
}
|
||||
|
||||
private Object[] resolveArguments(InvocationContext context) {
|
||||
return this.operationMethod.getParameters().stream().map((parameter) -> resolveArgument(parameter, context))
|
||||
.toArray();
|
||||
return this.operationMethod.getParameters()
|
||||
.stream()
|
||||
.map((parameter) -> resolveArgument(parameter, context))
|
||||
.toArray();
|
||||
}
|
||||
|
||||
private Object resolveArgument(OperationParameter parameter, InvocationContext context) {
|
||||
@@ -121,8 +125,9 @@ public class ReflectiveOperationInvoker implements OperationInvoker {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("target", this.target).append("method", this.operationMethod)
|
||||
.toString();
|
||||
return new ToStringCreator(this).append("target", this.target)
|
||||
.append("method", this.operationMethod)
|
||||
.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -533,8 +533,8 @@ public abstract class GatewayRequestPredicates {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public boolean test(ServerRequest request) {
|
||||
Map<String, String> weights = (Map<String, String>) request.attributes().getOrDefault(WEIGHT_ATTR,
|
||||
Collections.emptyMap());
|
||||
Map<String, String> weights = (Map<String, String>) request.attributes()
|
||||
.getOrDefault(WEIGHT_ATTR, Collections.emptyMap());
|
||||
|
||||
String routeId = (String) request.attributes().get(GATEWAY_ROUTE_ID_ATTR);
|
||||
if (ObjectUtils.isEmpty(routeId)) {
|
||||
|
||||
@@ -38,26 +38,27 @@ public class GatewayServerMvcAutoConfigurationTests {
|
||||
|
||||
@Test
|
||||
void filterEnabledPropertiesWork() {
|
||||
new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(GatewayServerMvcAutoConfiguration.class,
|
||||
RestTemplateAutoConfiguration.class, RestClientAutoConfiguration.class, SslAutoConfiguration.class))
|
||||
.withPropertyValues("spring.cloud.gateway.mvc.form-filter.enabled=false",
|
||||
"spring.cloud.gateway.mvc.forwarded-request-headers-filter.enabled=false",
|
||||
"spring.cloud.gateway.mvc.remove-content-length-request-headers-filter.enabled=false",
|
||||
"spring.cloud.gateway.mvc.remove-hop-by-hop-request-headers-filter.enabled=false",
|
||||
"spring.cloud.gateway.mvc.remove-hop-by-hop-response-headers-filter.enabled=false",
|
||||
"spring.cloud.gateway.mvc.transfer-encoding-normalization-request-headers-filter.enabled=false",
|
||||
"spring.cloud.gateway.mvc.weight-calculator-filter.enabled=false",
|
||||
"spring.cloud.gateway.mvc.x-forwarded-request-headers-filter.enabled=false")
|
||||
.run(context -> {
|
||||
assertThat(context).doesNotHaveBean(FormFilter.class);
|
||||
assertThat(context).doesNotHaveBean(ForwardedRequestHeadersFilter.class);
|
||||
assertThat(context).doesNotHaveBean(RemoveContentLengthRequestHeadersFilter.class);
|
||||
assertThat(context).doesNotHaveBean(RemoveHopByHopRequestHeadersFilter.class);
|
||||
assertThat(context).doesNotHaveBean(RemoveHopByHopResponseHeadersFilter.class);
|
||||
assertThat(context).doesNotHaveBean(TransferEncodingNormalizationRequestHeadersFilter.class);
|
||||
assertThat(context).doesNotHaveBean(WeightCalculatorFilter.class);
|
||||
assertThat(context).doesNotHaveBean(XForwardedRequestHeadersFilter.class);
|
||||
});
|
||||
new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(GatewayServerMvcAutoConfiguration.class,
|
||||
RestTemplateAutoConfiguration.class, RestClientAutoConfiguration.class, SslAutoConfiguration.class))
|
||||
.withPropertyValues("spring.cloud.gateway.mvc.form-filter.enabled=false",
|
||||
"spring.cloud.gateway.mvc.forwarded-request-headers-filter.enabled=false",
|
||||
"spring.cloud.gateway.mvc.remove-content-length-request-headers-filter.enabled=false",
|
||||
"spring.cloud.gateway.mvc.remove-hop-by-hop-request-headers-filter.enabled=false",
|
||||
"spring.cloud.gateway.mvc.remove-hop-by-hop-response-headers-filter.enabled=false",
|
||||
"spring.cloud.gateway.mvc.transfer-encoding-normalization-request-headers-filter.enabled=false",
|
||||
"spring.cloud.gateway.mvc.weight-calculator-filter.enabled=false",
|
||||
"spring.cloud.gateway.mvc.x-forwarded-request-headers-filter.enabled=false")
|
||||
.run(context -> {
|
||||
assertThat(context).doesNotHaveBean(FormFilter.class);
|
||||
assertThat(context).doesNotHaveBean(ForwardedRequestHeadersFilter.class);
|
||||
assertThat(context).doesNotHaveBean(RemoveContentLengthRequestHeadersFilter.class);
|
||||
assertThat(context).doesNotHaveBean(RemoveHopByHopRequestHeadersFilter.class);
|
||||
assertThat(context).doesNotHaveBean(RemoveHopByHopResponseHeadersFilter.class);
|
||||
assertThat(context).doesNotHaveBean(TransferEncodingNormalizationRequestHeadersFilter.class);
|
||||
assertThat(context).doesNotHaveBean(WeightCalculatorFilter.class);
|
||||
assertThat(context).doesNotHaveBean(XForwardedRequestHeadersFilter.class);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -178,131 +178,208 @@ public class ServerMvcIntegrationTests {
|
||||
|
||||
@Test
|
||||
public void addRequestParameterWorks() {
|
||||
restClient.get().uri("/anything/addrequestparam").exchange().expectStatus().isOk().expectBody(Map.class)
|
||||
.consumeWith(res -> {
|
||||
Map<String, Object> map = res.getResponseBody();
|
||||
Map<String, Object> args = getMap(map, "args");
|
||||
assertThat(args).containsEntry("param1", "param1val");
|
||||
});
|
||||
restClient.get()
|
||||
.uri("/anything/addrequestparam")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(Map.class)
|
||||
.consumeWith(res -> {
|
||||
Map<String, Object> map = res.getResponseBody();
|
||||
Map<String, Object> args = getMap(map, "args");
|
||||
assertThat(args).containsEntry("param1", "param1val");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeHopByHopRequestHeadersFilterWorks() {
|
||||
restClient.get().uri("/anything/removehopbyhoprequestheaders").exchange().expectStatus().isOk()
|
||||
.expectBody(Map.class).consumeWith(res -> {
|
||||
Map<String, Object> map = res.getResponseBody();
|
||||
Map<String, Object> headers = getMap(map, "headers");
|
||||
assertThat(headers).doesNotContainKeys("x-application-context");
|
||||
});
|
||||
restClient.get()
|
||||
.uri("/anything/removehopbyhoprequestheaders")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(Map.class)
|
||||
.consumeWith(res -> {
|
||||
Map<String, Object> map = res.getResponseBody();
|
||||
Map<String, Object> headers = getMap(map, "headers");
|
||||
assertThat(headers).doesNotContainKeys("x-application-context");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setPathWorks() {
|
||||
restClient.get().uri("/mycustompathextra1").exchange().expectStatus().isOk().expectBody(Map.class)
|
||||
.consumeWith(res -> {
|
||||
Map<String, Object> map = res.getResponseBody();
|
||||
Map<String, Object> args = getMap(map, "args");
|
||||
assertThat(args).containsEntry("param1", "param1valextra1");
|
||||
});
|
||||
restClient.get()
|
||||
.uri("/mycustompathextra1")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(Map.class)
|
||||
.consumeWith(res -> {
|
||||
Map<String, Object> map = res.getResponseBody();
|
||||
Map<String, Object> args = getMap(map, "args");
|
||||
assertThat(args).containsEntry("param1", "param1valextra1");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setPathPostWorks() {
|
||||
restClient.post().uri("/mycustompathpost").bodyValue("hello").header("Host", "www.setpathpost.org").exchange()
|
||||
.expectStatus().isOk().expectBody(Map.class).consumeWith(res -> {
|
||||
Map<String, Object> map = res.getResponseBody();
|
||||
assertThat(map).containsEntry("data", "hello");
|
||||
});
|
||||
restClient.post()
|
||||
.uri("/mycustompathpost")
|
||||
.bodyValue("hello")
|
||||
.header("Host", "www.setpathpost.org")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(Map.class)
|
||||
.consumeWith(res -> {
|
||||
Map<String, Object> map = res.getResponseBody();
|
||||
assertThat(map).containsEntry("data", "hello");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stripPrefixWorks() {
|
||||
restClient.get().uri("/long/path/to/get").exchange().expectStatus().isOk().expectBody(Map.class)
|
||||
.consumeWith(res -> {
|
||||
Map<String, Object> map = res.getResponseBody();
|
||||
Map<String, Object> headers = getMap(map, "headers");
|
||||
assertThat(headers).containsEntry("X-Test", "stripPrefix");
|
||||
});
|
||||
restClient.get()
|
||||
.uri("/long/path/to/get")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(Map.class)
|
||||
.consumeWith(res -> {
|
||||
Map<String, Object> map = res.getResponseBody();
|
||||
Map<String, Object> headers = getMap(map, "headers");
|
||||
assertThat(headers).containsEntry("X-Test", "stripPrefix");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stripPrefixPostWorks() {
|
||||
restClient.post().uri("/long/path/to/post").bodyValue("hello").header("Host", "www.stripprefixpost.org")
|
||||
.exchange().expectStatus().isOk().expectBody(Map.class).consumeWith(res -> {
|
||||
Map<String, Object> map = res.getResponseBody();
|
||||
assertThat(map).containsEntry("data", "hello");
|
||||
Map<String, Object> headers = getMap(map, "headers");
|
||||
assertThat(headers).containsEntry("X-Test", "stripPrefixPost");
|
||||
});
|
||||
restClient.post()
|
||||
.uri("/long/path/to/post")
|
||||
.bodyValue("hello")
|
||||
.header("Host", "www.stripprefixpost.org")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(Map.class)
|
||||
.consumeWith(res -> {
|
||||
Map<String, Object> map = res.getResponseBody();
|
||||
assertThat(map).containsEntry("data", "hello");
|
||||
Map<String, Object> headers = getMap(map, "headers");
|
||||
assertThat(headers).containsEntry("X-Test", "stripPrefixPost");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setStatusGatewayRouterFunctionWorks() {
|
||||
restClient.get().uri("/status/201").exchange().expectStatus().isEqualTo(HttpStatus.TOO_MANY_REQUESTS)
|
||||
.expectHeader().valueEquals("x-status", "201"); // .expectBody(String.class).isEqualTo("Failed
|
||||
// with 201");
|
||||
restClient.get()
|
||||
.uri("/status/201")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isEqualTo(HttpStatus.TOO_MANY_REQUESTS)
|
||||
.expectHeader()
|
||||
.valueEquals("x-status", "201"); // .expectBody(String.class).isEqualTo("Failed
|
||||
// with 201");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addResponseHeaderWorks() {
|
||||
restClient.get().uri("/anything/addresheader").exchange().expectStatus().isOk().expectBody(Map.class)
|
||||
.consumeWith(res -> {
|
||||
Map<String, Object> map = res.getResponseBody();
|
||||
Map<String, Object> headers = getMap(map, "headers");
|
||||
assertThat(headers).doesNotContainKey("x-bar");
|
||||
assertThat(res.getResponseHeaders()).containsEntry("x-bar", Collections.singletonList("val1"));
|
||||
});
|
||||
restClient.get()
|
||||
.uri("/anything/addresheader")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(Map.class)
|
||||
.consumeWith(res -> {
|
||||
Map<String, Object> map = res.getResponseBody();
|
||||
Map<String, Object> headers = getMap(map, "headers");
|
||||
assertThat(headers).doesNotContainKey("x-bar");
|
||||
assertThat(res.getResponseHeaders()).containsEntry("x-bar", Collections.singletonList("val1"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postWorks() {
|
||||
restClient.post().uri("/post").bodyValue("Post Value").header("test", "post").exchange().expectStatus().isOk()
|
||||
.expectBody(Map.class).consumeWith(res -> {
|
||||
Map<String, Object> map = res.getResponseBody();
|
||||
assertThat(map).isNotEmpty().containsEntry("data", "Post Value");
|
||||
});
|
||||
restClient.post()
|
||||
.uri("/post")
|
||||
.bodyValue("Post Value")
|
||||
.header("test", "post")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(Map.class)
|
||||
.consumeWith(res -> {
|
||||
Map<String, Object> map = res.getResponseBody();
|
||||
assertThat(map).isNotEmpty().containsEntry("data", "Post Value");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadbalancerWorks() {
|
||||
restClient.get().uri("/anything/loadbalancer").exchange().expectStatus().isOk().expectBody(Map.class)
|
||||
.consumeWith(res -> {
|
||||
Map<String, Object> map = res.getResponseBody();
|
||||
Map<String, Object> headers = getMap(map, "headers");
|
||||
assertThat(headers).containsEntry("X-Test", "loadbalancer");
|
||||
});
|
||||
restClient.get()
|
||||
.uri("/anything/loadbalancer")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(Map.class)
|
||||
.consumeWith(res -> {
|
||||
Map<String, Object> map = res.getResponseBody();
|
||||
Map<String, Object> headers = getMap(map, "headers");
|
||||
assertThat(headers).containsEntry("X-Test", "loadbalancer");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hostPredicateWorks() {
|
||||
String host = "www1.myjavadslhost.com";
|
||||
restClient.get().uri("/anything/hostpredicate").header("Host", host).exchange().expectStatus().isOk()
|
||||
.expectHeader().valueEquals("X-SubDomain", "www1").expectBody(Map.class).consumeWith(res -> {
|
||||
Map<String, Object> map = res.getResponseBody();
|
||||
Map<String, Object> headers = getMap(map, "headers");
|
||||
assertThat(headers).containsEntry("Host", host);
|
||||
});
|
||||
restClient.get()
|
||||
.uri("/anything/hostpredicate")
|
||||
.header("Host", host)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectHeader()
|
||||
.valueEquals("X-SubDomain", "www1")
|
||||
.expectBody(Map.class)
|
||||
.consumeWith(res -> {
|
||||
Map<String, Object> map = res.getResponseBody();
|
||||
Map<String, Object> headers = getMap(map, "headers");
|
||||
assertThat(headers).containsEntry("Host", host);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void circuitBreakerFallbackWorks() {
|
||||
restClient.get().uri("/anything/circuitbreakerfallback").exchange().expectStatus().isOk()
|
||||
.expectBody(String.class).isEqualTo("Hello");
|
||||
restClient.get()
|
||||
.uri("/anything/circuitbreakerfallback")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(String.class)
|
||||
.isEqualTo("Hello");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void circuitBreakerGatewayFallbackWorks() {
|
||||
restClient.get().uri("/anything/circuitbreakergatewayfallback").exchange().expectStatus().isOk()
|
||||
.expectBody(Map.class).consumeWith(res -> {
|
||||
Map<String, Object> headers = getMap(res.getResponseBody(), "headers");
|
||||
assertThat(headers).containsKeys(CB_EXECUTION_EXCEPTION_TYPE, CB_EXECUTION_EXCEPTION_MESSAGE);
|
||||
});
|
||||
restClient.get()
|
||||
.uri("/anything/circuitbreakergatewayfallback")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(Map.class)
|
||||
.consumeWith(res -> {
|
||||
Map<String, Object> headers = getMap(res.getResponseBody(), "headers");
|
||||
assertThat(headers).containsKeys(CB_EXECUTION_EXCEPTION_TYPE, CB_EXECUTION_EXCEPTION_MESSAGE);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void circuitBreakerNoFallbackWorks() {
|
||||
restClient.get().uri("/anything/circuitbreakernofallback").exchange().expectStatus()
|
||||
.isEqualTo(HttpStatus.GATEWAY_TIMEOUT);
|
||||
restClient.get()
|
||||
.uri("/anything/circuitbreakernofallback")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isEqualTo(HttpStatus.GATEWAY_TIMEOUT);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -320,8 +397,13 @@ public class ServerMvcIntegrationTests {
|
||||
restClient.get().uri("/retry?key=get").exchange().expectStatus().isOk().expectBody(String.class).isEqualTo("3");
|
||||
// test for: java.lang.IllegalArgumentException: You have already selected another
|
||||
// retry policy
|
||||
restClient.get().uri("/retry?key=get2").exchange().expectStatus().isOk().expectBody(String.class)
|
||||
.isEqualTo("3");
|
||||
restClient.get()
|
||||
.uri("/retry?key=get2")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(String.class)
|
||||
.isEqualTo("3");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -333,97 +415,152 @@ public class ServerMvcIntegrationTests {
|
||||
@Test
|
||||
public void headerRegexWorks() {
|
||||
restClient.get().uri("/headerregex").exchange().expectStatus().isNotFound();
|
||||
restClient.get().uri("/headerregex").header("X-MyHeader", "foo").exchange().expectStatus().isOk()
|
||||
.expectBody(Map.class).consumeWith(res -> {
|
||||
Map<String, Object> map = res.getResponseBody();
|
||||
Map<String, Object> headers = getMap(map, "headers");
|
||||
assertThat(headers).containsEntry("X-Myheader", "foo");
|
||||
});
|
||||
restClient.get()
|
||||
.uri("/headerregex")
|
||||
.header("X-MyHeader", "foo")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(Map.class)
|
||||
.consumeWith(res -> {
|
||||
Map<String, Object> map = res.getResponseBody();
|
||||
Map<String, Object> headers = getMap(map, "headers");
|
||||
assertThat(headers).containsEntry("X-Myheader", "foo");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cookieRegexWorks() {
|
||||
restClient.get().uri("/cookieregex").exchange().expectStatus().isNotFound();
|
||||
restClient.get().uri("/cookieregex").cookie("mycookie", "foo").exchange().expectStatus().isOk()
|
||||
.expectBody(Map.class).consumeWith(res -> {
|
||||
Map<String, Object> map = res.getResponseBody();
|
||||
Map<String, Object> headers = getMap(map, "headers");
|
||||
assertThat(headers).containsEntry("Cookie", "mycookie=foo");
|
||||
});
|
||||
restClient.get()
|
||||
.uri("/cookieregex")
|
||||
.cookie("mycookie", "foo")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(Map.class)
|
||||
.consumeWith(res -> {
|
||||
Map<String, Object> map = res.getResponseBody();
|
||||
Map<String, Object> headers = getMap(map, "headers");
|
||||
assertThat(headers).containsEntry("Cookie", "mycookie=foo");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rewritePathWorks() {
|
||||
restClient.get().uri("/foo/get").header("Host", "www.rewritepath.org").exchange().expectStatus().isOk()
|
||||
.expectBody(Map.class).consumeWith(res -> {
|
||||
Map<String, Object> map = res.getResponseBody();
|
||||
Map<String, Object> headers = getMap(map, "headers");
|
||||
assertThat(headers).containsEntry("X-Test", "rewritepath");
|
||||
});
|
||||
restClient.get()
|
||||
.uri("/foo/get")
|
||||
.header("Host", "www.rewritepath.org")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(Map.class)
|
||||
.consumeWith(res -> {
|
||||
Map<String, Object> map = res.getResponseBody();
|
||||
Map<String, Object> headers = getMap(map, "headers");
|
||||
assertThat(headers).containsEntry("X-Test", "rewritepath");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rewritePathPostWorks() {
|
||||
restClient.post().uri("/baz/post").bodyValue("hello").header("Host", "www.rewritepathpost.org").exchange()
|
||||
.expectStatus().isOk().expectBody(Map.class).consumeWith(res -> {
|
||||
Map<String, Object> map = res.getResponseBody();
|
||||
assertThat(map).containsEntry("data", "hello");
|
||||
Map<String, Object> headers = getMap(map, "headers");
|
||||
assertThat(headers).containsEntry("X-Test", "rewritepathpost");
|
||||
});
|
||||
restClient.post()
|
||||
.uri("/baz/post")
|
||||
.bodyValue("hello")
|
||||
.header("Host", "www.rewritepathpost.org")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(Map.class)
|
||||
.consumeWith(res -> {
|
||||
Map<String, Object> map = res.getResponseBody();
|
||||
assertThat(map).containsEntry("data", "hello");
|
||||
Map<String, Object> headers = getMap(map, "headers");
|
||||
assertThat(headers).containsEntry("X-Test", "rewritepathpost");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rewritePathPostLocalWorks() {
|
||||
restClient.post().uri("/baz/post").bodyValue("hello").header("Host", "www.rewritepathpostlocal.org").exchange()
|
||||
.expectStatus().isOk().expectBody(Map.class).consumeWith(res -> {
|
||||
Map<String, Object> map = res.getResponseBody();
|
||||
assertThat(map).containsEntry("data", "hello");
|
||||
Map<String, Object> headers = getMap(map, "headers");
|
||||
assertThat(headers).containsEntry("x-test", "rewritepathpostlocal");
|
||||
});
|
||||
restClient.post()
|
||||
.uri("/baz/post")
|
||||
.bodyValue("hello")
|
||||
.header("Host", "www.rewritepathpostlocal.org")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(Map.class)
|
||||
.consumeWith(res -> {
|
||||
Map<String, Object> map = res.getResponseBody();
|
||||
assertThat(map).containsEntry("data", "hello");
|
||||
Map<String, Object> headers = getMap(map, "headers");
|
||||
assertThat(headers).containsEntry("x-test", "rewritepathpostlocal");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forwardedHeadersWork() {
|
||||
restClient.get().uri("/headers").header("test", "forwarded").exchange().expectStatus().isOk()
|
||||
.expectBody(Map.class).consumeWith(res -> {
|
||||
Map<String, Object> map = res.getResponseBody();
|
||||
Map<String, Object> headers = getMap(map, "headers");
|
||||
assertThat(headers).containsKeys(ForwardedRequestHeadersFilter.FORWARDED_HEADER,
|
||||
XForwardedRequestHeadersFilter.X_FORWARDED_FOR_HEADER,
|
||||
XForwardedRequestHeadersFilter.X_FORWARDED_HOST_HEADER,
|
||||
XForwardedRequestHeadersFilter.X_FORWARDED_PORT_HEADER,
|
||||
XForwardedRequestHeadersFilter.X_FORWARDED_PROTO_HEADER);
|
||||
assertThat(headers.get(ForwardedRequestHeadersFilter.FORWARDED_HEADER)).asString()
|
||||
.contains("proto=http").contains("host=\"localhost:").contains("for=\"127.0.0.1:");
|
||||
assertThat(headers.get(XForwardedRequestHeadersFilter.X_FORWARDED_HOST_HEADER)).asString()
|
||||
.isEqualTo("localhost:" + this.port);
|
||||
assertThat(headers.get(XForwardedRequestHeadersFilter.X_FORWARDED_PORT_HEADER)).asString()
|
||||
.isEqualTo(String.valueOf(this.port));
|
||||
assertThat(headers.get(XForwardedRequestHeadersFilter.X_FORWARDED_PROTO_HEADER).toString())
|
||||
.asString().isEqualTo("http");
|
||||
});
|
||||
restClient.get()
|
||||
.uri("/headers")
|
||||
.header("test", "forwarded")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(Map.class)
|
||||
.consumeWith(res -> {
|
||||
Map<String, Object> map = res.getResponseBody();
|
||||
Map<String, Object> headers = getMap(map, "headers");
|
||||
assertThat(headers).containsKeys(ForwardedRequestHeadersFilter.FORWARDED_HEADER,
|
||||
XForwardedRequestHeadersFilter.X_FORWARDED_FOR_HEADER,
|
||||
XForwardedRequestHeadersFilter.X_FORWARDED_HOST_HEADER,
|
||||
XForwardedRequestHeadersFilter.X_FORWARDED_PORT_HEADER,
|
||||
XForwardedRequestHeadersFilter.X_FORWARDED_PROTO_HEADER);
|
||||
assertThat(headers.get(ForwardedRequestHeadersFilter.FORWARDED_HEADER)).asString()
|
||||
.contains("proto=http")
|
||||
.contains("host=\"localhost:")
|
||||
.contains("for=\"127.0.0.1:");
|
||||
assertThat(headers.get(XForwardedRequestHeadersFilter.X_FORWARDED_HOST_HEADER)).asString()
|
||||
.isEqualTo("localhost:" + this.port);
|
||||
assertThat(headers.get(XForwardedRequestHeadersFilter.X_FORWARDED_PORT_HEADER)).asString()
|
||||
.isEqualTo(String.valueOf(this.port));
|
||||
assertThat(headers.get(XForwardedRequestHeadersFilter.X_FORWARDED_PROTO_HEADER).toString()).asString()
|
||||
.isEqualTo("http");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestSizeWorks() {
|
||||
restClient.post().uri("/post").bodyValue("123456").header("test", "requestsize").exchange().expectStatus()
|
||||
.isEqualTo(HttpStatus.PAYLOAD_TOO_LARGE).expectHeader().valueMatches("errormessage",
|
||||
"Request size is larger than permissible limit. Request size is .* where permissible limit is .*");
|
||||
restClient.post()
|
||||
.uri("/post")
|
||||
.bodyValue("123456")
|
||||
.header("test", "requestsize")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isEqualTo(HttpStatus.PAYLOAD_TOO_LARGE)
|
||||
.expectHeader()
|
||||
.valueMatches("errormessage",
|
||||
"Request size is larger than permissible limit. Request size is .* where permissible limit is .*");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestHeaderSizeWorks() {
|
||||
restClient.get().uri("/headers").header("test", "requestheadersize")
|
||||
.header("X-AnyHeader",
|
||||
"11111111112222222222333333333344444444445555555555666666666677777777778888888888")
|
||||
.exchange().expectStatus().isEqualTo(HttpStatus.REQUEST_HEADER_FIELDS_TOO_LARGE).expectHeader()
|
||||
.valueMatches("errormessage",
|
||||
"Request Header/s size is larger than permissible limit (.*). Request Header/s size for 'x-anyheader' is .*");
|
||||
restClient.get().uri("/headers").header("test", "requestheadersize")
|
||||
.header("X-AnyHeader", "111111111122222222223333333333444444444455555555556666666666").exchange()
|
||||
.expectStatus().isOk();
|
||||
restClient.get()
|
||||
.uri("/headers")
|
||||
.header("test", "requestheadersize")
|
||||
.header("X-AnyHeader", "11111111112222222222333333333344444444445555555555666666666677777777778888888888")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isEqualTo(HttpStatus.REQUEST_HEADER_FIELDS_TOO_LARGE)
|
||||
.expectHeader()
|
||||
.valueMatches("errormessage",
|
||||
"Request Header/s size is larger than permissible limit (.*). Request Header/s size for 'x-anyheader' is .*");
|
||||
restClient.get()
|
||||
.uri("/headers")
|
||||
.header("test", "requestheadersize")
|
||||
.header("X-AnyHeader", "111111111122222222223333333333444444444455555555556666666666")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk();
|
||||
}
|
||||
|
||||
public static final MediaType FORM_URL_ENCODED_CONTENT_TYPE = new MediaType(APPLICATION_FORM_URLENCODED,
|
||||
@@ -469,7 +606,9 @@ public class ServerMvcIntegrationTests {
|
||||
void multipartFormDataRestTemplateWorks() {
|
||||
MultiValueMap<String, HttpEntity<?>> formData = createMultipartData();
|
||||
RequestEntity<MultiValueMap<String, HttpEntity<?>>> request = RequestEntity.post("/post")
|
||||
.contentType(MULTIPART_FORM_DATA).header("Host", "www.testform.org").body(formData);
|
||||
.contentType(MULTIPART_FORM_DATA)
|
||||
.header("Host", "www.testform.org")
|
||||
.body(formData);
|
||||
ResponseEntity<Map> response = restTemplate.exchange(request, Map.class);
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertMultipartData(response.getBody());
|
||||
@@ -477,8 +616,13 @@ public class ServerMvcIntegrationTests {
|
||||
|
||||
@Test
|
||||
public void redirectToWorks() {
|
||||
restClient.get().uri("/anything/redirect").exchange().expectStatus().isEqualTo(HttpStatus.MOVED_PERMANENTLY)
|
||||
.expectHeader().valueEquals(HttpHeaders.LOCATION, "https://exampleredirect.com");
|
||||
restClient.get()
|
||||
.uri("/anything/redirect")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isEqualTo(HttpStatus.MOVED_PERMANENTLY)
|
||||
.expectHeader()
|
||||
.valueEquals(HttpHeaders.LOCATION, "https://exampleredirect.com");
|
||||
}
|
||||
|
||||
private MultiValueMap<String, HttpEntity<?>> createMultipartData() {
|
||||
@@ -497,41 +641,65 @@ public class ServerMvcIntegrationTests {
|
||||
|
||||
@Test
|
||||
public void removeRequestHeaderWorks() {
|
||||
restClient.get().uri("/anything/removerequestheader").header("X-Request-Foo", "Bar").exchange().expectStatus()
|
||||
.isOk().expectBody(Map.class).consumeWith(res -> {
|
||||
Map<String, Object> map = res.getResponseBody();
|
||||
Map<String, Object> headers = getMap(map, "headers");
|
||||
assertThat(headers).doesNotContainKey("X-Request-Foo");
|
||||
});
|
||||
restClient.get()
|
||||
.uri("/anything/removerequestheader")
|
||||
.header("X-Request-Foo", "Bar")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(Map.class)
|
||||
.consumeWith(res -> {
|
||||
Map<String, Object> map = res.getResponseBody();
|
||||
Map<String, Object> headers = getMap(map, "headers");
|
||||
assertThat(headers).doesNotContainKey("X-Request-Foo");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setRequestHeaderWorks() {
|
||||
restClient.get().uri("/headers").header("test", "setrequestheader").exchange().expectStatus().isOk()
|
||||
.expectBody(Map.class).consumeWith(res -> {
|
||||
Map<String, Object> headers = getMap(res.getResponseBody(), "headers");
|
||||
assertThat(headers).doesNotContainEntry("X-Test", "value1");
|
||||
assertThat(headers).containsEntry("X-Test", "value2");
|
||||
});
|
||||
restClient.get()
|
||||
.uri("/headers")
|
||||
.header("test", "setrequestheader")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(Map.class)
|
||||
.consumeWith(res -> {
|
||||
Map<String, Object> headers = getMap(res.getResponseBody(), "headers");
|
||||
assertThat(headers).doesNotContainEntry("X-Test", "value1");
|
||||
assertThat(headers).containsEntry("X-Test", "value2");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setRequestHeaderHostWorks() {
|
||||
restClient.get().uri("/headers").header("Host", "www.setrequesthostheader.org").exchange().expectStatus().isOk()
|
||||
.expectBody(Map.class).consumeWith(res -> {
|
||||
Map<String, Object> headers = getMap(res.getResponseBody(), "headers");
|
||||
assertThat(headers).containsEntry("Host", "otherhost.io");
|
||||
});
|
||||
restClient.get()
|
||||
.uri("/headers")
|
||||
.header("Host", "www.setrequesthostheader.org")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(Map.class)
|
||||
.consumeWith(res -> {
|
||||
Map<String, Object> headers = getMap(res.getResponseBody(), "headers");
|
||||
assertThat(headers).containsEntry("Host", "otherhost.io");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setResponseHeaderWorks() {
|
||||
restClient.get().uri("/anything/setresponseheader").header("test", "setresponseheader").exchange()
|
||||
.expectStatus().isOk().expectBody(Map.class).consumeWith(res -> {
|
||||
HttpHeaders headers = res.getResponseHeaders();
|
||||
assertThat(headers).doesNotContainEntry("X-Test", List.of("value1"));
|
||||
assertThat(headers).containsEntry("X-Test", List.of("value2"));
|
||||
});
|
||||
restClient.get()
|
||||
.uri("/anything/setresponseheader")
|
||||
.header("test", "setresponseheader")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(Map.class)
|
||||
.consumeWith(res -> {
|
||||
HttpHeaders headers = res.getResponseHeaders();
|
||||
assertThat(headers).doesNotContainEntry("X-Test", List.of("value1"));
|
||||
assertThat(headers).containsEntry("X-Test", List.of("value2"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -541,38 +709,70 @@ public class ServerMvcIntegrationTests {
|
||||
}
|
||||
|
||||
private void testNestedRoute(String nestedPath) {
|
||||
restClient.get().uri("/anything/nested/" + nestedPath).header("test", "nested").exchange().expectStatus().isOk()
|
||||
.expectBody(Map.class).consumeWith(res -> {
|
||||
Map<String, Object> headers = getMap(res.getResponseBody(), "headers");
|
||||
assertThat(headers).containsEntry("X-Test", nestedPath);
|
||||
});
|
||||
restClient.get()
|
||||
.uri("/anything/nested/" + nestedPath)
|
||||
.header("test", "nested")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(Map.class)
|
||||
.consumeWith(res -> {
|
||||
Map<String, Object> headers = getMap(res.getResponseBody(), "headers");
|
||||
assertThat(headers).containsEntry("X-Test", nestedPath);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeRequestParameterWorks() {
|
||||
restClient.get().uri("/anything/removerequestparameter?foo=bar").header("test", "removerequestparam").exchange()
|
||||
.expectStatus().isOk().expectHeader().doesNotExist("foo");
|
||||
restClient.get()
|
||||
.uri("/anything/removerequestparameter?foo=bar")
|
||||
.header("test", "removerequestparam")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectHeader()
|
||||
.doesNotExist("foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeRequestParameterPostWorks() {
|
||||
restClient.post().uri("/post?foo=bar").bodyValue("hello").header("Host", "www.removerequestparampost.org")
|
||||
.exchange().expectStatus().isOk().expectHeader().doesNotExist("foo").expectBody(Map.class)
|
||||
.consumeWith(res -> {
|
||||
assertThat(res.getResponseBody()).containsEntry("data", "hello");
|
||||
});
|
||||
restClient.post()
|
||||
.uri("/post?foo=bar")
|
||||
.bodyValue("hello")
|
||||
.header("Host", "www.removerequestparampost.org")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectHeader()
|
||||
.doesNotExist("foo")
|
||||
.expectBody(Map.class)
|
||||
.consumeWith(res -> {
|
||||
assertThat(res.getResponseBody()).containsEntry("data", "hello");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeResponseHeaderWorks() {
|
||||
restClient.get().uri("/anything/removeresponseheader").header("test", "removeresponseheader").exchange()
|
||||
.expectStatus().isOk().expectHeader().doesNotExist("X-Test");
|
||||
restClient.get()
|
||||
.uri("/anything/removeresponseheader")
|
||||
.header("test", "removeresponseheader")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectHeader()
|
||||
.doesNotExist("X-Test");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rewriteResponseHeaderWorks() {
|
||||
restClient.get().uri("/headers").header("test", "rewriteresponseheader").exchange().expectStatus().isOk()
|
||||
.expectHeader().valueEquals("X-Request-Foo", "/42?user=ford&password=***&flag=true");
|
||||
restClient.get()
|
||||
.uri("/headers")
|
||||
.header("test", "rewriteresponseheader")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectHeader()
|
||||
.valueEquals("X-Request-Foo", "/42?user=ford&password=***&flag=true");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -589,75 +789,126 @@ public class ServerMvcIntegrationTests {
|
||||
|
||||
@Test
|
||||
public void mapRequestHeaderWorks() {
|
||||
restClient.get().uri("/anything/maprequestheader").header("X-Foo", "fooval").exchange().expectStatus().isOk()
|
||||
.expectBody(Map.class).consumeWith(res -> {
|
||||
Map<String, Object> headers = getMap(res.getResponseBody(), "headers");
|
||||
assertThat(headers).containsEntry("X-Bar", "fooval");
|
||||
});
|
||||
restClient.get()
|
||||
.uri("/anything/maprequestheader")
|
||||
.header("X-Foo", "fooval")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(Map.class)
|
||||
.consumeWith(res -> {
|
||||
Map<String, Object> headers = getMap(res.getResponseBody(), "headers");
|
||||
assertThat(headers).containsEntry("X-Bar", "fooval");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dedupeResponseHeaderWorks() {
|
||||
restClient.get().uri("/headers").header("Host", "www.deduperesponseheader.org").exchange().expectStatus().isOk()
|
||||
.expectHeader().valueEquals("Access-Control-Allow-Credentials", "true").expectHeader()
|
||||
.valueEquals("Access-Control-Allow-Origin", "https://example.org").expectHeader()
|
||||
.valueEquals("Scout-Cookie", "S'mores").expectHeader()
|
||||
.valueEquals("Next-Week-Lottery-Numbers", "4", "2", "42");
|
||||
restClient.get()
|
||||
.uri("/headers")
|
||||
.header("Host", "www.deduperesponseheader.org")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectHeader()
|
||||
.valueEquals("Access-Control-Allow-Credentials", "true")
|
||||
.expectHeader()
|
||||
.valueEquals("Access-Control-Allow-Origin", "https://example.org")
|
||||
.expectHeader()
|
||||
.valueEquals("Scout-Cookie", "S'mores")
|
||||
.expectHeader()
|
||||
.valueEquals("Next-Week-Lottery-Numbers", "4", "2", "42");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addRequestHeadersIfNotPresentWorks() {
|
||||
restClient.get().uri("/headers").header("Host", "www.addrequestheadersifnotpresent.org")
|
||||
.header("X-Request-Beta", "Value1").exchange().expectStatus().isOk().expectBody(Map.class)
|
||||
.consumeWith(res -> {
|
||||
Map<String, Object> headers = getMap(res.getResponseBody(), "headers");
|
||||
// this asserts that Value2 was not added
|
||||
assertThat(headers).containsEntry("X-Request-Beta", "Value1");
|
||||
assertThat(headers).containsKey("X-Request-Acme");
|
||||
List<String> values = (List<String>) headers.get("X-Request-Acme");
|
||||
assertThat(values).hasSize(4).containsOnly("ValueX", "ValueY", "ValueZ", "www");
|
||||
});
|
||||
restClient.get()
|
||||
.uri("/headers")
|
||||
.header("Host", "www.addrequestheadersifnotpresent.org")
|
||||
.header("X-Request-Beta", "Value1")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(Map.class)
|
||||
.consumeWith(res -> {
|
||||
Map<String, Object> headers = getMap(res.getResponseBody(), "headers");
|
||||
// this asserts that Value2 was not added
|
||||
assertThat(headers).containsEntry("X-Request-Beta", "Value1");
|
||||
assertThat(headers).containsKey("X-Request-Acme");
|
||||
List<String> values = (List<String>) headers.get("X-Request-Acme");
|
||||
assertThat(values).hasSize(4).containsOnly("ValueX", "ValueY", "ValueZ", "www");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rewriteLocationResponseHeaderWorks() {
|
||||
restClient.get().uri("/anything/rewritelocationresponseheader")
|
||||
.header("Host", "test1.rewritelocationresponseheader.org").exchange().expectStatus().isOk()
|
||||
.expectHeader()
|
||||
.valueEquals("Location", "https://test1.rewritelocationresponseheader.org/some/object/id");
|
||||
restClient.get()
|
||||
.uri("/anything/rewritelocationresponseheader")
|
||||
.header("Host", "test1.rewritelocationresponseheader.org")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectHeader()
|
||||
.valueEquals("Location", "https://test1.rewritelocationresponseheader.org/some/object/id");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readBodyWorks() {
|
||||
Event messageEvent = new Event("message", "bar");
|
||||
|
||||
restClient.post().uri("/events").bodyValue(messageEvent).exchange().expectStatus().isOk().expectHeader()
|
||||
.valueEquals("X-Foo", "message").expectBody(Event.class)
|
||||
.consumeWith(res -> assertThat(res.getResponseBody()).isEqualTo(messageEvent));
|
||||
restClient.post()
|
||||
.uri("/events")
|
||||
.bodyValue(messageEvent)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectHeader()
|
||||
.valueEquals("X-Foo", "message")
|
||||
.expectBody(Event.class)
|
||||
.consumeWith(res -> assertThat(res.getResponseBody()).isEqualTo(messageEvent));
|
||||
|
||||
Event messageChannelEvent = new Event("message.channel", "baz");
|
||||
|
||||
restClient.post().uri("/events").bodyValue(messageChannelEvent).exchange().expectStatus().isOk().expectHeader()
|
||||
.valueEquals("X-Channel-Foo", "message.channel").expectBody(Event.class)
|
||||
.consumeWith(res -> assertThat(res.getResponseBody()).isEqualTo(messageChannelEvent));
|
||||
restClient.post()
|
||||
.uri("/events")
|
||||
.bodyValue(messageChannelEvent)
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectHeader()
|
||||
.valueEquals("X-Channel-Foo", "message.channel")
|
||||
.expectBody(Event.class)
|
||||
.consumeWith(res -> assertThat(res.getResponseBody()).isEqualTo(messageChannelEvent));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void rewriteRequestBodyStringWorks() {
|
||||
restClient.post().uri("/post").header("Host", "www.modifyrequestbodystring.org").bodyValue("hello").exchange()
|
||||
.expectStatus().isOk().expectBody(Map.class)
|
||||
.consumeWith(result -> assertThat(result.getResponseBody()).containsEntry("data", "HELLOHELLO"));
|
||||
restClient.post()
|
||||
.uri("/post")
|
||||
.header("Host", "www.modifyrequestbodystring.org")
|
||||
.bodyValue("hello")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(Map.class)
|
||||
.consumeWith(result -> assertThat(result.getResponseBody()).containsEntry("data", "HELLOHELLO"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void rewriteRequestBodyObjectWorks() {
|
||||
restClient.post().uri("/post").header("Host", "www.modifyrequestbodyobject.org").bodyValue("hello world")
|
||||
.exchange().expectStatus().isOk().expectBody(Map.class)
|
||||
.consumeWith(result -> assertThat(result.getResponseBody()).containsEntry("data",
|
||||
"{\"message\":\"HELLO WORLD\"}"));
|
||||
restClient.post()
|
||||
.uri("/post")
|
||||
.header("Host", "www.modifyrequestbodyobject.org")
|
||||
.bodyValue("hello world")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(Map.class)
|
||||
.consumeWith(result -> assertThat(result.getResponseBody()).containsEntry("data",
|
||||
"{\"message\":\"HELLO WORLD\"}"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -667,45 +918,66 @@ public class ServerMvcIntegrationTests {
|
||||
|
||||
@Test
|
||||
public void forwardNon200StatusWorks() {
|
||||
restClient.get().uri("/doforward2").exchange().expectStatus().isCreated().expectBody(String.class)
|
||||
.isEqualTo("hello2");
|
||||
restClient.get()
|
||||
.uri("/doforward2")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isCreated()
|
||||
.expectBody(String.class)
|
||||
.isEqualTo("hello2");
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void queryParamWorks() {
|
||||
restClient.get().uri("/get?foo=bar").exchange().expectStatus().isOk().expectBody(Map.class)
|
||||
.consumeWith(result -> {
|
||||
Map responseBody = result.getResponseBody();
|
||||
assertThat(responseBody).containsKey("args");
|
||||
Map args = getMap(responseBody, "args");
|
||||
assertThat(args).containsKey("foo");
|
||||
assertThat(args.get("foo")).isEqualTo("bar");
|
||||
});
|
||||
restClient.get()
|
||||
.uri("/get?foo=bar")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(Map.class)
|
||||
.consumeWith(result -> {
|
||||
Map responseBody = result.getResponseBody();
|
||||
assertThat(responseBody).containsKey("args");
|
||||
Map args = getMap(responseBody, "args");
|
||||
assertThat(args).containsKey("foo");
|
||||
assertThat(args.get("foo")).isEqualTo("bar");
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Test
|
||||
public void queryParamWithSpecialCharactersWorks() {
|
||||
restClient.get().uri("/get?myparam= &intlparam=æøå").exchange().expectStatus().isOk().expectBody(Map.class)
|
||||
.consumeWith(result -> {
|
||||
Map responseBody = result.getResponseBody();
|
||||
assertThat(responseBody).containsKey("args");
|
||||
Map args = getMap(responseBody, "args");
|
||||
assertThat(args).containsKey("myparam");
|
||||
assertThat(args.get("myparam")).isEqualTo(" ");
|
||||
assertThat(args).containsKey("intlparam");
|
||||
assertThat(args.get("intlparam")).isEqualTo("æøå");
|
||||
});
|
||||
restClient.get()
|
||||
.uri("/get?myparam= &intlparam=æøå")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(Map.class)
|
||||
.consumeWith(result -> {
|
||||
Map responseBody = result.getResponseBody();
|
||||
assertThat(responseBody).containsKey("args");
|
||||
Map args = getMap(responseBody, "args");
|
||||
assertThat(args).containsKey("myparam");
|
||||
assertThat(args.get("myparam")).isEqualTo(" ");
|
||||
assertThat(args).containsKey("intlparam");
|
||||
assertThat(args.get("intlparam")).isEqualTo("æøå");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clientResponseBodyAttributeWorks() {
|
||||
restClient.get().uri("/anything/readresponsebody").header("X-Foo", "fooval").exchange().expectStatus().isOk()
|
||||
.expectBody(Map.class).consumeWith(res -> {
|
||||
Map<String, Object> headers = getMap(res.getResponseBody(), "headers");
|
||||
assertThat(headers).containsEntry("X-Foo", "FOOVAL");
|
||||
});
|
||||
restClient.get()
|
||||
.uri("/anything/readresponsebody")
|
||||
.header("X-Foo", "fooval")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(Map.class)
|
||||
.consumeWith(res -> {
|
||||
Map<String, Object> headers = getMap(res.getResponseBody(), "headers");
|
||||
assertThat(headers).containsEntry("X-Foo", "FOOVAL");
|
||||
});
|
||||
}
|
||||
|
||||
@SpringBootConfiguration
|
||||
@@ -742,7 +1014,7 @@ public class ServerMvcIntegrationTests {
|
||||
@Bean
|
||||
public RouterFunction<ServerResponse> nonGatewayRouterFunctions2() {
|
||||
return route(GET("/hello2"), request -> ServerResponse.status(HttpStatus.CREATED).body("hello2"))
|
||||
.withAttribute(MvcUtils.GATEWAY_ROUTE_ID_ATTR, "hello2");
|
||||
.withAttribute(MvcUtils.GATEWAY_ROUTE_ID_ATTR, "hello2");
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -86,8 +86,10 @@ public class TokenRelayFilterFunctionsTests {
|
||||
when(accessToken.getTokenValue()).thenReturn("mytoken");
|
||||
|
||||
ClientRegistration clientRegistration = ClientRegistration.withRegistrationId("myregistrationid")
|
||||
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS).clientId("myclientid")
|
||||
.tokenUri("mytokenuri").build();
|
||||
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
|
||||
.clientId("myclientid")
|
||||
.tokenUri("mytokenuri")
|
||||
.build();
|
||||
OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(clientRegistration, "joe", accessToken);
|
||||
|
||||
when(authorizedClientManager.authorize(any(OAuth2AuthorizeRequest.class))).thenReturn(authorizedClient);
|
||||
|
||||
@@ -56,10 +56,17 @@ public class VanillaRouterFunctionTests {
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Test
|
||||
public void routerFunctionsRouteWorks() {
|
||||
restClient.post().uri("/anything/routerfunctionsroute").header("Host", "www.routerfunctionsroute.org")
|
||||
.bodyValue("hello").exchange().expectStatus().isOk().expectBody(Map.class).consumeWith(result -> {
|
||||
System.out.println();
|
||||
});
|
||||
restClient.post()
|
||||
.uri("/anything/routerfunctionsroute")
|
||||
.header("Host", "www.routerfunctionsroute.org")
|
||||
.bodyValue("hello")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(Map.class)
|
||||
.consumeWith(result -> {
|
||||
System.out.println();
|
||||
});
|
||||
}
|
||||
|
||||
@SpringBootConfiguration
|
||||
|
||||
@@ -65,8 +65,8 @@ public class GatewayMvcPropertiesBeanDefinitionRegistrarTests {
|
||||
void contextLoads(ApplicationContext context) {
|
||||
Map<String, RouterFunction> routerFunctions = getRouterFunctions(context);
|
||||
|
||||
assertThat(routerFunctions).hasSizeGreaterThanOrEqualTo(5).containsKeys("listRoute1", "route1",
|
||||
"route2CustomId", "listRoute2", "listRoute3", "listRoute4");
|
||||
assertThat(routerFunctions).hasSizeGreaterThanOrEqualTo(5)
|
||||
.containsKeys("listRoute1", "route1", "route2CustomId", "listRoute2", "listRoute3", "listRoute4");
|
||||
RouterFunction listRoute1RouterFunction = routerFunctions.get("listRoute1");
|
||||
listRoute1RouterFunction.accept(new AbstractRouterFunctionsVisitor() {
|
||||
@Override
|
||||
@@ -152,7 +152,7 @@ public class GatewayMvcPropertiesBeanDefinitionRegistrarTests {
|
||||
public void attributes(Map<String, Object> attributes) {
|
||||
if (attributes.containsKey("gatewayRouterFunctions")) {
|
||||
Map<String, RouterFunction> map = (Map<String, RouterFunction>) attributes
|
||||
.get("gatewayRouterFunctions");
|
||||
.get("gatewayRouterFunctions");
|
||||
routerFunctionsRef.compareAndSet(null, map);
|
||||
}
|
||||
}
|
||||
@@ -165,21 +165,32 @@ public class GatewayMvcPropertiesBeanDefinitionRegistrarTests {
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void configuredRouteWorks() {
|
||||
restClient.get().uri("/anything/listRoute1").exchange().expectStatus().isOk().expectBody(Map.class)
|
||||
.consumeWith(res -> {
|
||||
Map<String, Object> headers = getMap(res.getResponseBody(), "headers");
|
||||
assertThat(headers).containsEntry("X-Test", "listRoute1");
|
||||
});
|
||||
restClient.get()
|
||||
.uri("/anything/listRoute1")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(Map.class)
|
||||
.consumeWith(res -> {
|
||||
Map<String, Object> headers = getMap(res.getResponseBody(), "headers");
|
||||
assertThat(headers).containsEntry("X-Test", "listRoute1");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void lbRouteWorks() {
|
||||
restClient.get().uri("/anything/listRoute3").header("MyHeaderName", "MyHeaderVal").exchange().expectStatus()
|
||||
.isOk().expectBody(Map.class).consumeWith(res -> {
|
||||
Map<String, Object> headers = getMap(res.getResponseBody(), "headers");
|
||||
assertThat(headers).containsEntry("X-Test", "listRoute3");
|
||||
});
|
||||
restClient.get()
|
||||
.uri("/anything/listRoute3")
|
||||
.header("MyHeaderName", "MyHeaderVal")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(Map.class)
|
||||
.consumeWith(res -> {
|
||||
Map<String, Object> headers = getMap(res.getResponseBody(), "headers");
|
||||
assertThat(headers).containsEntry("X-Test", "listRoute3");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -187,21 +198,28 @@ public class GatewayMvcPropertiesBeanDefinitionRegistrarTests {
|
||||
void refreshWorks(ConfigurableApplicationContext context) {
|
||||
Map<String, RouterFunction> routerFunctions = getRouterFunctions(context);
|
||||
assertThat(routerFunctions).hasSize(6);
|
||||
TestPropertyValues.of("spring.cloud.gateway.mvc.routesMap.route3.uri=https://example3.com",
|
||||
"spring.cloud.gateway.mvc.routesMap.route3.predicates[0].name=Path",
|
||||
"spring.cloud.gateway.mvc.routesMap.route3.predicates[0].args.pattern=/anything/mapRoute3",
|
||||
"spring.cloud.gateway.mvc.routesMap.route3.filters[0].Name=HttpbinUriResolver",
|
||||
"spring.cloud.gateway.mvc.routesMap.route3.filters[1].Name=AddRequestHeader",
|
||||
"spring.cloud.gateway.mvc.routesMap.route3.filters[1].args.name=X-Test",
|
||||
"spring.cloud.gateway.mvc.routesMap.route3.filters[1].args.values=mapRoute3").applyTo(context);
|
||||
TestPropertyValues
|
||||
.of("spring.cloud.gateway.mvc.routesMap.route3.uri=https://example3.com",
|
||||
"spring.cloud.gateway.mvc.routesMap.route3.predicates[0].name=Path",
|
||||
"spring.cloud.gateway.mvc.routesMap.route3.predicates[0].args.pattern=/anything/mapRoute3",
|
||||
"spring.cloud.gateway.mvc.routesMap.route3.filters[0].Name=HttpbinUriResolver",
|
||||
"spring.cloud.gateway.mvc.routesMap.route3.filters[1].Name=AddRequestHeader",
|
||||
"spring.cloud.gateway.mvc.routesMap.route3.filters[1].args.name=X-Test",
|
||||
"spring.cloud.gateway.mvc.routesMap.route3.filters[1].args.values=mapRoute3")
|
||||
.applyTo(context);
|
||||
ContextRefresher contextRefresher = context.getBean(ContextRefresher.class);
|
||||
contextRefresher.refresh();
|
||||
// make http call before getRouterFunction()
|
||||
restClient.get().uri("/anything/mapRoute3").exchange().expectStatus().isOk().expectBody(Map.class)
|
||||
.consumeWith(res -> {
|
||||
Map<String, Object> headers = getMap(res.getResponseBody(), "headers");
|
||||
assertThat(headers).containsEntry("X-Test", "mapRoute3");
|
||||
});
|
||||
restClient.get()
|
||||
.uri("/anything/mapRoute3")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBody(Map.class)
|
||||
.consumeWith(res -> {
|
||||
Map<String, Object> headers = getMap(res.getResponseBody(), "headers");
|
||||
assertThat(headers).containsEntry("X-Test", "mapRoute3");
|
||||
});
|
||||
|
||||
GatewayMvcProperties properties = context.getBean(GatewayMvcProperties.class);
|
||||
assertThat(properties.getRoutesMap()).hasSize(3).containsKey("route3");
|
||||
|
||||
@@ -55,7 +55,9 @@ public class ForwardedRequestHeadersFilterTests {
|
||||
@Test
|
||||
public void forwardedHeaderDoesNotExist() {
|
||||
MockHttpServletRequest servletRequest = MockMvcRequestBuilders.get("http://localhost/get")
|
||||
.remoteAddress("10.0.0.1:80").header(HttpHeaders.HOST, "myhost").buildRequest(null);
|
||||
.remoteAddress("10.0.0.1:80")
|
||||
.header(HttpHeaders.HOST, "myhost")
|
||||
.buildRequest(null);
|
||||
servletRequest.setRemoteHost("10.0.0.1");
|
||||
ServerRequest request = ServerRequest.create(servletRequest, Collections.emptyList());
|
||||
|
||||
@@ -70,16 +72,17 @@ public class ForwardedRequestHeadersFilterTests {
|
||||
assertThat(forwardeds).hasSize(1);
|
||||
Forwarded forwarded = forwardeds.get(0);
|
||||
|
||||
assertThat(forwarded.getValues()).containsEntry("host", "myhost").containsEntry("proto", "http")
|
||||
.containsEntry("for", "\"10.0.0.1:80\"");
|
||||
assertThat(forwarded.getValues()).containsEntry("host", "myhost")
|
||||
.containsEntry("proto", "http")
|
||||
.containsEntry("for", "\"10.0.0.1:80\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forwardedHeaderExists() {
|
||||
MockHttpServletRequest servletRequest = MockMvcRequestBuilders.get("http://localhost/get")
|
||||
.remoteAddress("10.0.0.1:80")
|
||||
.header(FORWARDED_HEADER, "for=12.34.56.78;host=example.com;proto=https; for=23.45.67.89")
|
||||
.buildRequest(null);
|
||||
.remoteAddress("10.0.0.1:80")
|
||||
.header(FORWARDED_HEADER, "for=12.34.56.78;host=example.com;proto=https; for=23.45.67.89")
|
||||
.buildRequest(null);
|
||||
servletRequest.setRemoteHost("10.0.0.1");
|
||||
ServerRequest request = ServerRequest.create(servletRequest, Collections.emptyList());
|
||||
|
||||
@@ -95,17 +98,18 @@ public class ForwardedRequestHeadersFilterTests {
|
||||
Forwarded addedForwardedHeader = forwardeds.get(0);
|
||||
Forwarded existingForwardedHeader = forwardeds.get(1);
|
||||
|
||||
assertThat(existingForwardedHeader.getValues()).containsEntry("proto", "http").containsEntry("for",
|
||||
"\"10.0.0.1:80\"");
|
||||
assertThat(existingForwardedHeader.getValues()).containsEntry("proto", "http")
|
||||
.containsEntry("for", "\"10.0.0.1:80\"");
|
||||
|
||||
assertThat(addedForwardedHeader.getValues()).containsEntry("proto", "https").containsEntry("for",
|
||||
"23.45.67.89");
|
||||
assertThat(addedForwardedHeader.getValues()).containsEntry("proto", "https")
|
||||
.containsEntry("for", "23.45.67.89");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noHostHeader() throws UnknownHostException {
|
||||
MockHttpServletRequest servletRequest = MockMvcRequestBuilders.get("http://localhost/get")
|
||||
.remoteAddress("10.0.0.1:80").buildRequest(null);
|
||||
.remoteAddress("10.0.0.1:80")
|
||||
.buildRequest(null);
|
||||
servletRequest.setRemoteHost("10.0.0.1");
|
||||
ServerRequest request = ServerRequest.create(servletRequest, Collections.emptyList());
|
||||
|
||||
@@ -126,7 +130,9 @@ public class ForwardedRequestHeadersFilterTests {
|
||||
@Test
|
||||
public void correctIPv6RemoteAddressMapping() throws UnknownHostException {
|
||||
MockHttpServletRequest servletRequest = MockMvcRequestBuilders.get("http://localhost/get")
|
||||
.remoteAddress("2001:db8:cafe:0:0:0:0:17:80").header(HttpHeaders.HOST, "myhost").buildRequest(null);
|
||||
.remoteAddress("2001:db8:cafe:0:0:0:0:17:80")
|
||||
.header(HttpHeaders.HOST, "myhost")
|
||||
.buildRequest(null);
|
||||
servletRequest.setRemoteHost("2001:db8:cafe:0:0:0:0:17");
|
||||
ServerRequest request = ServerRequest.create(servletRequest, Collections.emptyList());
|
||||
|
||||
@@ -147,7 +153,8 @@ public class ForwardedRequestHeadersFilterTests {
|
||||
@Test
|
||||
public void unresolvedRemoteAddressFallsBackToHostName() throws UnknownHostException {
|
||||
MockHttpServletRequest servletRequest = MockMvcRequestBuilders.get("http://localhost/get")
|
||||
.remoteAddress("unresolvable-hostname:80").buildRequest(null);
|
||||
.remoteAddress("unresolvable-hostname:80")
|
||||
.buildRequest(null);
|
||||
servletRequest.setRemoteHost("unresolvable-hostname");
|
||||
ServerRequest request = ServerRequest.create(servletRequest, Collections.emptyList());
|
||||
|
||||
@@ -162,8 +169,8 @@ public class ForwardedRequestHeadersFilterTests {
|
||||
assertThat(forwardeds).hasSize(1);
|
||||
Forwarded forwarded = forwardeds.get(0);
|
||||
|
||||
assertThat(forwarded.getValues()).containsEntry("proto", "http").containsEntry("for",
|
||||
"\"unresolvable-hostname:80\"");
|
||||
assertThat(forwarded.getValues()).containsEntry("proto", "http")
|
||||
.containsEntry("for", "\"unresolvable-hostname:80\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -32,7 +32,8 @@ public class TransferEncodingNormalizationRequestHeadersFilterTests {
|
||||
@Test
|
||||
public void noTransferEncodingWithContentLength() {
|
||||
MockHttpServletRequest servletRequest = MockMvcRequestBuilders.post("http://localhost/post")
|
||||
.header(HttpHeaders.CONTENT_LENGTH, "6").buildRequest(null);
|
||||
.header(HttpHeaders.CONTENT_LENGTH, "6")
|
||||
.buildRequest(null);
|
||||
|
||||
HttpHeaders headers = testFilter(ServerRequest.create(servletRequest, Collections.emptyList()));
|
||||
assertThat(headers).containsKey(HttpHeaders.CONTENT_LENGTH).doesNotContainKey(HttpHeaders.TRANSFER_ENCODING);
|
||||
@@ -41,8 +42,9 @@ public class TransferEncodingNormalizationRequestHeadersFilterTests {
|
||||
@Test
|
||||
public void transferEncodingWithContentLength() {
|
||||
MockHttpServletRequest servletRequest = MockMvcRequestBuilders.post("http://localhost/post")
|
||||
.header(HttpHeaders.CONTENT_LENGTH, "6").header(HttpHeaders.TRANSFER_ENCODING, "chunked")
|
||||
.buildRequest(null);
|
||||
.header(HttpHeaders.CONTENT_LENGTH, "6")
|
||||
.header(HttpHeaders.TRANSFER_ENCODING, "chunked")
|
||||
.buildRequest(null);
|
||||
|
||||
HttpHeaders headers = testFilter(ServerRequest.create(servletRequest, Collections.emptyList()));
|
||||
assertThat(headers).doesNotContainKey(HttpHeaders.CONTENT_LENGTH).containsKey(HttpHeaders.TRANSFER_ENCODING);
|
||||
@@ -51,8 +53,9 @@ public class TransferEncodingNormalizationRequestHeadersFilterTests {
|
||||
@Test
|
||||
public void transferEncodingCaseInsensitiveWithContentLength() {
|
||||
MockHttpServletRequest servletRequest = MockMvcRequestBuilders.post("http://localhost/post")
|
||||
.header(HttpHeaders.CONTENT_LENGTH, "6").header(HttpHeaders.TRANSFER_ENCODING, "Chunked ")
|
||||
.buildRequest(null);
|
||||
.header(HttpHeaders.CONTENT_LENGTH, "6")
|
||||
.header(HttpHeaders.TRANSFER_ENCODING, "Chunked ")
|
||||
.buildRequest(null);
|
||||
|
||||
HttpHeaders headers = testFilter(ServerRequest.create(servletRequest, Collections.emptyList()));
|
||||
assertThat(headers).doesNotContainKey(HttpHeaders.CONTENT_LENGTH).containsKey(HttpHeaders.TRANSFER_ENCODING);
|
||||
|
||||
@@ -64,16 +64,28 @@ public class WeightRequestPredicateIntegrationTests {
|
||||
public void highWeight() {
|
||||
filter.setRandomSupplier(getRandom(0.9));
|
||||
|
||||
testClient.get().uri("/get").header(HttpHeaders.HOST, "www.weight-high.org").exchange().expectStatus().isOk()
|
||||
.expectHeader().valueEquals("X-Route", "weight_high_test");
|
||||
testClient.get()
|
||||
.uri("/get")
|
||||
.header(HttpHeaders.HOST, "www.weight-high.org")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectHeader()
|
||||
.valueEquals("X-Route", "weight_high_test");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lowWeight() {
|
||||
filter.setRandomSupplier(getRandom(0.1));
|
||||
|
||||
testClient.get().uri("/get").header(HttpHeaders.HOST, "www.weight-low.org").exchange().expectStatus().isOk()
|
||||
.expectHeader().valueEquals("X-Route", "weight_low_test");
|
||||
testClient.get()
|
||||
.uri("/get")
|
||||
.header(HttpHeaders.HOST, "www.weight-low.org")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectHeader()
|
||||
.valueEquals("X-Route", "weight_low_test");
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
|
||||
@@ -49,7 +49,7 @@ public class HttpbinTestcontainers implements ApplicationContextInitializer<Conf
|
||||
|
||||
public static GenericContainer<?> createContainer() {
|
||||
return new GenericContainer<>(DEFAULT_IMAGE_NAME).withExposedPorts(DEFAULT_PORT)
|
||||
.waitingFor(new HttpWaitStrategy().forPort(DEFAULT_PORT));
|
||||
.waitingFor(new HttpWaitStrategy().forPort(DEFAULT_PORT));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -569,7 +569,8 @@ public class DefaultTestRestClient implements TestRestClient {
|
||||
return "";
|
||||
}
|
||||
Charset charset = Optional.ofNullable(this.result.getResponseHeaders().getContentType())
|
||||
.map(MimeType::getCharset).orElse(StandardCharsets.UTF_8);
|
||||
.map(MimeType::getCharset)
|
||||
.orElse(StandardCharsets.UTF_8);
|
||||
return new String(body, charset);
|
||||
}
|
||||
|
||||
|
||||
@@ -264,8 +264,10 @@ public class ExchangeResult {
|
||||
}
|
||||
|
||||
private String formatHeaders(HttpHeaders headers, String delimiter) {
|
||||
return headers.entrySet().stream().map(entry -> entry.getKey() + ": " + entry.getValue())
|
||||
.collect(Collectors.joining(delimiter));
|
||||
return headers.entrySet()
|
||||
.stream()
|
||||
.map(entry -> entry.getKey() + ": " + entry.getValue())
|
||||
.collect(Collectors.joining(delimiter));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -124,10 +124,10 @@ public class HeaderAssertions {
|
||||
this.exchangeResult.assertWithDiagnostics(() -> {
|
||||
List<String> values = getRequiredValues(name);
|
||||
AssertionErrors
|
||||
.assertTrue(
|
||||
getMessage(name) + " has fewer or more values " + values
|
||||
+ " than number of patterns to match with " + Arrays.toString(patterns),
|
||||
values.size() == patterns.length);
|
||||
.assertTrue(
|
||||
getMessage(name) + " has fewer or more values " + values
|
||||
+ " than number of patterns to match with " + Arrays.toString(patterns),
|
||||
values.size() == patterns.length);
|
||||
for (int i = 0; i < values.size(); i++) {
|
||||
String value = values.get(i);
|
||||
String pattern = patterns[i];
|
||||
|
||||
@@ -150,7 +150,7 @@ public class StatusAssertions {
|
||||
public TestRestClient.ResponseSpec reasonEquals(String reason) {
|
||||
String actual = getReasonPhrase(this.exchangeResult.getStatus());
|
||||
this.exchangeResult
|
||||
.assertWithDiagnostics(() -> AssertionErrors.assertEquals("Response status reason", reason, actual));
|
||||
.assertWithDiagnostics(() -> AssertionErrors.assertEquals("Response status reason", reason, actual));
|
||||
return this.responseSpec;
|
||||
}
|
||||
|
||||
|
||||
@@ -180,8 +180,12 @@ public class XpathAssertions {
|
||||
}
|
||||
|
||||
private String getCharset() {
|
||||
return Optional.of(this.bodySpec.returnResult()).map(EntityExchangeResult::getResponseHeaders)
|
||||
.map(HttpHeaders::getContentType).map(MimeType::getCharset).orElse(StandardCharsets.UTF_8).name();
|
||||
return Optional.of(this.bodySpec.returnResult())
|
||||
.map(EntityExchangeResult::getResponseHeaders)
|
||||
.map(HttpHeaders::getContentType)
|
||||
.map(MimeType::getCharset)
|
||||
.orElse(StandardCharsets.UTF_8)
|
||||
.name();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -120,35 +120,43 @@ public class AbstractGatewayControllerEndpoint implements ApplicationEventPublis
|
||||
getAvailableEndpointsForClass(AbstractGatewayControllerEndpoint.class.getName()),
|
||||
getAvailableEndpointsForClass(GatewayControllerEndpoint.class.getName()));
|
||||
|
||||
return Flux.fromIterable(endpoints).map(p -> p)
|
||||
.flatMap(path -> this.routeLocator.getRoutes().map(r -> generateHref(r, path)).distinct().collectList()
|
||||
.flatMapMany(Flux::fromIterable))
|
||||
.distinct() // Ensure overall uniqueness
|
||||
.collectList();
|
||||
return Flux.fromIterable(endpoints)
|
||||
.map(p -> p)
|
||||
.flatMap(path -> this.routeLocator.getRoutes()
|
||||
.map(r -> generateHref(r, path))
|
||||
.distinct()
|
||||
.collectList()
|
||||
.flatMapMany(Flux::fromIterable))
|
||||
.distinct() // Ensure overall uniqueness
|
||||
.collectList();
|
||||
}
|
||||
|
||||
private List<GatewayEndpointInfo> mergeEndpoints(List<GatewayEndpointInfo> listA, List<GatewayEndpointInfo> listB) {
|
||||
Map<String, List<String>> mergedMap = new HashMap<>();
|
||||
|
||||
Stream.concat(listA.stream(), listB.stream()).forEach(e -> mergedMap
|
||||
.computeIfAbsent(e.getHref(), k -> new ArrayList<>()).addAll(Arrays.asList(e.getMethods())));
|
||||
Stream.concat(listA.stream(), listB.stream())
|
||||
.forEach(e -> mergedMap.computeIfAbsent(e.getHref(), k -> new ArrayList<>())
|
||||
.addAll(Arrays.asList(e.getMethods())));
|
||||
|
||||
return mergedMap.entrySet().stream().map(entry -> new GatewayEndpointInfo(entry.getKey(), entry.getValue()))
|
||||
.collect(Collectors.toList());
|
||||
return mergedMap.entrySet()
|
||||
.stream()
|
||||
.map(entry -> new GatewayEndpointInfo(entry.getKey(), entry.getValue()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<GatewayEndpointInfo> getAvailableEndpointsForClass(String className) {
|
||||
try {
|
||||
MetadataReader metadataReader = simpleMetadataReaderFactory.getMetadataReader(className);
|
||||
Set<MethodMetadata> annotatedMethods = metadataReader.getAnnotationMetadata()
|
||||
.getAnnotatedMethods(RequestMapping.class.getName());
|
||||
.getAnnotatedMethods(RequestMapping.class.getName());
|
||||
|
||||
String gatewayActuatorPath = webEndpointProperties.getBasePath() + "/gateway";
|
||||
return annotatedMethods.stream().map(method -> new GatewayEndpointInfo(gatewayActuatorPath
|
||||
+ ((String[]) method.getAnnotationAttributes(RequestMapping.class.getName()).get("path"))[0],
|
||||
((RequestMethod[]) method.getAnnotationAttributes(RequestMapping.class.getName()).get("method"))[0]
|
||||
.name()))
|
||||
.collect(Collectors.toList());
|
||||
return annotatedMethods.stream()
|
||||
.map(method -> new GatewayEndpointInfo(gatewayActuatorPath
|
||||
+ ((String[]) method.getAnnotationAttributes(RequestMapping.class.getName()).get("path"))[0],
|
||||
((RequestMethod[]) method.getAnnotationAttributes(RequestMapping.class.getName())
|
||||
.get("method"))[0].name()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
catch (IOException exception) {
|
||||
log.warn(exception.getMessage());
|
||||
@@ -186,8 +194,9 @@ public class AbstractGatewayControllerEndpoint implements ApplicationEventPublis
|
||||
}
|
||||
|
||||
private Map<String, Object> convertToMap(List<String> byMetadata) {
|
||||
return byMetadata.stream().map(keyValueStr -> keyValueStr.split(":"))
|
||||
.collect(Collectors.toMap(kv -> kv[0], kv -> kv.length > 1 ? kv[1] : null));
|
||||
return byMetadata.stream()
|
||||
.map(keyValueStr -> keyValueStr.split(":"))
|
||||
.collect(Collectors.toMap(kv -> kv[0], kv -> kv.length > 1 ? kv[1] : null));
|
||||
}
|
||||
|
||||
@GetMapping("/globalfilters")
|
||||
@@ -228,13 +237,14 @@ public class AbstractGatewayControllerEndpoint implements ApplicationEventPublis
|
||||
@SuppressWarnings("unchecked")
|
||||
public Mono<ResponseEntity<Object>> save(@PathVariable String id, @RequestBody RouteDefinition route) {
|
||||
|
||||
return Mono.just(route).doOnNext(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())));
|
||||
return Mono.just(route)
|
||||
.doOnNext(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())));
|
||||
}
|
||||
|
||||
@PostMapping("/routes")
|
||||
@@ -246,11 +256,12 @@ public class AbstractGatewayControllerEndpoint implements ApplicationEventPublis
|
||||
});
|
||||
|
||||
return Flux.fromIterable(routes)
|
||||
.flatMap(routeDefinition -> this.routeDefinitionWriter.save(Mono.just(routeDefinition).map(r -> {
|
||||
log.debug("Saving route: " + routeDefinition);
|
||||
return r;
|
||||
}))).then(Mono.defer(() -> Mono.just(ResponseEntity.ok().build())))
|
||||
.switchIfEmpty(Mono.defer(() -> Mono.just(ResponseEntity.badRequest().build())));
|
||||
.flatMap(routeDefinition -> this.routeDefinitionWriter.save(Mono.just(routeDefinition).map(r -> {
|
||||
log.debug("Saving route: " + routeDefinition);
|
||||
return r;
|
||||
})))
|
||||
.then(Mono.defer(() -> Mono.just(ResponseEntity.ok().build())))
|
||||
.switchIfEmpty(Mono.defer(() -> Mono.just(ResponseEntity.badRequest().build())));
|
||||
}
|
||||
|
||||
private void validateRouteId(RouteDefinition routeDefinition) {
|
||||
@@ -260,11 +271,17 @@ public class AbstractGatewayControllerEndpoint implements ApplicationEventPublis
|
||||
}
|
||||
|
||||
private void validateRouteDefinition(RouteDefinition routeDefinition) {
|
||||
Set<String> unavailableFilterDefinitions = routeDefinition.getFilters().stream().filter(rd -> !isAvailable(rd))
|
||||
.map(FilterDefinition::getName).collect(Collectors.toSet());
|
||||
Set<String> unavailableFilterDefinitions = routeDefinition.getFilters()
|
||||
.stream()
|
||||
.filter(rd -> !isAvailable(rd))
|
||||
.map(FilterDefinition::getName)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
Set<String> unavailablePredicatesDefinitions = routeDefinition.getPredicates().stream()
|
||||
.filter(rd -> !isAvailable(rd)).map(PredicateDefinition::getName).collect(Collectors.toSet());
|
||||
Set<String> unavailablePredicatesDefinitions = routeDefinition.getPredicates()
|
||||
.stream()
|
||||
.filter(rd -> !isAvailable(rd))
|
||||
.map(PredicateDefinition::getName)
|
||||
.collect(Collectors.toSet());
|
||||
if (!unavailableFilterDefinitions.isEmpty()) {
|
||||
handleUnavailableDefinition(FilterDefinition.class.getSimpleName(), unavailableFilterDefinitions);
|
||||
}
|
||||
@@ -298,12 +315,12 @@ public class AbstractGatewayControllerEndpoint implements ApplicationEventPublis
|
||||
|
||||
private boolean isAvailable(FilterDefinition filterDefinition) {
|
||||
return GatewayFilters.stream()
|
||||
.anyMatch(gatewayFilterFactory -> filterDefinition.getName().equals(gatewayFilterFactory.name()));
|
||||
.anyMatch(gatewayFilterFactory -> filterDefinition.getName().equals(gatewayFilterFactory.name()));
|
||||
}
|
||||
|
||||
private boolean isAvailable(PredicateDefinition predicateDefinition) {
|
||||
return routePredicates.stream()
|
||||
.anyMatch(routePredicate -> predicateDefinition.getName().equals(routePredicate.name()));
|
||||
.anyMatch(routePredicate -> predicateDefinition.getName().equals(routePredicate.name()));
|
||||
}
|
||||
|
||||
@DeleteMapping("/routes/{id}")
|
||||
@@ -317,8 +334,9 @@ public class AbstractGatewayControllerEndpoint implements ApplicationEventPublis
|
||||
@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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ public class GatewayLegacyControllerEndpoint extends AbstractGatewayControllerEn
|
||||
@GetMapping("/routes")
|
||||
public Mono<List<Map<String, Object>>> routes() {
|
||||
Mono<Map<String, RouteDefinition>> routeDefs = this.routeDefinitionLocator.getRouteDefinitions()
|
||||
.collectMap(RouteDefinition::getId);
|
||||
.collectMap(RouteDefinition::getId);
|
||||
Mono<List<Route>> routes = this.routeLocator.getRoutes().collectList();
|
||||
return Mono.zip(routeDefs, routes).map(tuple -> {
|
||||
Map<String, RouteDefinition> defs = tuple.getT1();
|
||||
@@ -103,8 +103,11 @@ 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()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ public abstract class AbstractSslConfigurer<T, S> {
|
||||
try {
|
||||
if (ssl.getKeyStore() != null && ssl.getKeyStore().length() > 0) {
|
||||
KeyManagerFactory keyManagerFactory = KeyManagerFactory
|
||||
.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
||||
.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
||||
char[] keyPassword = ssl.getKeyPassword() != null ? ssl.getKeyPassword().toCharArray() : null;
|
||||
|
||||
if (keyPassword == null && ssl.getKeyStorePassword() != null) {
|
||||
|
||||
@@ -106,8 +106,9 @@ class ConfigurableHintsRegistrationProcessor implements BeanFactoryInitializatio
|
||||
|
||||
private static void addGenericsForClass(Set<Class<?>> genericsToAdd, ResolvableType resolvableType) {
|
||||
if (resolvableType.getSuperType().hasGenerics()) {
|
||||
genericsToAdd.addAll(Arrays.stream(resolvableType.getSuperType().getGenerics()).map(ResolvableType::toClass)
|
||||
.collect(Collectors.toSet()));
|
||||
genericsToAdd.addAll(Arrays.stream(resolvableType.getSuperType().getGenerics())
|
||||
.map(ResolvableType::toClass)
|
||||
.collect(Collectors.toSet()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -356,7 +356,7 @@ public class GatewayAutoConfiguration {
|
||||
public GrpcSslConfigurer grpcSslConfigurer(HttpClientProperties properties)
|
||||
throws KeyStoreException, NoSuchAlgorithmException {
|
||||
TrustManagerFactory trustManagerFactory = TrustManagerFactory
|
||||
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||
trustManagerFactory.init(KeyStore.getInstance(KeyStore.getDefaultType()));
|
||||
|
||||
return new GrpcSslConfigurer(properties.getSsl());
|
||||
@@ -792,7 +792,7 @@ public class GatewayAutoConfiguration {
|
||||
HttpClient httpClient) {
|
||||
Supplier<WebsocketClientSpec.Builder> builderSupplier = () -> {
|
||||
WebsocketClientSpec.Builder builder = WebsocketClientSpec.builder()
|
||||
.handlePing(properties.getWebsocket().isProxyPing());
|
||||
.handlePing(properties.getWebsocket().isProxyPing());
|
||||
if (properties.getWebsocket().getMaxFramePayloadLength() != null) {
|
||||
builder.maxFramePayloadLength(properties.getWebsocket().getMaxFramePayloadLength());
|
||||
}
|
||||
@@ -886,19 +886,19 @@ class GatewayHints implements RuntimeHintsRegistrar {
|
||||
return;
|
||||
}
|
||||
hints.reflection()
|
||||
.registerType(TypeReference.of(FilterDefinition.class),
|
||||
hint -> hint.withMembers(MemberCategory.DECLARED_FIELDS, MemberCategory.INVOKE_DECLARED_METHODS,
|
||||
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS))
|
||||
.registerType(TypeReference.of(PredicateDefinition.class),
|
||||
hint -> hint.withMembers(MemberCategory.DECLARED_FIELDS, MemberCategory.INVOKE_DECLARED_METHODS,
|
||||
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS))
|
||||
.registerType(TypeReference.of(AbstractNameValueGatewayFilterFactory.NameValueConfig.class),
|
||||
hint -> hint.withMembers(MemberCategory.DECLARED_FIELDS, MemberCategory.INVOKE_DECLARED_METHODS,
|
||||
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS))
|
||||
.registerType(TypeReference.of(
|
||||
"org.springframework.cloud.gateway.discovery.DiscoveryClientRouteDefinitionLocator$DelegatingServiceInstance"),
|
||||
hint -> hint.withMembers(MemberCategory.DECLARED_FIELDS, MemberCategory.INVOKE_DECLARED_METHODS,
|
||||
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS));
|
||||
.registerType(TypeReference.of(FilterDefinition.class),
|
||||
hint -> hint.withMembers(MemberCategory.DECLARED_FIELDS, MemberCategory.INVOKE_DECLARED_METHODS,
|
||||
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS))
|
||||
.registerType(TypeReference.of(PredicateDefinition.class),
|
||||
hint -> hint.withMembers(MemberCategory.DECLARED_FIELDS, MemberCategory.INVOKE_DECLARED_METHODS,
|
||||
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS))
|
||||
.registerType(TypeReference.of(AbstractNameValueGatewayFilterFactory.NameValueConfig.class),
|
||||
hint -> hint.withMembers(MemberCategory.DECLARED_FIELDS, MemberCategory.INVOKE_DECLARED_METHODS,
|
||||
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS))
|
||||
.registerType(TypeReference
|
||||
.of("org.springframework.cloud.gateway.discovery.DiscoveryClientRouteDefinitionLocator$DelegatingServiceInstance"),
|
||||
hint -> hint.withMembers(MemberCategory.DECLARED_FIELDS, MemberCategory.INVOKE_DECLARED_METHODS,
|
||||
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,8 +27,9 @@ 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")));
|
||||
env.getPropertySources()
|
||||
.addFirst(new MapPropertySource("gateway-properties",
|
||||
Collections.singletonMap("spring.webflux.hiddenmethod.filter.enabled", "false")));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -79,8 +79,10 @@ public class GatewayMetricsProperties {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("enabled", enabled).append("prefix", prefix).append("tags", tags)
|
||||
.toString();
|
||||
return new ToStringCreator(this).append("enabled", enabled)
|
||||
.append("prefix", prefix)
|
||||
.append("tags", tags)
|
||||
.toString();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -105,9 +105,11 @@ public class GatewayProperties {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("routes", routes).append("defaultFilters", defaultFilters)
|
||||
.append("streamingMediaTypes", streamingMediaTypes)
|
||||
.append("failOnRouteDefinitionError", failOnRouteDefinitionError).toString();
|
||||
return new ToStringCreator(this).append("routes", routes)
|
||||
.append("defaultFilters", defaultFilters)
|
||||
.append("streamingMediaTypes", streamingMediaTypes)
|
||||
.append("failOnRouteDefinitionError", failOnRouteDefinitionError)
|
||||
.toString();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,10 @@ public class GatewayReactiveOAuth2AutoConfiguration {
|
||||
ReactiveClientRegistrationRepository clientRegistrationRepository,
|
||||
ServerOAuth2AuthorizedClientRepository authorizedClientRepository) {
|
||||
ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder
|
||||
.builder().authorizationCode().refreshToken().build();
|
||||
.builder()
|
||||
.authorizationCode()
|
||||
.refreshToken()
|
||||
.build();
|
||||
DefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager = new DefaultReactiveOAuth2AuthorizedClientManager(
|
||||
clientRegistrationRepository, authorizedClientRepository);
|
||||
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
|
||||
|
||||
@@ -87,7 +87,7 @@ class GatewayRedisAutoConfiguration {
|
||||
Jackson2JsonRedisSerializer<RouteDefinition> valueSerializer = new Jackson2JsonRedisSerializer<>(
|
||||
RouteDefinition.class);
|
||||
RedisSerializationContext.RedisSerializationContextBuilder<String, RouteDefinition> builder = RedisSerializationContext
|
||||
.newSerializationContext(keySerializer);
|
||||
.newSerializationContext(keySerializer);
|
||||
RedisSerializationContext<String, RouteDefinition> context = builder.value(valueSerializer).build();
|
||||
|
||||
return new ReactiveRedisTemplate<>(factory, context);
|
||||
|
||||
@@ -80,8 +80,8 @@ public class HttpClientFactory extends AbstractFactoryBean<HttpClient> {
|
||||
ConnectionProvider connectionProvider = buildConnectionProvider(properties);
|
||||
|
||||
HttpClient httpClient = HttpClient.create(connectionProvider)
|
||||
// TODO: move customizations to HttpClientCustomizers
|
||||
.httpResponseDecoder(this::httpResponseDecoder);
|
||||
// TODO: move customizations to HttpClientCustomizers
|
||||
.httpResponseDecoder(this::httpResponseDecoder);
|
||||
|
||||
if (serverProperties.getHttp2().isEnabled()) {
|
||||
httpClient = httpClient.protocol(HttpProtocol.HTTP11, HttpProtocol.H2);
|
||||
@@ -170,13 +170,15 @@ public class HttpClientFactory extends AbstractFactoryBean<HttpClient> {
|
||||
// create either Fixed or Elastic pool
|
||||
ConnectionProvider.Builder builder = ConnectionProvider.builder(pool.getName());
|
||||
if (pool.getType() == FIXED) {
|
||||
builder.maxConnections(pool.getMaxConnections()).pendingAcquireMaxCount(-1)
|
||||
.pendingAcquireTimeout(Duration.ofMillis(pool.getAcquireTimeout()));
|
||||
builder.maxConnections(pool.getMaxConnections())
|
||||
.pendingAcquireMaxCount(-1)
|
||||
.pendingAcquireTimeout(Duration.ofMillis(pool.getAcquireTimeout()));
|
||||
}
|
||||
else {
|
||||
// Elastic
|
||||
builder.maxConnections(Integer.MAX_VALUE).pendingAcquireTimeout(Duration.ofMillis(0))
|
||||
.pendingAcquireMaxCount(-1);
|
||||
builder.maxConnections(Integer.MAX_VALUE)
|
||||
.pendingAcquireTimeout(Duration.ofMillis(0))
|
||||
.pendingAcquireMaxCount(-1);
|
||||
}
|
||||
|
||||
if (pool.getMaxIdleTime() != null) {
|
||||
|
||||
@@ -499,10 +499,11 @@ public class HttpClientProperties {
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("useInsecureTrustManager", useInsecureTrustManager)
|
||||
.append("trustedX509Certificates", trustedX509Certificates)
|
||||
.append("handshakeTimeout", handshakeTimeout)
|
||||
.append("closeNotifyFlushTimeout", closeNotifyFlushTimeout)
|
||||
.append("closeNotifyReadTimeout", closeNotifyReadTimeout).toString();
|
||||
.append("trustedX509Certificates", trustedX509Certificates)
|
||||
.append("handshakeTimeout", handshakeTimeout)
|
||||
.append("closeNotifyFlushTimeout", closeNotifyFlushTimeout)
|
||||
.append("closeNotifyReadTimeout", closeNotifyReadTimeout)
|
||||
.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -534,7 +535,8 @@ public class HttpClientProperties {
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("maxFramePayloadLength", maxFramePayloadLength)
|
||||
.append("proxyPing", proxyPing).toString();
|
||||
.append("proxyPing", proxyPing)
|
||||
.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -68,9 +68,10 @@ public class HttpClientSslConfigurer extends AbstractSslConfigurer<HttpClient, H
|
||||
}
|
||||
});
|
||||
|
||||
sslContextSpec.sslContext(clientSslContext).handshakeTimeout(ssl.getHandshakeTimeout())
|
||||
.closeNotifyFlushTimeout(ssl.getCloseNotifyFlushTimeout())
|
||||
.closeNotifyReadTimeout(ssl.getCloseNotifyReadTimeout());
|
||||
sslContextSpec.sslContext(clientSslContext)
|
||||
.handshakeTimeout(ssl.getHandshakeTimeout())
|
||||
.closeNotifyFlushTimeout(ssl.getCloseNotifyFlushTimeout())
|
||||
.closeNotifyReadTimeout(ssl.getCloseNotifyReadTimeout());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ public class DiscoveryClientRouteDefinitionLocator implements RouteDefinitionLoc
|
||||
DiscoveryLocatorProperties properties) {
|
||||
this(discoveryClient.getClass().getSimpleName(), properties);
|
||||
serviceInstances = discoveryClient.getServices()
|
||||
.flatMap(service -> discoveryClient.getInstances(service).collectList());
|
||||
.flatMap(service -> discoveryClient.getInstances(service).collectList());
|
||||
}
|
||||
|
||||
private DiscoveryClientRouteDefinitionLocator(String discoveryClientName, DiscoveryLocatorProperties properties) {
|
||||
@@ -96,36 +96,39 @@ public class DiscoveryClientRouteDefinitionLocator implements RouteDefinitionLoc
|
||||
};
|
||||
}
|
||||
|
||||
return serviceInstances.filter(instances -> !instances.isEmpty()).flatMap(Flux::fromIterable)
|
||||
.filter(includePredicate).collectMap(ServiceInstance::getServiceId)
|
||||
// remove duplicates
|
||||
.flatMapMany(map -> Flux.fromIterable(map.values())).map(instance -> {
|
||||
RouteDefinition routeDefinition = buildRouteDefinition(urlExpr, instance);
|
||||
return serviceInstances.filter(instances -> !instances.isEmpty())
|
||||
.flatMap(Flux::fromIterable)
|
||||
.filter(includePredicate)
|
||||
.collectMap(ServiceInstance::getServiceId)
|
||||
// remove duplicates
|
||||
.flatMapMany(map -> Flux.fromIterable(map.values()))
|
||||
.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);
|
||||
predicate.addArg(entry.getKey(), value);
|
||||
}
|
||||
routeDefinition.getPredicates().add(predicate);
|
||||
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);
|
||||
predicate.addArg(entry.getKey(), value);
|
||||
}
|
||||
routeDefinition.getPredicates().add(predicate);
|
||||
}
|
||||
|
||||
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);
|
||||
filter.addArg(entry.getKey(), value);
|
||||
}
|
||||
routeDefinition.getFilters().add(filter);
|
||||
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);
|
||||
filter.addArg(entry.getKey(), value);
|
||||
}
|
||||
routeDefinition.getFilters().add(filter);
|
||||
}
|
||||
|
||||
return routeDefinition;
|
||||
});
|
||||
return routeDefinition;
|
||||
});
|
||||
}
|
||||
|
||||
protected RouteDefinition buildRouteDefinition(Expression urlExpr, ServiceInstance serviceInstance) {
|
||||
|
||||
@@ -116,10 +116,14 @@ 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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -72,8 +72,9 @@ 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))
|
||||
.doOnError(throwable -> endTimerRespectingCommit(exchange, sample));
|
||||
return chain.filter(exchange)
|
||||
.doOnSuccess(aVoid -> endTimerRespectingCommit(exchange, sample))
|
||||
.doOnError(throwable -> endTimerRespectingCommit(exchange, sample));
|
||||
}
|
||||
|
||||
private void endTimerRespectingCommit(ServerWebExchange exchange, Sample sample) {
|
||||
|
||||
@@ -76,7 +76,8 @@ public class LoadBalancerServiceInstanceCookieFilter implements GlobalFilter, Or
|
||||
ServerWebExchange newExchange = exchange.mutate().request(exchange.getRequest().mutate().headers((headers) -> {
|
||||
List<String> cookieHeaders = new ArrayList<>(headers.getOrEmpty(HttpHeaders.COOKIE));
|
||||
String serviceInstanceCookie = new HttpCookie(instanceIdCookieName,
|
||||
serviceInstanceResponse.getServer().getInstanceId()).toString();
|
||||
serviceInstanceResponse.getServer().getInstanceId())
|
||||
.toString();
|
||||
cookieHeaders.add(serviceInstanceCookie);
|
||||
headers.put(HttpHeaders.COOKIE, cookieHeaders);
|
||||
}).build()).build();
|
||||
|
||||
@@ -131,69 +131,69 @@ public class NettyRoutingFilter implements GlobalFilter, Ordered {
|
||||
Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
|
||||
|
||||
Flux<HttpClientResponse> responseFlux = getHttpClientMono(route, exchange)
|
||||
.flatMapMany(httpClient -> httpClient.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) -> {
|
||||
.flatMapMany(httpClient -> httpClient.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().addAll(filteredResponseHeaders);
|
||||
response.getHeaders().addAll(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)))
|
||||
.onErrorMap(TimeoutException.class,
|
||||
th -> new ResponseStatusException(HttpStatus.GATEWAY_TIMEOUT, th.getMessage(), th));
|
||||
.timeout(responseTimeout,
|
||||
Mono.error(new TimeoutException("Response took longer than timeout: " + responseTimeout)))
|
||||
.onErrorMap(TimeoutException.class,
|
||||
th -> new ResponseStatusException(HttpStatus.GATEWAY_TIMEOUT, th.getMessage(), th));
|
||||
}
|
||||
|
||||
return responseFlux.then(chain.filter(exchange));
|
||||
|
||||
@@ -111,15 +111,15 @@ public class ReactiveLoadBalancerClientFilter implements GlobalFilter, Ordered {
|
||||
URI requestUri = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
|
||||
String serviceId = requestUri.getHost();
|
||||
Set<LoadBalancerLifecycle> supportedLifecycleProcessors = LoadBalancerLifecycleValidator
|
||||
.getSupportedLifecycleProcessors(clientFactory.getInstances(serviceId, LoadBalancerLifecycle.class),
|
||||
RequestDataContext.class, ResponseData.class, ServiceInstance.class);
|
||||
.getSupportedLifecycleProcessors(clientFactory.getInstances(serviceId, LoadBalancerLifecycle.class),
|
||||
RequestDataContext.class, ResponseData.class, ServiceInstance.class);
|
||||
DefaultRequest<RequestDataContext> lbRequest = new DefaultRequest<>(new RequestDataContext(
|
||||
new RequestData(exchange.getRequest(), exchange.getAttributes()), getHint(serviceId)));
|
||||
return choose(lbRequest, serviceId, supportedLifecycleProcessors).doOnNext(response -> {
|
||||
|
||||
if (!response.hasServer()) {
|
||||
supportedLifecycleProcessors.forEach(lifecycle -> lifecycle
|
||||
.onComplete(new CompletionContext<>(CompletionContext.Status.DISCARD, lbRequest, response)));
|
||||
.onComplete(new CompletionContext<>(CompletionContext.Status.DISCARD, lbRequest, response)));
|
||||
throw NotFoundException.create(properties.isUse404(), "Unable to find instance for " + url.getHost());
|
||||
}
|
||||
|
||||
@@ -145,17 +145,18 @@ public class ReactiveLoadBalancerClientFilter implements GlobalFilter, Ordered {
|
||||
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl);
|
||||
exchange.getAttributes().put(GATEWAY_LOADBALANCER_RESPONSE_ATTR, response);
|
||||
supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onStartRequest(lbRequest, response));
|
||||
}).then(chain.filter(exchange))
|
||||
.doOnError(throwable -> supportedLifecycleProcessors.forEach(lifecycle -> lifecycle
|
||||
.onComplete(new CompletionContext<ResponseData, ServiceInstance, RequestDataContext>(
|
||||
CompletionContext.Status.FAILED, throwable, lbRequest,
|
||||
exchange.getAttribute(GATEWAY_LOADBALANCER_RESPONSE_ATTR)))))
|
||||
.doOnSuccess(aVoid -> supportedLifecycleProcessors.forEach(lifecycle -> lifecycle
|
||||
.onComplete(new CompletionContext<ResponseData, ServiceInstance, RequestDataContext>(
|
||||
CompletionContext.Status.SUCCESS, lbRequest,
|
||||
exchange.getAttribute(GATEWAY_LOADBALANCER_RESPONSE_ATTR),
|
||||
new ResponseData(exchange.getResponse(),
|
||||
new RequestData(exchange.getRequest(), exchange.getAttributes()))))));
|
||||
})
|
||||
.then(chain.filter(exchange))
|
||||
.doOnError(throwable -> supportedLifecycleProcessors.forEach(lifecycle -> lifecycle
|
||||
.onComplete(new CompletionContext<ResponseData, ServiceInstance, RequestDataContext>(
|
||||
CompletionContext.Status.FAILED, throwable, lbRequest,
|
||||
exchange.getAttribute(GATEWAY_LOADBALANCER_RESPONSE_ATTR)))))
|
||||
.doOnSuccess(aVoid -> supportedLifecycleProcessors.forEach(lifecycle -> lifecycle
|
||||
.onComplete(new CompletionContext<ResponseData, ServiceInstance, RequestDataContext>(
|
||||
CompletionContext.Status.SUCCESS, lbRequest,
|
||||
exchange.getAttribute(GATEWAY_LOADBALANCER_RESPONSE_ATTR),
|
||||
new ResponseData(exchange.getResponse(),
|
||||
new RequestData(exchange.getRequest(), exchange.getAttributes()))))));
|
||||
}
|
||||
|
||||
protected URI reconstructURI(ServiceInstance serviceInstance, URI original) {
|
||||
|
||||
@@ -85,8 +85,12 @@ 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();
|
||||
// .uri(routeUri)
|
||||
.scheme(routeUri.getScheme())
|
||||
.host(routeUri.getHost())
|
||||
.port(routeUri.getPort())
|
||||
.build(encoded)
|
||||
.toUri();
|
||||
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, mergedUrl);
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
@@ -106,17 +106,17 @@ public class WebClientHttpRoutingFilter implements GlobalFilter, Ordered {
|
||||
}
|
||||
|
||||
return headersSpec.exchangeToMono(Mono::just)
|
||||
// .log("webClient route")
|
||||
.flatMap(res -> {
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
response.getHeaders().putAll(res.headers().asHttpHeaders());
|
||||
response.setStatusCode(res.statusCode());
|
||||
// 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);
|
||||
return chain.filter(exchange);
|
||||
});
|
||||
// .log("webClient route")
|
||||
.flatMap(res -> {
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
response.getHeaders().putAll(res.headers().asHttpHeaders());
|
||||
response.setStatusCode(res.statusCode());
|
||||
// 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);
|
||||
return chain.filter(exchange);
|
||||
});
|
||||
}
|
||||
|
||||
private boolean requiresBody(HttpMethod method) {
|
||||
|
||||
@@ -58,8 +58,8 @@ public class WebClientWriteResponseFilter implements GlobalFilter, Ordered {
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
|
||||
return response.writeWith(clientResponse.body(BodyExtractors.toDataBuffers()))
|
||||
// .log("webClient response")
|
||||
.doOnCancel(() -> cleanup(exchange));
|
||||
// .log("webClient response")
|
||||
.doOnCancel(() -> cleanup(exchange));
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@@ -244,28 +244,32 @@ public class WebsocketRoutingFilter implements GlobalFilter, Ordered {
|
||||
|
||||
@Override
|
||||
public Mono<Void> handle(WebSocketSession proxySession) {
|
||||
Mono<Void> serverClose = proxySession.closeStatus().filter(__ -> session.isOpen())
|
||||
.map(this::adaptCloseStatus).flatMap(session::close);
|
||||
Mono<Void> proxyClose = session.closeStatus().filter(__ -> proxySession.isOpen())
|
||||
.map(this::adaptCloseStatus).flatMap(proxySession::close);
|
||||
Mono<Void> serverClose = proxySession.closeStatus()
|
||||
.filter(__ -> session.isOpen())
|
||||
.map(this::adaptCloseStatus)
|
||||
.flatMap(session::close);
|
||||
Mono<Void> proxyClose = session.closeStatus()
|
||||
.filter(__ -> proxySession.isOpen())
|
||||
.map(this::adaptCloseStatus)
|
||||
.flatMap(proxySession::close);
|
||||
// Use retain() for Reactor Netty
|
||||
Mono<Void> proxySessionSend = proxySession
|
||||
.send(session.receive().doOnNext(WebSocketMessage::retain).doOnNext(webSocketMessage -> {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("proxySession(send from client): " + proxySession.getId()
|
||||
+ ", corresponding session:" + session.getId() + ", packet: "
|
||||
+ webSocketMessage.getPayloadAsText());
|
||||
}
|
||||
}));
|
||||
.send(session.receive().doOnNext(WebSocketMessage::retain).doOnNext(webSocketMessage -> {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("proxySession(send from client): " + proxySession.getId()
|
||||
+ ", corresponding session:" + session.getId() + ", packet: "
|
||||
+ webSocketMessage.getPayloadAsText());
|
||||
}
|
||||
}));
|
||||
// .log("proxySessionSend", Level.FINE);
|
||||
Mono<Void> serverSessionSend = session.send(
|
||||
proxySession.receive().doOnNext(WebSocketMessage::retain).doOnNext(webSocketMessage -> {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("session(send from backend): " + session.getId()
|
||||
+ ", corresponding proxySession:" + proxySession.getId() + " packet: "
|
||||
+ webSocketMessage.getPayloadAsText());
|
||||
}
|
||||
}));
|
||||
Mono<Void> serverSessionSend = session
|
||||
.send(proxySession.receive().doOnNext(WebSocketMessage::retain).doOnNext(webSocketMessage -> {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("session(send from backend): " + session.getId()
|
||||
+ ", corresponding proxySession:" + proxySession.getId() + " packet: "
|
||||
+ webSocketMessage.getPayloadAsText());
|
||||
}
|
||||
}));
|
||||
// .log("sessionSend", Level.FINE);
|
||||
// Ensure closeStatus from one propagates to the other
|
||||
Mono.when(serverClose, proxyClose).subscribe();
|
||||
|
||||
@@ -315,8 +315,11 @@ public class WeightCalculatorWebFilter implements WebFilter, Ordered, SmartAppli
|
||||
|
||||
@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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -106,17 +106,17 @@ public class CorsGatewayFilterApplicationListener implements ApplicationListener
|
||||
final CorsConfiguration corsConfiguration = new CorsConfiguration();
|
||||
|
||||
findValue(corsMetadata, "allowCredentials")
|
||||
.ifPresent(value -> corsConfiguration.setAllowCredentials((Boolean) value));
|
||||
.ifPresent(value -> corsConfiguration.setAllowCredentials((Boolean) value));
|
||||
findValue(corsMetadata, "allowedHeaders")
|
||||
.ifPresent(value -> corsConfiguration.setAllowedHeaders(asList(value)));
|
||||
.ifPresent(value -> corsConfiguration.setAllowedHeaders(asList(value)));
|
||||
findValue(corsMetadata, "allowedMethods")
|
||||
.ifPresent(value -> corsConfiguration.setAllowedMethods(asList(value)));
|
||||
.ifPresent(value -> corsConfiguration.setAllowedMethods(asList(value)));
|
||||
findValue(corsMetadata, "allowedOriginPatterns")
|
||||
.ifPresent(value -> corsConfiguration.setAllowedOriginPatterns(asList(value)));
|
||||
.ifPresent(value -> corsConfiguration.setAllowedOriginPatterns(asList(value)));
|
||||
findValue(corsMetadata, "allowedOrigins")
|
||||
.ifPresent(value -> corsConfiguration.setAllowedOrigins(asList(value)));
|
||||
.ifPresent(value -> corsConfiguration.setAllowedOrigins(asList(value)));
|
||||
findValue(corsMetadata, "exposedHeaders")
|
||||
.ifPresent(value -> corsConfiguration.setExposedHeaders(asList(value)));
|
||||
.ifPresent(value -> corsConfiguration.setExposedHeaders(asList(value)));
|
||||
findValue(corsMetadata, "maxAge").ifPresent(value -> corsConfiguration.setMaxAge(asLong(value)));
|
||||
|
||||
return Optional.of(corsConfiguration);
|
||||
|
||||
@@ -37,8 +37,10 @@ public class AddRequestHeaderGatewayFilterFactory extends AbstractNameValueGatew
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
String value = ServerWebExchangeUtils.expand(exchange, config.getValue());
|
||||
ServerHttpRequest request = exchange.getRequest().mutate()
|
||||
.headers(httpHeaders -> httpHeaders.add(config.getName(), value)).build();
|
||||
ServerHttpRequest request = exchange.getRequest()
|
||||
.mutate()
|
||||
.headers(httpHeaders -> httpHeaders.add(config.getName(), value))
|
||||
.build();
|
||||
|
||||
return chain.filter(exchange.mutate().request(request).build());
|
||||
}
|
||||
@@ -46,7 +48,8 @@ public class AddRequestHeaderGatewayFilterFactory extends AbstractNameValueGatew
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(AddRequestHeaderGatewayFilterFactory.this)
|
||||
.append(config.getName(), config.getValue()).toString();
|
||||
.append(config.getName(), config.getValue())
|
||||
.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -69,8 +69,11 @@ public class AddRequestHeadersIfNotPresentGatewayFilterFactory
|
||||
for (Map.Entry<String, List<String>> kv : aggregatedHeaders.entrySet()) {
|
||||
String headerName = kv.getKey();
|
||||
|
||||
boolean headerIsMissingOrBlank = exchange.getRequest().getHeaders().getOrEmpty(headerName).stream()
|
||||
.allMatch(h -> !StringUtils.hasText(h));
|
||||
boolean headerIsMissingOrBlank = exchange.getRequest()
|
||||
.getHeaders()
|
||||
.getOrEmpty(headerName)
|
||||
.stream()
|
||||
.allMatch(h -> !StringUtils.hasText(h));
|
||||
|
||||
if (headerIsMissingOrBlank) {
|
||||
if (requestBuilder == null) {
|
||||
@@ -78,9 +81,10 @@ public class AddRequestHeadersIfNotPresentGatewayFilterFactory
|
||||
}
|
||||
ServerWebExchange finalExchange = exchange;
|
||||
requestBuilder.headers(httpHeaders -> {
|
||||
List<String> replacedValues = kv.getValue().stream()
|
||||
.map(value -> ServerWebExchangeUtils.expand(finalExchange, value))
|
||||
.collect(Collectors.toList());
|
||||
List<String> replacedValues = kv.getValue()
|
||||
.stream()
|
||||
.map(value -> ServerWebExchangeUtils.expand(finalExchange, value))
|
||||
.collect(Collectors.toList());
|
||||
httpHeaders.addAll(headerName, replacedValues);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -60,8 +60,10 @@ public class AddRequestParameterGatewayFilterFactory extends AbstractNameValueGa
|
||||
|
||||
boolean encoded = containsEncodedParts(uri);
|
||||
try {
|
||||
URI newUri = UriComponentsBuilder.fromUri(uri).replaceQuery(query.toString()).build(encoded)
|
||||
.toUri();
|
||||
URI newUri = UriComponentsBuilder.fromUri(uri)
|
||||
.replaceQuery(query.toString())
|
||||
.build(encoded)
|
||||
.toUri();
|
||||
|
||||
ServerHttpRequest request = exchange.getRequest().mutate().uri(newUri).build();
|
||||
|
||||
@@ -75,7 +77,8 @@ public class AddRequestParameterGatewayFilterFactory extends AbstractNameValueGa
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(AddRequestParameterGatewayFilterFactory.this)
|
||||
.append(config.getName(), config.getValue()).toString();
|
||||
.append(config.getName(), config.getValue())
|
||||
.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -42,7 +42,8 @@ public class AddResponseHeaderGatewayFilterFactory extends AbstractNameValueGate
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(AddResponseHeaderGatewayFilterFactory.this)
|
||||
.append(config.getName(), config.getValue()).toString();
|
||||
.append(config.getName(), config.getValue())
|
||||
.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -77,23 +77,23 @@ public class CacheRequestBodyGatewayFilterFactory
|
||||
|
||||
return ServerWebExchangeUtils.cacheRequestBodyAndRequest(exchange, (serverHttpRequest) -> {
|
||||
final ServerRequest serverRequest = ServerRequest
|
||||
.create(exchange.mutate().request(serverHttpRequest).build(), messageReaders);
|
||||
.create(exchange.mutate().request(serverHttpRequest).build(), messageReaders);
|
||||
return serverRequest.bodyToMono((config.getBodyClass())).doOnNext(objectValue -> {
|
||||
Object previousCachedBody = exchange.getAttributes()
|
||||
.put(ServerWebExchangeUtils.CACHED_REQUEST_BODY_ATTR, objectValue);
|
||||
.put(ServerWebExchangeUtils.CACHED_REQUEST_BODY_ATTR, objectValue);
|
||||
if (previousCachedBody != null) {
|
||||
// store previous cached body
|
||||
exchange.getAttributes().put(CACHED_ORIGINAL_REQUEST_BODY_BACKUP_ATTR, previousCachedBody);
|
||||
}
|
||||
}).then(Mono.defer(() -> {
|
||||
ServerHttpRequest cachedRequest = exchange
|
||||
.getAttribute(CACHED_SERVER_HTTP_REQUEST_DECORATOR_ATTR);
|
||||
.getAttribute(CACHED_SERVER_HTTP_REQUEST_DECORATOR_ATTR);
|
||||
Assert.notNull(cachedRequest, "cache request shouldn't be null");
|
||||
exchange.getAttributes().remove(CACHED_SERVER_HTTP_REQUEST_DECORATOR_ATTR);
|
||||
return chain.filter(exchange.mutate().request(cachedRequest).build()).doFinally(s -> {
|
||||
//
|
||||
Object backupCachedBody = exchange.getAttributes()
|
||||
.get(CACHED_ORIGINAL_REQUEST_BODY_BACKUP_ATTR);
|
||||
.get(CACHED_ORIGINAL_REQUEST_BODY_BACKUP_ATTR);
|
||||
if (backupCachedBody instanceof DataBuffer dataBuffer) {
|
||||
DataBufferUtils.release(dataBuffer);
|
||||
}
|
||||
@@ -105,7 +105,8 @@ public class CacheRequestBodyGatewayFilterFactory
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(CacheRequestBodyGatewayFilterFactory.this)
|
||||
.append("Body class", config.getBodyClass()).toString();
|
||||
.append("Body class", config.getBodyClass())
|
||||
.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -93,13 +93,14 @@ public class DedupeResponseHeaderGatewayFilterFactory
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
return chain.filter(exchange)
|
||||
.then(Mono.fromRunnable(() -> dedupe(exchange.getResponse().getHeaders(), config)));
|
||||
.then(Mono.fromRunnable(() -> dedupe(exchange.getResponse().getHeaders(), config)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(DedupeResponseHeaderGatewayFilterFactory.this)
|
||||
.append(config.getName(), config.getStrategy()).toString();
|
||||
.append(config.getName(), config.getStrategy())
|
||||
.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ public class JsonToGrpcGatewayFilterFactory
|
||||
|
||||
ServerWebExchangeUtils.setAlreadyRouted(exchange);
|
||||
return modifiedResponse.writeWith(exchange.getRequest().getBody())
|
||||
.then(chain.filter(exchange.mutate().response(modifiedResponse).build()));
|
||||
.then(chain.filter(exchange.mutate().response(modifiedResponse).build()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -190,7 +190,7 @@ public class JsonToGrpcGatewayFilterFactory
|
||||
Resource protoFile = resourceLoader.getResource(config.getProtoFile());
|
||||
|
||||
descriptor = DescriptorProtos.FileDescriptorProto.parseFrom(descriptorFile.getInputStream())
|
||||
.getDescriptorForType();
|
||||
.getDescriptorForType();
|
||||
|
||||
Descriptors.MethodDescriptor methodDescriptor = getMethodDescriptor(config,
|
||||
descriptorFile.getInputStream());
|
||||
@@ -218,19 +218,25 @@ public class JsonToGrpcGatewayFilterFactory
|
||||
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
|
||||
exchange.getResponse().getHeaders().set("Content-Type", "application/json");
|
||||
|
||||
return getDelegate().writeWith(deserializeJSONRequest().map(callGRPCServer()).map(serialiseGRPCResponse())
|
||||
.map(wrapGRPCResponse()).cast(DataBuffer.class).last());
|
||||
return getDelegate().writeWith(deserializeJSONRequest().map(callGRPCServer())
|
||||
.map(serialiseGRPCResponse())
|
||||
.map(wrapGRPCResponse())
|
||||
.cast(DataBuffer.class)
|
||||
.last());
|
||||
}
|
||||
|
||||
private ClientCall<DynamicMessage, DynamicMessage> createClientCallForType(Config config,
|
||||
Descriptors.ServiceDescriptor serviceDescriptor, Descriptors.Descriptor outputType) {
|
||||
MethodDescriptor.Marshaller<DynamicMessage> marshaller = ProtoUtils
|
||||
.marshaller(DynamicMessage.newBuilder(outputType).build());
|
||||
.marshaller(DynamicMessage.newBuilder(outputType).build());
|
||||
MethodDescriptor<DynamicMessage, DynamicMessage> methodDescriptor = MethodDescriptor
|
||||
.<DynamicMessage, DynamicMessage>newBuilder().setType(MethodDescriptor.MethodType.UNKNOWN)
|
||||
.setFullMethodName(MethodDescriptor.generateFullMethodName(serviceDescriptor.getFullName(),
|
||||
config.getMethod()))
|
||||
.setRequestMarshaller(marshaller).setResponseMarshaller(marshaller).build();
|
||||
.<DynamicMessage, DynamicMessage>newBuilder()
|
||||
.setType(MethodDescriptor.MethodType.UNKNOWN)
|
||||
.setFullMethodName(
|
||||
MethodDescriptor.generateFullMethodName(serviceDescriptor.getFullName(), config.getMethod()))
|
||||
.setRequestMarshaller(marshaller)
|
||||
.setResponseMarshaller(marshaller)
|
||||
.build();
|
||||
Channel channel = createChannel();
|
||||
return channel.newCall(methodDescriptor, CallOptions.DEFAULT);
|
||||
}
|
||||
@@ -238,7 +244,7 @@ public class JsonToGrpcGatewayFilterFactory
|
||||
private Descriptors.MethodDescriptor getMethodDescriptor(Config config, InputStream descriptorFile)
|
||||
throws IOException, Descriptors.DescriptorValidationException {
|
||||
DescriptorProtos.FileDescriptorSet fileDescriptorSet = DescriptorProtos.FileDescriptorSet
|
||||
.parseFrom(descriptorFile);
|
||||
.parseFrom(descriptorFile);
|
||||
DescriptorProtos.FileDescriptorProto fileProto = fileDescriptorSet.getFile(0);
|
||||
Descriptors.FileDescriptor fileDescriptor = Descriptors.FileDescriptor.buildFrom(fileProto,
|
||||
new Descriptors.FileDescriptor[0]);
|
||||
@@ -250,8 +256,10 @@ public class JsonToGrpcGatewayFilterFactory
|
||||
|
||||
List<Descriptors.MethodDescriptor> methods = serviceDescriptor.getMethods();
|
||||
|
||||
return methods.stream().filter(method -> method.getName().equals(config.getMethod())).findFirst()
|
||||
.orElseThrow(() -> new NoSuchElementException("No Method found"));
|
||||
return methods.stream()
|
||||
.filter(method -> method.getName().equals(config.getMethod()))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new NoSuchElementException("No Method found"));
|
||||
}
|
||||
|
||||
private ManagedChannel createChannel() {
|
||||
@@ -296,7 +304,7 @@ public class JsonToGrpcGatewayFilterFactory
|
||||
return jsonResponse -> {
|
||||
try {
|
||||
return new NettyDataBufferFactory(new PooledByteBufAllocator())
|
||||
.wrap(Objects.requireNonNull(new ObjectMapper().writeValueAsBytes(jsonResponse)));
|
||||
.wrap(Objects.requireNonNull(new ObjectMapper().writeValueAsBytes(jsonResponse)));
|
||||
}
|
||||
catch (JsonProcessingException e) {
|
||||
return new NettyDataBufferFactory(new PooledByteBufAllocator()).allocateBuffer();
|
||||
|
||||
@@ -64,8 +64,10 @@ public class MapRequestHeaderGatewayFilterFactory
|
||||
}
|
||||
List<String> headerValues = exchange.getRequest().getHeaders().get(config.getFromHeader());
|
||||
|
||||
ServerHttpRequest request = exchange.getRequest().mutate()
|
||||
.headers(i -> i.addAll(config.getToHeader(), headerValues)).build();
|
||||
ServerHttpRequest request = exchange.getRequest()
|
||||
.mutate()
|
||||
.headers(i -> i.addAll(config.getToHeader(), headerValues))
|
||||
.build();
|
||||
|
||||
return chain.filter(exchange.mutate().request(request).build());
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ public class PrefixPathGatewayFilterFactory
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(PrefixPathGatewayFilterFactory.this).append("prefix", config.getPrefix())
|
||||
.toString();
|
||||
.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -102,8 +102,11 @@ public class RedirectToGatewayFilterFactory
|
||||
|
||||
String location;
|
||||
if (includeRequestParams) {
|
||||
location = UriComponentsBuilder.fromUri(uri).queryParams(exchange.getRequest().getQueryParams())
|
||||
.build().toUri().toString();
|
||||
location = UriComponentsBuilder.fromUri(uri)
|
||||
.queryParams(exchange.getRequest().getQueryParams())
|
||||
.build()
|
||||
.toUri()
|
||||
.toString();
|
||||
}
|
||||
else {
|
||||
location = uri.toString();
|
||||
@@ -126,7 +129,8 @@ public class RedirectToGatewayFilterFactory
|
||||
status = httpStatus.getStatus().toString();
|
||||
}
|
||||
return filterToStringCreator(RedirectToGatewayFilterFactory.this).append(status, uri)
|
||||
.append(INCLUDE_REQUEST_PARAMS_KEY, includeRequestParams).toString();
|
||||
.append(INCLUDE_REQUEST_PARAMS_KEY, includeRequestParams)
|
||||
.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -139,7 +139,8 @@ public class RemoveJsonAttributesResponseBodyGatewayFilterFactory extends
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("fieldList", fieldList)
|
||||
.append("deleteRecursively", deleteRecursively).toString();
|
||||
.append("deleteRecursively", deleteRecursively)
|
||||
.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -48,8 +48,10 @@ public class RemoveRequestHeaderGatewayFilterFactory
|
||||
return new GatewayFilter() {
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
ServerHttpRequest request = exchange.getRequest().mutate()
|
||||
.headers(httpHeaders -> httpHeaders.remove(config.getName())).build();
|
||||
ServerHttpRequest request = exchange.getRequest()
|
||||
.mutate()
|
||||
.headers(httpHeaders -> httpHeaders.remove(config.getName()))
|
||||
.build();
|
||||
|
||||
return chain.filter(exchange.mutate().request(request).build());
|
||||
}
|
||||
@@ -57,7 +59,8 @@ public class RemoveRequestHeaderGatewayFilterFactory
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(RemoveRequestHeaderGatewayFilterFactory.this)
|
||||
.append("name", config.getName()).toString();
|
||||
.append("name", config.getName())
|
||||
.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -58,7 +58,9 @@ public class RemoveRequestParameterGatewayFilterFactory
|
||||
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();
|
||||
|
||||
@@ -68,7 +70,8 @@ public class RemoveRequestParameterGatewayFilterFactory
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(RemoveRequestParameterGatewayFilterFactory.this)
|
||||
.append("name", config.getName()).toString();
|
||||
.append("name", config.getName())
|
||||
.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -48,13 +48,14 @@ public class RemoveResponseHeaderGatewayFilterFactory
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
return chain.filter(exchange)
|
||||
.then(Mono.fromRunnable(() -> exchange.getResponse().getHeaders().remove(config.getName())));
|
||||
.then(Mono.fromRunnable(() -> exchange.getResponse().getHeaders().remove(config.getName())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(RemoveResponseHeaderGatewayFilterFactory.this)
|
||||
.append("name", config.getName()).toString();
|
||||
.append("name", config.getName())
|
||||
.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -83,8 +83,9 @@ public class RequestHeaderSizeGatewayFilterFactory
|
||||
|
||||
if (!longHeaders.isEmpty()) {
|
||||
exchange.getResponse().setStatusCode(HttpStatus.REQUEST_HEADER_FIELDS_TOO_LARGE);
|
||||
exchange.getResponse().getHeaders().add(errorHeaderName,
|
||||
getErrorMessage(longHeaders, config.getMaxSize()));
|
||||
exchange.getResponse()
|
||||
.getHeaders()
|
||||
.add(errorHeaderName, getErrorMessage(longHeaders, config.getMaxSize()));
|
||||
return exchange.getResponse().setComplete();
|
||||
|
||||
}
|
||||
@@ -95,7 +96,8 @@ public class RequestHeaderSizeGatewayFilterFactory
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(RequestHeaderSizeGatewayFilterFactory.this)
|
||||
.append("maxSize", config.getMaxSize()).toString();
|
||||
.append("maxSize", config.getMaxSize())
|
||||
.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -103,7 +105,7 @@ public class RequestHeaderSizeGatewayFilterFactory
|
||||
private static String getErrorMessage(HashMap<String, Long> longHeaders, DataSize maxSize) {
|
||||
StringBuilder msg = new StringBuilder(String.format(ERROR_PREFIX, maxSize));
|
||||
longHeaders
|
||||
.forEach((header, size) -> msg.append(String.format(ERROR, header, DataSize.of(size, DataUnit.BYTES))));
|
||||
.forEach((header, size) -> msg.append(String.format(ERROR, header, DataSize.of(size, DataUnit.BYTES))));
|
||||
return msg.toString();
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +59,8 @@ public class RequestHeaderToRequestUriGatewayFilterFactory
|
||||
@Override
|
||||
public String toString() {
|
||||
return filterToStringCreator(RequestHeaderToRequestUriGatewayFilterFactory.this)
|
||||
.append("name", config.getName()).toString();
|
||||
.append("name", config.getName())
|
||||
.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ public class RequestRateLimiterGatewayFilterFactory
|
||||
RateLimiter<Object> limiter = getOrDefault(config.rateLimiter, defaultRateLimiter);
|
||||
boolean denyEmpty = getOrDefault(config.denyEmptyKey, this.denyEmptyKey);
|
||||
HttpStatusHolder emptyKeyStatus = HttpStatusHolder
|
||||
.parse(getOrDefault(config.emptyKeyStatus, this.emptyKeyStatusCode));
|
||||
.parse(getOrDefault(config.emptyKeyStatus, this.emptyKeyStatusCode));
|
||||
|
||||
return (exchange, chain) -> resolver.resolve(exchange).defaultIfEmpty(EMPTY_KEY).flatMap(key -> {
|
||||
if (EMPTY_KEY.equals(key)) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user