Skip to content

Commit bcf26e8

Browse files
committed
add httpclint integration test module
Signed-off-by: joecqupt <[email protected]>
1 parent 5247da1 commit bcf26e8

File tree

7 files changed

+245
-63
lines changed

7 files changed

+245
-63
lines changed
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xmlns="http://maven.apache.org/POM/4.0.0"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
7+
<artifactId>httpclient</artifactId>
8+
<packaging>jar</packaging>
9+
10+
<name>Spring Cloud Gateway HttpClient Integration Test</name>
11+
<description>Spring Cloud Gateway HttpClient Integration Test</description>
12+
13+
<properties>
14+
</properties>
15+
16+
<parent>
17+
<groupId>org.springframework.cloud</groupId>
18+
<artifactId>spring-cloud-gateway-integration-tests</artifactId>
19+
<version>4.3.0-SNAPSHOT</version>
20+
<relativePath>..</relativePath> <!-- lookup parent from repository -->
21+
</parent>
22+
23+
24+
<dependencies>
25+
<dependency>
26+
<groupId>org.springframework.boot</groupId>
27+
<artifactId>spring-boot-starter-web</artifactId>
28+
</dependency>
29+
30+
<dependency>
31+
<groupId>org.springframework.boot</groupId>
32+
<artifactId>spring-boot-starter-webflux</artifactId>
33+
</dependency>
34+
35+
<dependency>
36+
<groupId>org.springframework.cloud</groupId>
37+
<artifactId>spring-cloud-starter-gateway-server-webmvc</artifactId>
38+
</dependency>
39+
40+
<dependency>
41+
<groupId>org.springframework.retry</groupId>
42+
<artifactId>spring-retry</artifactId>
43+
</dependency>
44+
45+
<dependency>
46+
<groupId>org.springframework.cloud</groupId>
47+
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
48+
</dependency>
49+
50+
<dependency>
51+
<groupId>org.apache.httpcomponents.client5</groupId>
52+
<artifactId>httpclient5</artifactId>
53+
</dependency>
54+
55+
<dependency>
56+
<groupId>org.springframework.boot</groupId>
57+
<artifactId>spring-boot-starter-test</artifactId>
58+
<scope>test</scope>
59+
</dependency>
60+
<dependency>
61+
<groupId>org.assertj</groupId>
62+
<artifactId>assertj-core</artifactId>
63+
<scope>test</scope>
64+
</dependency>
65+
</dependencies>
66+
67+
<build>
68+
<plugins>
69+
<plugin>
70+
<artifactId>maven-deploy-plugin</artifactId>
71+
<configuration>
72+
<skip>true</skip>
73+
</configuration>
74+
</plugin>
75+
</plugins>
76+
</build>
77+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
/*
2+
* Copyright 2013-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.cloud.gateway.tests.httpclient;
18+
19+
import java.time.Duration;
20+
import java.util.concurrent.ConcurrentHashMap;
21+
import java.util.concurrent.atomic.AtomicInteger;
22+
23+
import org.apache.commons.logging.Log;
24+
import org.apache.commons.logging.LogFactory;
25+
import org.apache.hc.core5.util.Timeout;
26+
27+
import org.springframework.beans.factory.annotation.Value;
28+
import org.springframework.boot.SpringApplication;
29+
import org.springframework.boot.SpringBootConfiguration;
30+
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
31+
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
32+
import org.springframework.boot.http.client.HttpComponentsClientHttpRequestFactoryBuilder;
33+
import org.springframework.cloud.client.DefaultServiceInstance;
34+
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
35+
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
36+
import org.springframework.cloud.loadbalancer.support.ServiceInstanceListSuppliers;
37+
import org.springframework.context.annotation.Bean;
38+
import org.springframework.http.HttpStatus;
39+
import org.springframework.http.ResponseEntity;
40+
import org.springframework.web.bind.annotation.GetMapping;
41+
import org.springframework.web.bind.annotation.RequestParam;
42+
import org.springframework.web.bind.annotation.RestController;
43+
import org.springframework.web.servlet.function.RouterFunction;
44+
import org.springframework.web.servlet.function.ServerResponse;
45+
46+
import static org.springframework.cloud.gateway.server.mvc.filter.FilterFunctions.prefixPath;
47+
import static org.springframework.cloud.gateway.server.mvc.filter.LoadBalancerFilterFunctions.lb;
48+
import static org.springframework.cloud.gateway.server.mvc.filter.RetryFilterFunctions.retry;
49+
import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route;
50+
import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http;
51+
52+
/**
53+
* @author jiangyuan
54+
*/
55+
@SpringBootConfiguration
56+
@EnableAutoConfiguration
57+
@LoadBalancerClient(name = "myservice", configuration = MyServiceConf.class)
58+
public class HttpClientApplication {
59+
60+
public static void main(String[] args) {
61+
SpringApplication.run(HttpClientApplication.class, args);
62+
}
63+
64+
@Bean
65+
public HttpComponentsClientHttpRequestFactoryBuilder httpComponentsClientHttpRequestFactoryBuilder() {
66+
return ClientHttpRequestFactoryBuilder.httpComponents()
67+
.withConnectionManagerCustomizer(builder -> builder.setMaxConnTotal(2).setMaxConnPerRoute(2))
68+
.withDefaultRequestConfigCustomizer(
69+
c -> c.setConnectionRequestTimeout(Timeout.of(Duration.ofMillis(3000))));
70+
}
71+
72+
@Bean
73+
public RouterFunction<ServerResponse> gatewayRouterFunctionsRetry() {
74+
return route("test-retry").GET("/retry", http())
75+
.filter(lb("myservice"))
76+
.filter(prefixPath("/do"))
77+
.filter(retry(3))
78+
.build();
79+
}
80+
81+
@RestController
82+
protected static class RetryController {
83+
84+
Log log = LogFactory.getLog(getClass());
85+
86+
ConcurrentHashMap<String, AtomicInteger> map = new ConcurrentHashMap<>();
87+
88+
@GetMapping("/do/retry")
89+
public ResponseEntity<String> retry(@RequestParam("key") String key,
90+
@RequestParam(name = "count", defaultValue = "3") int count,
91+
@RequestParam(name = "failStatus", required = false) Integer failStatus) {
92+
AtomicInteger num = map.computeIfAbsent(key, s -> new AtomicInteger());
93+
int i = num.incrementAndGet();
94+
log.warn("Retry count: " + i);
95+
String body = String.valueOf(i);
96+
if (i < count) {
97+
HttpStatus httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
98+
if (failStatus != null) {
99+
httpStatus = HttpStatus.resolve(failStatus);
100+
}
101+
return ResponseEntity.status(httpStatus).header("X-Retry-Count", body).body("temporarily broken");
102+
}
103+
return ResponseEntity.status(HttpStatus.OK).header("X-Retry-Count", body).body(body);
104+
}
105+
106+
}
107+
108+
}
109+
110+
class MyServiceConf {
111+
112+
@Value("${local.server.port}")
113+
private int port = 0;
114+
115+
@Bean
116+
public ServiceInstanceListSupplier staticServiceInstanceListSupplier() {
117+
return ServiceInstanceListSuppliers.from("myservice",
118+
new DefaultServiceInstance("myservice-1", "myservice", "localhost", port, false));
119+
}
120+
121+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
logging:
2+
level:
3+
org.springframework.cloud.gateway: TRACE
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright 2013-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.cloud.gateway.tests.httpclient;
18+
19+
import org.junit.jupiter.api.Test;
20+
21+
import org.springframework.boot.test.context.SpringBootTest;
22+
import org.springframework.boot.test.web.server.LocalServerPort;
23+
import org.springframework.test.annotation.DirtiesContext;
24+
import org.springframework.test.web.reactive.server.WebTestClient;
25+
26+
/**
27+
* @author jiangyuan
28+
*/
29+
@SpringBootTest(classes = HttpClientApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
30+
@DirtiesContext
31+
public class HttpClientApplicationTests {
32+
33+
@LocalServerPort
34+
private int port;
35+
36+
@Test
37+
public void retryWorks() {
38+
WebTestClient client = WebTestClient.bindToServer().baseUrl("http://localhost:" + port).build();
39+
client.get().uri("/retry?key=get").exchange().expectStatus().isOk().expectBody(String.class).isEqualTo("3");
40+
}
41+
42+
}

spring-cloud-gateway-integration-tests/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
<module>grpc</module>
2525
<module>http2</module>
2626
<module>mvc-failure-analyzer</module>
27+
<module>httpclient</module>
2728
</modules>
2829

2930
<build>

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

Lines changed: 0 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -16,55 +16,42 @@
1616

1717
package org.springframework.cloud.gateway.server.mvc.filter;
1818

19-
import java.time.Duration;
2019
import java.util.Set;
2120
import java.util.concurrent.ConcurrentHashMap;
2221
import java.util.concurrent.atomic.AtomicInteger;
2322

2423
import org.apache.commons.logging.Log;
2524
import org.apache.commons.logging.LogFactory;
26-
import org.apache.hc.core5.util.Timeout;
2725
import org.junit.jupiter.api.Test;
2826

29-
import org.springframework.beans.factory.ObjectProvider;
3027
import org.springframework.beans.factory.annotation.Autowired;
3128
import org.springframework.boot.SpringBootConfiguration;
3229
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
33-
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
3430
import org.springframework.boot.test.context.SpringBootTest;
3531
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
3632
import org.springframework.boot.test.web.server.LocalServerPort;
37-
import org.springframework.cloud.gateway.server.mvc.config.GatewayMvcProperties;
38-
import org.springframework.cloud.gateway.server.mvc.handler.ProxyExchange;
39-
import org.springframework.cloud.gateway.server.mvc.handler.ProxyExchangeHandlerFunction;
40-
import org.springframework.cloud.gateway.server.mvc.handler.RestClientProxyExchange;
4133
import org.springframework.cloud.gateway.server.mvc.test.HttpbinTestcontainers;
4234
import org.springframework.cloud.gateway.server.mvc.test.LocalServerPortUriResolver;
4335
import org.springframework.cloud.gateway.server.mvc.test.TestLoadBalancerConfig;
4436
import org.springframework.cloud.gateway.server.mvc.test.client.TestRestClient;
4537
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
46-
import org.springframework.context.ApplicationContext;
4738
import org.springframework.context.annotation.Bean;
48-
import org.springframework.context.event.ContextRefreshedEvent;
4939
import org.springframework.core.log.LogMessage;
5040
import org.springframework.http.HttpMethod;
5141
import org.springframework.http.HttpStatus;
5242
import org.springframework.http.ResponseEntity;
53-
import org.springframework.http.client.ClientHttpRequestFactory;
5443
import org.springframework.test.context.ContextConfiguration;
5544
import org.springframework.util.StringUtils;
5645
import org.springframework.web.bind.annotation.GetMapping;
5746
import org.springframework.web.bind.annotation.PostMapping;
5847
import org.springframework.web.bind.annotation.RequestBody;
5948
import org.springframework.web.bind.annotation.RequestParam;
6049
import org.springframework.web.bind.annotation.RestController;
61-
import org.springframework.web.client.RestClient;
6250
import org.springframework.web.servlet.function.RouterFunction;
6351
import org.springframework.web.servlet.function.ServerResponse;
6452

6553
import static org.springframework.cloud.gateway.server.mvc.filter.FilterFunctions.adaptCachedBody;
6654
import static org.springframework.cloud.gateway.server.mvc.filter.FilterFunctions.prefixPath;
67-
import static org.springframework.cloud.gateway.server.mvc.filter.FilterFunctions.setPath;
6855
import static org.springframework.cloud.gateway.server.mvc.filter.RetryFilterFunctions.retry;
6956
import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route;
7057
import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http;
@@ -106,17 +93,6 @@ public void retryBodyWorks() {
10693
.isEqualTo("3");
10794
}
10895

109-
@Test
110-
public void retryWorksWithHttpComponentsClient() {
111-
restClient.get()
112-
.uri("/retrywithhttpcomponentsclient?key=retryWorksWithHttpComponentsClient")
113-
.exchange()
114-
.expectStatus()
115-
.isOk()
116-
.expectBody(String.class)
117-
.isEqualTo("3");
118-
}
119-
12096
@SpringBootConfiguration
12197
@EnableAutoConfiguration
12298
@LoadBalancerClient(name = "httpbin", configuration = TestLoadBalancerConfig.Httpbin.class)
@@ -134,41 +110,6 @@ public RouterFunction<ServerResponse> gatewayRouterFunctionsRetry() {
134110
// @formatter:on
135111
}
136112

137-
@Bean
138-
public RouterFunction<ServerResponse> gatewayRouterFunctionsRetryWithHttpComponentsClient(
139-
GatewayMvcProperties properties,
140-
ObjectProvider<HttpHeadersFilter.RequestHttpHeadersFilter> requestHttpHeadersFilters,
141-
ObjectProvider<HttpHeadersFilter.ResponseHttpHeadersFilter> responseHttpHeadersFilters,
142-
ApplicationContext applicationContext) {
143-
144-
// build httpComponents client factory
145-
ClientHttpRequestFactory clientHttpRequestFactory = ClientHttpRequestFactoryBuilder.httpComponents()
146-
.withConnectionManagerCustomizer(builder -> builder.setMaxConnTotal(2).setMaxConnPerRoute(2))
147-
.withDefaultRequestConfigCustomizer(
148-
c -> c.setConnectionRequestTimeout(Timeout.of(Duration.ofMillis(3000))))
149-
.build();
150-
151-
// build proxyExchange use httpComponents
152-
RestClient.Builder restClientBuilder = RestClient.builder();
153-
restClientBuilder.requestFactory(clientHttpRequestFactory);
154-
ProxyExchange proxyExchange = new RestClientProxyExchange(restClientBuilder.build(), properties);
155-
156-
// build handler function use httpComponents
157-
ProxyExchangeHandlerFunction function = new ProxyExchangeHandlerFunction(proxyExchange,
158-
requestHttpHeadersFilters, responseHttpHeadersFilters);
159-
function.onApplicationEvent(new ContextRefreshedEvent(applicationContext));
160-
161-
// @formatter:off
162-
return route("testretrywithhttpcomponentsclient")
163-
.GET("/retrywithhttpcomponentsclient", function)
164-
.before(new LocalServerPortUriResolver())
165-
.filter(retry(3))
166-
.filter(setPath("/retry"))
167-
.filter(prefixPath("/do"))
168-
.build();
169-
// @formatter:on
170-
}
171-
172113
@Bean
173114
public RouterFunction<ServerResponse> gatewayRouterFunctionsRetryBody() {
174115
// @formatter:off

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

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,4 @@ logging:
66
org.springframework.retry: TRACE
77
spring:
88
mvc:
9-
log-request-details: true
10-
http:
11-
client:
12-
factory: REACTOR
9+
log-request-details: true

0 commit comments

Comments
 (0)