Skip to content

Commit 44a9c81

Browse files
committed
add httpclint integration test module
Signed-off-by: joecqupt <[email protected]>
1 parent aa22085 commit 44a9c81

File tree

9 files changed

+249
-73
lines changed

9 files changed

+249
-73
lines changed
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+
}
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

+1
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/pom.xml

-5
Original file line numberDiff line numberDiff line change
@@ -135,10 +135,5 @@
135135
<artifactId>rabbitmq</artifactId>
136136
<scope>test</scope>
137137
</dependency>
138-
<dependency>
139-
<groupId>org.apache.httpcomponents.client5</groupId>
140-
<artifactId>httpclient5</artifactId>
141-
<scope>test</scope>
142-
</dependency>
143138
</dependencies>
144139
</project>

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

+4-5
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
import org.springframework.boot.builder.SpringApplicationBuilder;
3434
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
3535
import org.springframework.boot.http.client.ClientHttpRequestFactorySettings;
36-
import org.springframework.boot.http.client.ReactorClientHttpRequestFactoryBuilder;
36+
import org.springframework.boot.http.client.SimpleClientHttpRequestFactoryBuilder;
3737
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
3838
import org.springframework.cloud.gateway.server.mvc.filter.FormFilter;
3939
import org.springframework.cloud.gateway.server.mvc.filter.ForwardedRequestHeadersFilter;
@@ -47,7 +47,6 @@
4747
import org.springframework.context.ConfigurableApplicationContext;
4848

4949
import static org.assertj.core.api.Assertions.assertThat;
50-
import static org.springframework.boot.autoconfigure.http.client.HttpClientProperties.Factory.REACTOR;
5150

5251
public class GatewayServerMvcAutoConfigurationTests {
5352

@@ -152,7 +151,7 @@ void gatewayHttpClientPropertiesWork() {
152151
assertThat(properties.getConnectTimeout()).hasSeconds(1);
153152
assertThat(properties.getReadTimeout()).hasSeconds(2);
154153
assertThat(properties.getSsl().getBundle()).isEqualTo("mybundle");
155-
assertThat(properties.getFactory()).isEqualTo(REACTOR);
154+
assertThat(properties.getFactory()).isNull();
156155
assertThat(settings.readTimeout()).isEqualTo(Duration.ofSeconds(2));
157156
assertThat(settings.connectTimeout()).isEqualTo(Duration.ofSeconds(1));
158157
assertThat(settings.sslBundle()).isNotNull();
@@ -188,10 +187,10 @@ void bootHttpClientPropertiesWork() {
188187
@Test
189188
void settingHttpClientFactoryWorks() {
190189
ConfigurableApplicationContext context = new SpringApplicationBuilder(TestConfig.class)
191-
.properties("spring.main.web-application-type=none")
190+
.properties("spring.main.web-application-type=none", "spring.http.client.factory=simple")
192191
.run();
193192
ClientHttpRequestFactoryBuilder<?> builder = context.getBean(ClientHttpRequestFactoryBuilder.class);
194-
assertThat(builder).isInstanceOf(ReactorClientHttpRequestFactoryBuilder.class);
193+
assertThat(builder).isInstanceOf(SimpleClientHttpRequestFactoryBuilder.class);
195194
}
196195

197196
@SpringBootConfiguration

0 commit comments

Comments
 (0)