Adds response timeout using Mono.timeout
fixes gh-371
This commit is contained in:
@@ -217,8 +217,9 @@ public class GatewayAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public NettyRoutingFilter routingFilter(HttpClient httpClient,
|
||||
ObjectProvider<List<HttpHeadersFilter>> headersFilters) {
|
||||
return new NettyRoutingFilter(httpClient, headersFilters);
|
||||
ObjectProvider<List<HttpHeadersFilter>> headersFilters,
|
||||
HttpClientProperties properties) {
|
||||
return new NettyRoutingFilter(httpClient, headersFilters, properties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -20,6 +20,8 @@ package org.springframework.cloud.gateway.config;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import reactor.ipc.netty.resources.PoolResources;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
/**
|
||||
* Configuration properties for the Netty {@link reactor.ipc.netty.http.client.HttpClient}
|
||||
*/
|
||||
@@ -29,6 +31,9 @@ public class HttpClientProperties {
|
||||
/** The connect timeout in millis, the default is 45s. */
|
||||
private Integer connectTimeout;
|
||||
|
||||
/** The response timeout. */
|
||||
private Duration responseTimeout;
|
||||
|
||||
/** Pool configuration for Netty HttpClient */
|
||||
private Pool pool = new Pool();
|
||||
|
||||
@@ -42,6 +47,14 @@ public class HttpClientProperties {
|
||||
return connectTimeout;
|
||||
}
|
||||
|
||||
public Duration getResponseTimeout() {
|
||||
return responseTimeout;
|
||||
}
|
||||
|
||||
public void setResponseTimeout(Duration responseTimeout) {
|
||||
this.responseTimeout = responseTimeout;
|
||||
}
|
||||
|
||||
public void setConnectTimeout(Integer connectTimeout) {
|
||||
this.connectTimeout = connectTimeout;
|
||||
}
|
||||
@@ -220,6 +233,7 @@ public class HttpClientProperties {
|
||||
public String toString() {
|
||||
return "HttpClientProperties{" +
|
||||
"connectTimeout=" + connectTimeout +
|
||||
", responseTimeout=" + responseTimeout +
|
||||
", pool=" + pool +
|
||||
", proxy=" + proxy +
|
||||
", ssl=" + ssl +
|
||||
|
||||
@@ -26,10 +26,13 @@ import reactor.core.publisher.Mono;
|
||||
import reactor.ipc.netty.NettyPipeline;
|
||||
import reactor.ipc.netty.http.client.HttpClient;
|
||||
import reactor.ipc.netty.http.client.HttpClientRequest;
|
||||
import reactor.ipc.netty.http.client.HttpClientResponse;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.cloud.gateway.config.HttpClientProperties;
|
||||
import org.springframework.cloud.gateway.filter.headers.HttpHeadersFilter;
|
||||
import org.springframework.cloud.gateway.filter.headers.HttpHeadersFilter.Type;
|
||||
import org.springframework.cloud.gateway.support.TimeoutException;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.io.buffer.NettyDataBuffer;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
@@ -55,11 +58,14 @@ public class NettyRoutingFilter implements GlobalFilter, Ordered {
|
||||
|
||||
private final HttpClient httpClient;
|
||||
private final ObjectProvider<List<HttpHeadersFilter>> headersFilters;
|
||||
private final HttpClientProperties properties;
|
||||
|
||||
public NettyRoutingFilter(HttpClient httpClient,
|
||||
ObjectProvider<List<HttpHeadersFilter>> headersFilters) {
|
||||
ObjectProvider<List<HttpHeadersFilter>> headersFilters,
|
||||
HttpClientProperties properties) {
|
||||
this.httpClient = httpClient;
|
||||
this.headersFilters = headersFilters;
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -93,7 +99,7 @@ public class NettyRoutingFilter implements GlobalFilter, Ordered {
|
||||
|
||||
boolean preserveHost = exchange.getAttributeOrDefault(PRESERVE_HOST_HEADER_ATTRIBUTE, false);
|
||||
|
||||
return this.httpClient.request(method, url, req -> {
|
||||
Mono<HttpClientResponse> responseMono = this.httpClient.request(method, url, req -> {
|
||||
final HttpClientRequest proxyRequest = req.options(NettyPipeline.SendOptions::flushOnEach)
|
||||
.headers(httpHeaders)
|
||||
.chunkedTransfer(chunkedTransfer)
|
||||
@@ -107,8 +113,16 @@ public class NettyRoutingFilter implements GlobalFilter, Ordered {
|
||||
|
||||
return proxyRequest.sendHeaders() //I shouldn't need this
|
||||
.send(request.getBody().map(dataBuffer ->
|
||||
((NettyDataBuffer)dataBuffer).getNativeBuffer()));
|
||||
}).doOnNext(res -> {
|
||||
((NettyDataBuffer) dataBuffer).getNativeBuffer()));
|
||||
});
|
||||
|
||||
if (properties.getResponseTimeout() != null) {
|
||||
responseMono.timeout(properties.getResponseTimeout(),
|
||||
Mono.error(new TimeoutException("Response took longer than timeout: " +
|
||||
properties.getResponseTimeout())));
|
||||
}
|
||||
|
||||
return responseMono.doOnNext(res -> {
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
// put headers and status so filters can modify the response
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
|
||||
@@ -26,6 +26,7 @@ import java.util.function.Predicate;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.cloud.gateway.support.TimeoutException;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.retry.Repeat;
|
||||
import reactor.retry.RepeatContext;
|
||||
@@ -146,12 +147,11 @@ public class RetryGatewayFilterFactory extends AbstractGatewayFilterFactory<Retr
|
||||
};
|
||||
}
|
||||
|
||||
private static <T> List<T> toList(T item) {
|
||||
ArrayList<T> list = new ArrayList<>();
|
||||
list.add(item);
|
||||
return list;
|
||||
private static <T> List<T> toList(T... items) {
|
||||
return new ArrayList<>(Arrays.asList(items));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static class RetryConfig {
|
||||
private int retries = 3;
|
||||
|
||||
@@ -161,7 +161,7 @@ public class RetryGatewayFilterFactory extends AbstractGatewayFilterFactory<Retr
|
||||
|
||||
private List<HttpMethod> methods = toList(HttpMethod.GET);
|
||||
|
||||
private List<Class<? extends Throwable>> exceptions = toList(IOException.class);
|
||||
private List<Class<? extends Throwable>> exceptions = toList(IOException.class, TimeoutException.class);
|
||||
|
||||
public RetryConfig setRetries(int retries) {
|
||||
this.retries = retries;
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.support;
|
||||
|
||||
public class TimeoutException extends Exception {
|
||||
|
||||
public TimeoutException() {
|
||||
}
|
||||
|
||||
public TimeoutException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -62,6 +62,7 @@ public class GatewayAutoConfigurationTests {
|
||||
GatewayAutoConfiguration.class))
|
||||
.withPropertyValues("spring.cloud.gateway.httpclient.ssl.use-insecure-trust-manager=true",
|
||||
"spring.cloud.gateway.httpclient.connect-timeout=10",
|
||||
"spring.cloud.gateway.httpclient.response-timeout=10s",
|
||||
"spring.cloud.gateway.httpclient.pool.type=fixed",
|
||||
"spring.cloud.gateway.httpclient.proxy.host=myhost")
|
||||
.run(context -> {
|
||||
|
||||
Reference in New Issue
Block a user