Skip to content

Commit 53f3982

Browse files
committed
Support Jetty RS as HTTP driver for WebClient
This commit adds a new auto-configuration choice for `ClientHttpConnector`, this time using the Jetty RS HTTP client library if available. This is the best choice in case the application runs on a Jetty reactive server, as both client and server will share resources. Closes gh-14005
1 parent f74dd7d commit 53f3982

File tree

5 files changed

+83
-1
lines changed

5 files changed

+83
-1
lines changed

spring-boot-project/spring-boot-autoconfigure/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,11 @@
280280
<artifactId>jetty-webapp</artifactId>
281281
<optional>true</optional>
282282
</dependency>
283+
<dependency>
284+
<groupId>org.eclipse.jetty</groupId>
285+
<artifactId>jetty-reactive-httpclient</artifactId>
286+
<optional>true</optional>
287+
</dependency>
283288
<dependency>
284289
<groupId>org.eclipse.jetty.websocket</groupId>
285290
<artifactId>javax-websocket-server-impl</artifactId>

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/function/client/ClientHttpConnectorAutoConfiguration.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@
3939
*/
4040
@Configuration
4141
@ConditionalOnClass(WebClient.class)
42-
@Import({ ClientHttpConnectorConfiguration.ReactorNetty.class })
42+
@Import({ ClientHttpConnectorConfiguration.ReactorNetty.class,
43+
ClientHttpConnectorConfiguration.JettyClient.class })
4344
public class ClientHttpConnectorAutoConfiguration {
4445

4546
@Bean

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/function/client/ClientHttpConnectorConfiguration.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
import org.springframework.context.annotation.Bean;
2424
import org.springframework.context.annotation.Configuration;
2525
import org.springframework.http.client.reactive.ClientHttpConnector;
26+
import org.springframework.http.client.reactive.JettyClientHttpConnector;
27+
import org.springframework.http.client.reactive.JettyResourceFactory;
2628
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
2729
import org.springframework.http.client.reactive.ReactorResourceFactory;
2830

@@ -59,4 +61,24 @@ public ReactorClientHttpConnector reactorClientHttpConnector(
5961

6062
}
6163

64+
@Configuration
65+
@ConditionalOnClass(org.eclipse.jetty.reactive.client.ReactiveRequest.class)
66+
@ConditionalOnMissingBean(ClientHttpConnector.class)
67+
public static class JettyClient {
68+
69+
@Bean
70+
@ConditionalOnMissingBean
71+
public JettyResourceFactory jettyResourceFactory() {
72+
return new JettyResourceFactory();
73+
}
74+
75+
@Bean
76+
public JettyClientHttpConnector jettyClientHttpConnector(
77+
JettyResourceFactory jettyResourceFactory) {
78+
return new JettyClientHttpConnector(jettyResourceFactory, (httpClient) -> {
79+
});
80+
}
81+
82+
}
83+
6284
}

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/function/client/ClientHttpConnectorAutoConfigurationTests.java

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
import org.springframework.boot.autoconfigure.AutoConfigurations;
2222
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
2323
import org.springframework.boot.web.reactive.function.client.WebClientCustomizer;
24+
import org.springframework.context.annotation.Bean;
25+
import org.springframework.context.annotation.Configuration;
26+
import org.springframework.http.client.reactive.ClientHttpConnector;
2427
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
2528
import org.springframework.http.client.reactive.ReactorResourceFactory;
2629
import org.springframework.web.reactive.function.client.WebClient;
@@ -56,4 +59,49 @@ public void shouldCreateHttpClientBeans() {
5659
});
5760
}
5861

62+
@Test
63+
public void shouldNotOverrideCustomClientConnector() {
64+
this.contextRunner.withUserConfiguration(CustomClientHttpConnectorConfig.class)
65+
.run((context) -> {
66+
assertThat(context).hasSingleBean(ClientHttpConnector.class)
67+
.hasBean("customConnector")
68+
.doesNotHaveBean(ReactorResourceFactory.class);
69+
WebClientCustomizer clientCustomizer = context
70+
.getBean(WebClientCustomizer.class);
71+
WebClient.Builder builder = mock(WebClient.Builder.class);
72+
clientCustomizer.customize(builder);
73+
verify(builder, times(1))
74+
.clientConnector(any(ClientHttpConnector.class));
75+
});
76+
}
77+
78+
@Test
79+
public void shouldUseCustomReactorResourceFactory() {
80+
this.contextRunner.withUserConfiguration(CustomReactorResourceConfig.class)
81+
.run((context) -> assertThat(context)
82+
.hasSingleBean(ReactorClientHttpConnector.class)
83+
.hasSingleBean(ReactorResourceFactory.class)
84+
.hasBean("customReactorResourceFactory"));
85+
}
86+
87+
@Configuration
88+
static class CustomClientHttpConnectorConfig {
89+
90+
@Bean
91+
public ClientHttpConnector customConnector() {
92+
return mock(ClientHttpConnector.class);
93+
}
94+
95+
}
96+
97+
@Configuration
98+
static class CustomReactorResourceConfig {
99+
100+
@Bean
101+
public ReactorResourceFactory customReactorResourceFactory() {
102+
return new ReactorResourceFactory();
103+
}
104+
105+
}
106+
59107
}

spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5822,6 +5822,12 @@ The following code shows a typical example:
58225822
Spring Boot will auto-detect which `ClientHttpConnector` to drive `WebClient`, depending
58235823
on the libraries available on the application classpath.
58245824

5825+
The `spring-boot-starter-webflux` depends on `io.projectreactor.netty:reactor-netty` by
5826+
default, which brings both server and client implementations. If you choose to use Jetty
5827+
as a reactive server instead, you should add a dependency on the Jetty Reactive HTTP
5828+
client library, `org.eclipse.jetty:jetty-reactive-httpclient`, because it will
5829+
automatically share HTTP resources with the server.
5830+
58255831
Developers can override this choice by defining their own `ClientHttpConnector` bean;
58265832
in this case, and depending on your HTTP client library of choice, you should also
58275833
define a resource factory bean that manages the HTTP resources for that client.

0 commit comments

Comments
 (0)