Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions docs/modules/ROOT/pages/http-client.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,41 @@ include::{examples-dir}/channeloptions/Application.java[lines=18..54]
<4> Configures the time between individual `keepalive` probes to 1 minute.
<5> Configures the maximum number of TCP `keepalive` probes to 8.

=== HTTP/2 Connection Health Check
For `HTTP/2 connections`, it's recommended to configure `PING` frames for connection maintenance and health monitoring.
The `HttpClient` uses `PING` frames to detect unresponsive connections when `maxIdleTime` on the connection pool is configured.

Benefits of using `PING` frames:

- Actively probes connection liveness when idle
- Detects unresponsive connections through timely health checks
- Closes stale connections automatically
- Maintains reliable connection pool state

[NOTE]
====
`HTTP/2` `PING` frame-based health checks require `maxIdleTime` on the connection pool to be configured as a prerequisite.
Without idle timeout configuration, PING frames will not be sent and connections may remain open indefinitely,
preventing proper detection of inactive or unresponsive connections.

The idle timeout and PING settings work together:

1. Connection becomes idle (no activity for `maxIdleTime`)
2. A PING frame is sent to check if the peer is responsive
3. If no ACK within `pingAckTimeout`, retry based on `pingAckDropThreshold`
4. If all attempts fail, the connection is closed
====

To enable `PING` frames for `HTTP/2` connections, configure the `HttpClient` as follows:

{examples-link}/liveness/Application.java
----
include::{examples-dir}/liveness/Application.java[lines=27..52]
----
<1> Configure `maxIdleTime` to enable PING-based health checks. When a connection is idle for this duration, a PING frame will be sent.
<2> Configure `pingAckTimeout` - the maximum time to wait for each PING ACK response. If no ACK is received within this timeout, another PING is sent (up to the threshold).
<3> Configure `pingAckDropThreshold` - the maximum number of PING frames to send before closing the connection. For example, `2` means send up to 2 PING frames (if first fails, try once more) before closing.

[[ssl-tls-timeout]]
==== SSL/TLS Timeout
`HttpClient` supports the SSL/TLS functionality provided by Netty.
Expand Down
35 changes: 35 additions & 0 deletions docs/modules/ROOT/pages/http-server.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,41 @@ include::{examples-dir}/idle/timeout/Application.java[lines=18..35]
----
<1> Configures the default idle timeout to 1 second.

=== HTTP/2 Connection Health Check
For `HTTP/2 connections`, it's recommended to configure `PING` frames for connection maintenance and health monitoring.
The `HttpServer` uses `PING` frames to detect unresponsive clients when `idleTimeout` is configured.

Benefits of using `PING` frames:

- Actively probes connection liveness when idle
- Detects unresponsive clients through timely health checks
- Closes stale connections automatically
- Prevents resource exhaustion from dead connections

[NOTE]
====
`HTTP/2` `PING` frame-based health checks require `idleTimeout` to be configured as a prerequisite.
Without idle timeout configuration, PING frames will not be sent and connections may remain open indefinitely,
preventing proper detection of inactive or unresponsive connections.

The idle timeout and PING settings work together:

1. Connection becomes idle (no activity for `idleTimeout`)
2. A PING frame is sent to check if the client is responsive
3. If no ACK within `pingAckTimeout`, retry based on `pingAckDropThreshold`
4. If all attempts fail, the connection is closed
====

To enable `PING` frames for `HTTP/2` connections, configure the `HttpServer` as follows:

{examples-link}/liveness/Application.java
----
include::{examples-dir}/liveness/Application.java[lines=26..50]
----
<1> Configure `idleTimeout` to enable PING-based health checks. When a connection is idle for this duration, a PING frame will be sent.
<2> Configure `pingAckTimeout` - the maximum time to wait for each PING ACK response. If no ACK is received within this timeout, another PING is sent (up to the threshold).
<3> Configure `pingAckDropThreshold` - the maximum number of PING frames to send before closing the connection. For example, `2` means send up to 2 PING frames (if first fails, try once more) before closing.

[[http-server-ssl-tls-timeout]]
=== SSL/TLS Timeout
`HttpServer` supports the SSL/TLS functionality provided by Netty.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -644,6 +644,11 @@ public AllocationStrategy<?> allocationStrategy() {
return allocationStrategy;
}

@Nullable
public BiPredicate<Connection, ConnectionMetadata> evictionPredicate() {
return evictionPredicate;
}

public long maxIdleTime() {
return this.maxIdleTime;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright (c) 2025 VMware, Inc. or its affiliates, All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package reactor.netty.examples.documentation.http.client.liveness;

import io.netty.handler.codec.http.HttpHeaders;
import reactor.core.publisher.Mono;
import reactor.netty.http.HttpProtocol;
import reactor.netty.http.client.HttpClient;
import reactor.netty.resources.ConnectionProvider;
import reactor.util.function.Tuple2;

import java.time.Duration;

public class Application {

public static void main(String[] args) {
ConnectionProvider provider =
ConnectionProvider.builder("liveness")
.maxIdleTime(Duration.ofSeconds(2)) //<1>
.build();

HttpClient client =
HttpClient.create(provider)
.protocol(HttpProtocol.H2)
.secure()
.http2Settings(builder -> builder.pingAckTimeout(Duration.ofMillis(600)) //<2>
.pingAckDropThreshold(2)); //<3>

Tuple2<String, HttpHeaders> response =
client.get()
.uri("https://example.com/")
.responseSingle((res, bytes) -> bytes.asString()
.zipWith(Mono.just(res.responseHeaders())))
.block();

System.out.println("Used stream ID: " + response.getT2().get("x-http2-stream-id"));
System.out.println("Response: " + response.getT1());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright (c) 2025 VMware, Inc. or its affiliates, All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package reactor.netty.examples.documentation.http.server.liveness;

import reactor.netty.DisposableServer;
import reactor.netty.http.Http2SslContextSpec;
import reactor.netty.http.HttpProtocol;
import reactor.netty.http.server.HttpServer;

import java.io.File;
import java.time.Duration;

public class Application {

public static void main(String[] args) {
File cert = new File("certificate.crt");
File key = new File("private.key");

Http2SslContextSpec http2SslContextSpec = Http2SslContextSpec.forServer(cert, key);

DisposableServer server =
HttpServer.create()
.port(8080)
.protocol(HttpProtocol.H2)
.secure(spec -> spec.sslContext(http2SslContextSpec))
.idleTimeout(Duration.ofSeconds(1)) //<1>
.http2Settings(builder -> builder.pingAckTimeout(Duration.ofMillis(600)) //<2>
.pingAckDropThreshold(2)) //<3>
.bindNow();

server.onDispose()
.block();
}
}
1 change: 1 addition & 0 deletions reactor-netty-http/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ dependencies {
testImplementation "io.specto:hoverfly-java-junit5:$hoverflyJavaVersion"
testImplementation "org.apache.tomcat.embed:tomcat-embed-core:$tomcatVersion"
testImplementation "io.projectreactor:reactor-test:$testAddonVersion"
testImplementation "io.projectreactor.addons:reactor-extra:$reactorAddonsVersion"
testImplementation "org.assertj:assertj-core:$assertJVersion"
testImplementation "org.awaitility:awaitility:$awaitilityVersion"
testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion"
Expand Down
Loading