HTTP/3 support in Reactor 2024.0 Release Train

Engineering | Violeta Georgieva | November 26, 2024 | ...

HTTP/3, the latest major version of the Hypertext Transfer Protocol, had its specification finalized in June 2022. This version is designed to enhance performance, reliability, and security. Unlike its predecessors, HTTP/3 utilizes QUIC instead of TCP as its transport layer. QUIC is a UDP-based, multiplexed, and secure transport protocol that includes built-in TLS 1.3 encryption, making QUIC encrypted by default.

To learn more about the performance and benefits of HTTP/3, check out What is HTTP/3.

For information on browser adoption, take a look at Examining HTTP/3 Usage, which also provides raw data on HTTP versions used by different browsers.

Reactor Netty 1.2 (part of Reactor 2024.0 Release Train) adds HTTP/3 experimental support. With this new version of Reactor Netty, your Spring Boot application and Spring Cloud Gateway can be configured to support HTTP/3.

Let’s see how you can configure the HTTP/3 support.

Configure Reactor BOM Version

Spring Boot 3.4 comes by default with Reactor 2024.0 Release Train!

If you run an older Spring Boot version, you can experiment with this new feature by bumping Reactor BOM to 2024.0 (As of this writing, 2024.0.0 is the latest version).

Maven

<properties>
    <reactor-bom.version>2024.0.0</reactor-bom.version>
</properties>

Gradle

ext['reactor-bom.version'] = '2024.0.0'

Configure Netty HTTP3 Codec

You also need to add a runtime dependency on the Netty HTTP3 Codec (As of this writing, 0.0.28.Final is the latest version).

Maven

<dependencies>
    <dependency>
        <groupId>io.netty.incubator</groupId>
        <artifactId>netty-incubator-codec-http3</artifactId>
        <version>0.0.28.Final</version>
        <scope>runtime</scope>
    </dependency>
</dependencies>

Gradle

dependencies {
    runtimeOnly 'io.netty.incubator:netty-incubator-codec-http3:0.0.28.Final'
}

The Server

Configure SSL Bundle

The first thing that you need to provide is an SSL bundle with the configuration that your application requires: keystore, ciphers etc.

application.properties

spring.ssl.bundle.jks.server-http3.key.alias=http3
spring.ssl.bundle.jks.server-http3.keystore.location=...
spring.ssl.bundle.jks.server-http3.keystore.password=...
...

application.yml

spring:
  ssl:
    bundle:
      jks:
        server-http3:
          key:
            alias: http3
          keystore:
            location: ...
            password: ...
          ...

Configure the Embedded Server

Spring Boot gives you the ability to configure the embedded server. Spring Cloud Gateway uses the same approach to achieve this task.

You can declare a component WebServerFactoryCustomizer and get access to the server factory. In order to enable the HTTP/3 support you need to:

By default, Reactor Netty is configured to support HTTP/1.1, so you need to change it.

By default, Reactor Netty does not provide any settings because those are strongly specific to the application, so you have to configure them: idle timeout, max streams, etc.

The SSL Bundle, that was configured previously, can be obtained from the server factory via its name factory.getSslBundles().getBundle("server-http3") and you can configure Http3SslContextSpec.

@Component
class Http3NettyWebServerCustomizer implements WebServerFactoryCustomizer<NettyReactiveWebServerFactory> {

    @Override
    public void customize(NettyReactiveWebServerFactory factory) {
        factory.addServerCustomizers(server -> {
            SslBundle sslBundle = factory.getSslBundles().getBundle("server-http3");
            Http3SslContextSpec sslContextSpec =
                Http3SslContextSpec.forServer(sslBundle.getManagers().getKeyManagerFactory(), sslBundle.getKey().getPassword());

            return server
                    // Configure HTTP/3 protocol
                    .protocol(HttpProtocol.HTTP3)
                    // Configure HTTP/3 SslContext
                    .secure(spec -> spec.sslContext(sslContextSpec))
                    // Configure HTTP/3 settings
                    .http3Settings(spec -> spec
                            .idleTimeout(Duration.ofSeconds(5))
                            .maxData(10_000_000)
                            .maxStreamDataBidirectionalRemote(1_000_000)
                            .maxStreamsBidirectional(100));
        });
    }
}

REST Controller

The last thing that you have to add is a simple hello REST controller. The REST controller does not need any specific HTTP/3 configuration!

@RestController
class Http3Controller {

    @GetMapping("/hello")
    String hello() {
        return "Hello HTTP/3!";
    }
}

Now you are ready for your first HTTP/3 request:

curl --http3 https://localhost:8443/hello --verbose
* Connected to localhost (::1) port 8443
* using HTTP/3
* [HTTP/3] [0] OPENED stream for https://localhost:8443/hello
* [HTTP/3] [0] [:method: GET]
* [HTTP/3] [0] [:scheme: https]
* [HTTP/3] [0] [:authority: localhost:8443]
* [HTTP/3] [0] [:path: /hello]
* [HTTP/3] [0] [user-agent: curl]
* [HTTP/3] [0] [accept: */*]
> GET /hello HTTP/3
> Host: localhost:8443
> User-Agent: curl
> Accept: */*
> 
* Request completely sent off
< HTTP/3 200 
< content-type: text/plain;charset=UTF-8
< content-length: 13
< 
* Connection #0 to host localhost left intact
Hello HTTP/3!

spring-webflux-http3 repository contains the complete example!

The Client

Configuring the HTTP/3 support for the client is similar to how you configure the server!

You need to:

  • Specify the HTTP/3 protocol. By default, Reactor Netty is configured to support HTTP/1.1, so you need to change it.
  • Specify the HTTP/3 settings. By default, Reactor Netty does not provide any settings because those are strongly specific to the application, so you have to configure them: idle timeout, max streams, etc.
import reactor.netty.http.client.HttpClient;

HttpClient client = HttpClient.create()
        // Configure HTTP/3 protocol
        .protocol(HttpProtocol.HTTP3)
        // Configure HTTP/3 settings
        .http3Settings(spec -> spec
                .idleTimeout(Duration.ofSeconds(5))
                .maxData(10_000_000)
                .maxStreamDataBidirectionalLocal(1_000_000));

By default, the client uses the standard HTTP/3 SSLContext provided by Reactor Netty. However, if you need more specific configuration: truststore, ciphers, etc., you can prepare SSL Bundle similar to how you prepare it for the server and you can configure Http3SslContextSpec.

SslBundle sslBundle = factory.getSslBundles().getBundle("client-http3");
Http3SslContextSpec sslContextSpec = Http3SslContextSpec.forClient()
        // Configure TrustStore etc.
        .configure(...);
HttpClient client = HttpClient.create()
        ...
        // Configure HTTP/3 SslContext
        .secure(spec -> spec.sslContext(sslContextSpec));

WebClient

You can configure the underlying Reactor Netty HttpClient, using ReactorClientHttpConnector.

@Bean
WebClient http3WebClient(WebClient.Builder builder) {
    HttpClient client = ...;
    return builder.clientConnector(new ReactorClientHttpConnector(client)).build();
}

REST Controller

You can create a simple REST controller that makes a remote call utilizing the new HTTP/3 configuration. The REST controller does not need any specific HTTP/3 configuration!

@RestController
class Http3Controller {

    private final WebClient http3WebClient;

    Http3Controller(WebClient http3WebClient) {
        this.http3WebClient = http3WebClient;
    }

    @GetMapping("/remote")
    Mono<String> remote() {
        return http3WebClient
                .get()
                .uri("https://projectreactor.io/")
                .retrieve()
                .bodyToMono(String.class);
    }
}

spring-webflux-http3 repository contains the complete example.

RestClient

You can configure the underlying Reactor Netty HttpClient, using ReactorNettyClientRequestFactory.

@Bean
RestClient http3RestClient(RestClient.Builder builder) {
	HttpClient client = ...;
	return builder.requestFactory(new ReactorNettyClientRequestFactory(client)).build();
}

REST Controller

You can create a simple REST controller that makes a remote call utilizing the new HTTP/3 configuration. The REST controller does not need any specific HTTP/3 configuration!

@RestController
class Http3Controller {

    private final RestClient http3RestClient;

    Http3Controller(RestClient http3RestClient) {
        this.http3RestClient = http3RestClient;
    }

    @GetMapping("/remote")
    String remote() {
        return http3RestClient
                .get()
                .uri("https://projectreactor.io/")
                .retrieve()
                .body(String.class);
    }
}

spring-webmvc-http3 repository contains the complete example.

Now you are ready for your first HTTP/3 remote call:

curl --http3 https://localhost:8443/remote --verbose
* Connected to localhost (::1) port 8443
* using HTTP/3
* [HTTP/3] [0] OPENED stream for https://localhost:8443/remote
* [HTTP/3] [0] [:method: GET]
* [HTTP/3] [0] [:scheme: https]
* [HTTP/3] [0] [:authority: localhost:8443]
* [HTTP/3] [0] [:path: /remote]
* [HTTP/3] [0] [user-agent: curl/8]
* [HTTP/3] [0] [accept: */*]
> GET /remote HTTP/3
> Host: localhost:8443
> User-Agent: curl/8
> Accept: */*
> 
* Request completely sent off
< HTTP/3 200 
< content-type: text/plain;charset=UTF-8
< content-length: 17138
...

Spring Cloud Gateway

You can configure the underlying Reactor Netty HttpClient, using HttpClientCustomizer in Spring Cloud Gateway. To use this customizer, you need to register it with the Spring Cloud Gateway configuration.

@Configuration
class GatewayConfiguration {

    @Bean
    HttpClientCustomizer http3HttpClientCustomizer() {
        return httpClient ->
                httpClient
                        // Configure HTTP/3 protocol
                        .protocol(HttpProtocol.HTTP3)
                        // Configure HTTP/3 settings
                        .http3Settings(spec -> spec.idleTimeout(Duration.ofSeconds(5))
                                .maxData(10_000_000)
                                .maxStreamDataBidirectionalLocal(1_000_000));
    }
}

spring-cloud-gateway-http3 repository contains the complete example.

We hope you will enjoy the simplicity of our integration with HTTP/3. Do reach out in our GitHub/Twitter with feedback!

Get the Spring newsletter

Stay connected with the Spring newsletter

Subscribe

Get ahead

VMware offers training and certification to turbo-charge your progress.

Learn more

Get support

Tanzu Spring offers support and binaries for OpenJDK™, Spring, and Apache Tomcat® in one simple subscription.

Learn more

Upcoming events

Check out all the upcoming events in the Spring community.

View all