Skip to content

Commit 1dc0e8a

Browse files
authored
Disable per route response timeout with negative number (#2420)
* Disable per route response timeout with negative number * Formatting
1 parent 18d00d1 commit 1dc0e8a

File tree

6 files changed

+116
-41
lines changed

6 files changed

+116
-41
lines changed

docs/src/main/asciidoc/spring-cloud-gateway.adoc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2221,6 +2221,19 @@ import static org.springframework.cloud.gateway.support.RouteMetadataUtils.RESPO
22212221
}
22222222
----
22232223

2224+
A per-route `response-timeout` with a negative value will disable the global `response-timeout` value.
2225+
2226+
----
2227+
- id: per_route_timeouts
2228+
uri: https://example.org
2229+
predicates:
2230+
- name: Path
2231+
args:
2232+
pattern: /delay/{timeout}
2233+
metadata:
2234+
response-timeout: -1
2235+
----
2236+
22242237
=== Fluent Java Routes API
22252238

22262239
To allow for simple configuration in Java, the `RouteLocatorBuilder` bean includes a fluent API.

spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/NettyRoutingFilter.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,13 @@
7171
*/
7272
public class NettyRoutingFilter implements GlobalFilter, Ordered {
7373

74-
private static final Log log = LogFactory.getLog(NettyRoutingFilter.class);
75-
7674
/**
7775
* The order of the NettyRoutingFilter. See {@link Ordered#LOWEST_PRECEDENCE}.
7876
*/
7977
public static final int ORDER = Ordered.LOWEST_PRECEDENCE;
8078

79+
private static final Log log = LogFactory.getLog(NettyRoutingFilter.class);
80+
8181
private final HttpClient httpClient;
8282

8383
private final ObjectProvider<List<HttpHeadersFilter>> headersFiltersProvider;
@@ -261,16 +261,16 @@ static Integer getInteger(Object connectTimeoutAttr) {
261261

262262
private Duration getResponseTimeout(Route route) {
263263
Object responseTimeoutAttr = route.getMetadata().get(RESPONSE_TIMEOUT_ATTR);
264-
Long responseTimeout = null;
265-
if (responseTimeoutAttr != null) {
266-
if (responseTimeoutAttr instanceof Number) {
267-
responseTimeout = ((Number) responseTimeoutAttr).longValue();
264+
if (responseTimeoutAttr != null && responseTimeoutAttr instanceof Number) {
265+
Long routeResponseTimeout = ((Number) responseTimeoutAttr).longValue();
266+
if (routeResponseTimeout >= 0) {
267+
return Duration.ofMillis(routeResponseTimeout);
268268
}
269269
else {
270-
responseTimeout = Long.valueOf(responseTimeoutAttr.toString());
270+
return null;
271271
}
272272
}
273-
return responseTimeout != null ? Duration.ofMillis(responseTimeout) : properties.getResponseTimeout();
273+
return properties.getResponseTimeout();
274274
}
275275

276276
}

spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/NettyRoutingFilterCompatibleTests.java

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.springframework.context.annotation.Import;
2727
import org.springframework.http.HttpStatus;
2828
import org.springframework.test.annotation.DirtiesContext;
29+
import org.springframework.test.context.ActiveProfiles;
2930
import org.springframework.web.server.ServerWebExchange;
3031

3132
import static org.assertj.core.api.Assertions.assertThat;
@@ -39,17 +40,9 @@
3940
*
4041
* @author echooymxq
4142
**/
42-
@SpringBootTest(properties = { "spring.cloud.gateway.routes[0].id=route_connect_timeout",
43-
"spring.cloud.gateway.routes[0].uri=http://localhost:32167",
44-
"spring.cloud.gateway.routes[0].predicates[0].name=Path",
45-
"spring.cloud.gateway.routes[0].predicates[0].args[pattern]=/connect/delay/{timeout}",
46-
"spring.cloud.gateway.routes[0].metadata[connect-timeout]=5",
47-
"spring.cloud.gateway.routes[1].id=route_response_timeout",
48-
"spring.cloud.gateway.routes[1].uri=lb://testservice", "spring.cloud.gateway.routes[1].predicates[0].name=Path",
49-
"spring.cloud.gateway.routes[1].predicates[0].args[pattern]=/route/delay/{timeout}",
50-
"spring.cloud.gateway.routes[1].filters[0]=StripPrefix=1",
51-
"spring.cloud.gateway.routes[1].metadata.response-timeout=1000" }, webEnvironment = RANDOM_PORT)
43+
@SpringBootTest(webEnvironment = RANDOM_PORT)
5244
@DirtiesContext
45+
@ActiveProfiles("netty-routing-filter")
5346
class NettyRoutingFilterCompatibleTests extends BaseWebClientTests {
5447

5548
@Test

spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/NettyRoutingFilterIntegrationTests.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,20 @@
3232
import org.springframework.http.server.reactive.ServerHttpResponse;
3333
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
3434
import org.springframework.test.annotation.DirtiesContext;
35+
import org.springframework.test.context.ActiveProfiles;
3536
import org.springframework.test.web.reactive.server.WebTestClient;
3637
import org.springframework.web.server.ServerWebExchange;
3738

3839
import static org.assertj.core.api.Assertions.assertThat;
40+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
3941
import static org.assertj.core.data.Offset.offset;
4042
import static org.hamcrest.Matchers.allOf;
4143
import static org.hamcrest.Matchers.containsString;
4244
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
4345

44-
@SpringBootTest(properties = "spring.cloud.gateway.httpclient.response-timeout=3s", webEnvironment = RANDOM_PORT)
46+
@SpringBootTest(webEnvironment = RANDOM_PORT)
4547
@DirtiesContext
48+
@ActiveProfiles("netty-routing-filter")
4649
public class NettyRoutingFilterIntegrationTests extends BaseWebClientTests {
4750

4851
@Autowired
@@ -109,6 +112,21 @@ public void shouldApplyResponseTimeoutPerRoute() {
109112
.jsonPath("$.message").isEqualTo("Response took longer than timeout: PT1S");
110113
}
111114

115+
@Test
116+
public void shouldNotApplyResponseTimeoutPerRouteWhenNegativeValue() {
117+
assertThatThrownBy(() -> {
118+
testClient.get().uri("/disabledRoute/delay/10").exchange();
119+
}).isInstanceOf(IllegalStateException.class)
120+
.hasMessageContaining("Timeout on blocking read for 5000000000 NANOSECONDS");
121+
}
122+
123+
@Test
124+
public void shouldApplyGlobalResponseTimeoutForInvalidRouteTimeoutValue() {
125+
testClient.get().uri("/invalidRoute/delay/5").exchange().expectStatus().isEqualTo(HttpStatus.GATEWAY_TIMEOUT)
126+
.expectBody().jsonPath("$.status").isEqualTo(String.valueOf(HttpStatus.GATEWAY_TIMEOUT.value()))
127+
.jsonPath("$.message").isEqualTo("Response took longer than timeout: PT3S");
128+
}
129+
112130
@Test
113131
public void shouldNotApplyPerRouteTimeoutWhenItIsNotConfigured() {
114132
testClient.get().uri("/delay/2").exchange().expectStatus().isEqualTo(HttpStatus.OK);
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
test:
2+
hostport: httpbin.org:80
3+
uri: lb://testservice
4+
5+
server:
6+
error:
7+
include-message: always
8+
9+
spring:
10+
profiles:
11+
group:
12+
- logging
13+
14+
cloud:
15+
gateway:
16+
routes:
17+
# =====================================
18+
- id: per_route_connect_timeout
19+
uri: http://localhost:32167
20+
predicates:
21+
- name: Path
22+
args:
23+
pattern: /connect/delay/{timeout}
24+
metadata:
25+
connect-timeout: 5
26+
27+
# =====================================
28+
- id: per_route_response_timeout
29+
uri: ${test.uri}
30+
predicates:
31+
- name: Path
32+
args:
33+
pattern: /route/delay/{timeout}
34+
filters:
35+
- StripPrefix=1
36+
metadata:
37+
response-timeout: 1000
38+
39+
# =====================================
40+
- id: per_route_response_timeout_disabled
41+
uri: ${test.uri}
42+
predicates:
43+
- name: Path
44+
args:
45+
pattern: /disabledRoute/delay/{timeout}
46+
filters:
47+
- StripPrefix=1
48+
metadata:
49+
response-timeout: -1
50+
51+
# =====================================
52+
- id: per_route_response_timeout_invalid
53+
uri: ${test.uri}
54+
predicates:
55+
- name: Path
56+
args:
57+
pattern: /invalidRoute/delay/{timeout}
58+
filters:
59+
- StripPrefix=1
60+
metadata:
61+
response-timeout: notANumber
62+
63+
# =====================================
64+
# should be last and not follow alphabetical order
65+
- id: default_path_to_httpbin
66+
uri: ${test.uri}
67+
order: 10000
68+
predicates:
69+
- name: Path
70+
args:
71+
pattern: /**
72+
httpclient:
73+
response-timeout: 3s

spring-cloud-gateway-server/src/test/resources/application.yml

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -262,28 +262,6 @@ spring:
262262
filters:
263263
- SetPath=/anything/multi{num}
264264

265-
# =====================================
266-
- id: per_route_connect_timeout
267-
uri: http://localhost:32167
268-
predicates:
269-
- name: Path
270-
args:
271-
pattern: /connect/delay/{timeout}
272-
metadata:
273-
connect-timeout: 5
274-
275-
# =====================================
276-
- id: per_route_response_timeout
277-
uri: ${test.uri}
278-
predicates:
279-
- name: Path
280-
args:
281-
pattern: /route/delay/{timeout}
282-
filters:
283-
- StripPrefix=1
284-
metadata:
285-
response-timeout: 1000
286-
287265
# =====================================
288266
- id: redirect_to_test
289267
uri: ${test.uri}

0 commit comments

Comments
 (0)