1616
1717package org .springframework .cloud .gateway .server .mvc .filter ;
1818
19+ import java .time .Duration ;
1920import java .util .Set ;
2021import java .util .concurrent .ConcurrentHashMap ;
2122import java .util .concurrent .atomic .AtomicInteger ;
2223
2324import org .apache .commons .logging .Log ;
2425import org .apache .commons .logging .LogFactory ;
26+ import org .apache .hc .core5 .util .Timeout ;
2527import org .junit .jupiter .api .Test ;
2628
29+ import org .springframework .beans .factory .ObjectProvider ;
2730import org .springframework .beans .factory .annotation .Autowired ;
2831import org .springframework .boot .SpringBootConfiguration ;
2932import org .springframework .boot .autoconfigure .EnableAutoConfiguration ;
33+ import org .springframework .boot .http .client .ClientHttpRequestFactoryBuilder ;
3034import org .springframework .boot .test .context .SpringBootTest ;
3135import org .springframework .boot .test .context .SpringBootTest .WebEnvironment ;
3236import 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 ;
3341import org .springframework .cloud .gateway .server .mvc .test .HttpbinTestcontainers ;
3442import org .springframework .cloud .gateway .server .mvc .test .LocalServerPortUriResolver ;
3543import org .springframework .cloud .gateway .server .mvc .test .TestLoadBalancerConfig ;
3644import org .springframework .cloud .gateway .server .mvc .test .client .TestRestClient ;
3745import org .springframework .cloud .loadbalancer .annotation .LoadBalancerClient ;
46+ import org .springframework .context .ApplicationContext ;
3847import org .springframework .context .annotation .Bean ;
48+ import org .springframework .context .event .ContextRefreshedEvent ;
3949import org .springframework .core .log .LogMessage ;
4050import org .springframework .http .HttpMethod ;
4151import org .springframework .http .HttpStatus ;
4252import org .springframework .http .ResponseEntity ;
53+ import org .springframework .http .client .ClientHttpRequestFactory ;
4354import org .springframework .test .context .ContextConfiguration ;
4455import org .springframework .util .StringUtils ;
4556import org .springframework .web .bind .annotation .GetMapping ;
4657import org .springframework .web .bind .annotation .PostMapping ;
4758import org .springframework .web .bind .annotation .RequestBody ;
4859import org .springframework .web .bind .annotation .RequestParam ;
4960import org .springframework .web .bind .annotation .RestController ;
61+ import org .springframework .web .client .RestClient ;
62+ import org .springframework .web .servlet .function .HandlerFunction ;
5063import org .springframework .web .servlet .function .RouterFunction ;
5164import org .springframework .web .servlet .function .ServerResponse ;
5265
5366import static org .springframework .cloud .gateway .server .mvc .filter .FilterFunctions .adaptCachedBody ;
5467import static org .springframework .cloud .gateway .server .mvc .filter .FilterFunctions .prefixPath ;
68+ import static org .springframework .cloud .gateway .server .mvc .filter .FilterFunctions .setPath ;
5569import static org .springframework .cloud .gateway .server .mvc .filter .RetryFilterFunctions .retry ;
5670import static org .springframework .cloud .gateway .server .mvc .handler .GatewayRouterFunctions .route ;
5771import static org .springframework .cloud .gateway .server .mvc .handler .HandlerFunctions .http ;
@@ -93,6 +107,17 @@ public void retryBodyWorks() {
93107 .isEqualTo ("3" );
94108 }
95109
110+ @ Test
111+ public void retryWorksWithHttpComponentsClient () {
112+ restClient .get ()
113+ .uri ("/retrywithhttpcomponentsclient?key=retryWorksWithHttpComponentsClient" )
114+ .exchange ()
115+ .expectStatus ()
116+ .isOk ()
117+ .expectBody (String .class )
118+ .isEqualTo ("3" );
119+ }
120+
96121 @ SpringBootConfiguration
97122 @ EnableAutoConfiguration
98123 @ LoadBalancerClient (name = "httpbin" , configuration = TestLoadBalancerConfig .Httpbin .class )
@@ -110,6 +135,41 @@ public RouterFunction<ServerResponse> gatewayRouterFunctionsRetry() {
110135 // @formatter:on
111136 }
112137
138+ @ Bean
139+ public RouterFunction <ServerResponse > gatewayRouterFunctionsRetryWithHttpComponentsClient (
140+ GatewayMvcProperties properties ,
141+ ObjectProvider <HttpHeadersFilter .RequestHttpHeadersFilter > requestHttpHeadersFilters ,
142+ ObjectProvider <HttpHeadersFilter .ResponseHttpHeadersFilter > responseHttpHeadersFilters ,
143+ ApplicationContext applicationContext ) {
144+
145+ // build httpComponents client factory
146+ ClientHttpRequestFactory clientHttpRequestFactory = ClientHttpRequestFactoryBuilder .httpComponents ()
147+ .withConnectionManagerCustomizer (builder -> builder .setMaxConnTotal (2 ).setMaxConnPerRoute (2 ))
148+ .withDefaultRequestConfigCustomizer (
149+ c -> c .setConnectionRequestTimeout (Timeout .of (Duration .ofMillis (3000 ))))
150+ .build ();
151+
152+ // build proxyExchange use httpComponents
153+ RestClient .Builder restClientBuilder = RestClient .builder ();
154+ restClientBuilder .requestFactory (clientHttpRequestFactory );
155+ ProxyExchange proxyExchange = new RestClientProxyExchange (restClientBuilder .build (), properties );
156+
157+ // build handler function use httpComponents
158+ ProxyExchangeHandlerFunction function = new ProxyExchangeHandlerFunction (proxyExchange ,
159+ requestHttpHeadersFilters , responseHttpHeadersFilters );
160+ function .onApplicationEvent (new ContextRefreshedEvent (applicationContext ));
161+
162+ // @formatter:off
163+ return route ("testretrywithhttpcomponentsclient" )
164+ .GET ("/retrywithhttpcomponentsclient" , function )
165+ .before (new LocalServerPortUriResolver ())
166+ .filter (retry (3 ))
167+ .filter (setPath ("/retry" ))
168+ .filter (prefixPath ("/do" ))
169+ .build ();
170+ // @formatter:on
171+ }
172+
113173 @ Bean
114174 public RouterFunction <ServerResponse > gatewayRouterFunctionsRetryBody () {
115175 // @formatter:off
0 commit comments