Skip to content

Conversation

@arturobernalg
Copy link
Member

This change introduces per-stream idle and lifetime timeouts enforced by the HTTP/2 core multiplexer. Streams that exceed the configured limits are reset with H2StreamTimeoutException, while the underlying HTTP/2 connection remains usable for other streams.

Configuration is exposed via H2Config.setStreamIdleTimeout and setStreamLifetimeTimeout and is disabled by default, so existing users see no behaviour change unless they opt in. H2Stream now tracks creation and last-activity timestamps, and AbstractH2StreamMultiplexer inspects those on I/O events to apply the timeouts without background threads.

@arturobernalg arturobernalg requested a review from ok2c December 7, 2025 15:07
@rschmitt
Copy link
Contributor

rschmitt commented Dec 9, 2025

  1. What kinds of timeouts are currently supported with HTTP/2?
  2. Is H2Config the correct place to put these options, since they are not conceptually specific to HTTP/2?

@arturobernalg
Copy link
Member Author

  1. What kinds of timeouts are currently supported with HTTP/2?

    1. Is H2Config the correct place to put these options, since they are not conceptually specific to HTTP/2?

At the moment we only expose connection-level timeouts (for example via IOReactorConfig socket and session timeouts); there is no existing notion of per-stream timeout in the HTTP/2 code.
These new options are enforced entirely inside the HTTP/2 stream multiplexer, so they are scoped to HTTP/2 stream semantics and therefore live in H2Config.
If we ever want similar per-message timeouts for other protocols, we can add a higher-level abstraction then without changing this API.

@rschmitt
Copy link
Contributor

rschmitt commented Dec 9, 2025

If we ever want similar per-message timeouts for other protocols, we can add a higher-level abstraction then without changing this API.

What about RequestConfig#setResponseTimeout? The Javadoc seems to indicate that that option is supported everywhere but HTTP/2. Should we start by backfilling multiplexer support for this specific setting?

@arturobernalg
Copy link
Member Author

arturobernalg commented Dec 9, 2025

If we ever want similar per-message timeouts for other protocols, we can add a higher-level abstraction then without changing this API.

What about RequestConfig#setResponseTimeout? The Javadoc seems to indicate that that option is supported everywhere but HTTP/2. Should we start by backfilling multiplexer support for this specific setting?

RequestConfig#setResponseTimeout IMO lives in the client API, while H2Config is a transport-level setting in Core. I would keep this change scoped to Core and then look at wiring responseTimeout onto an appropriate per-stream timeout in the httpclient5 layer as a follow-up

@ok2c
Copy link
Member

ok2c commented Dec 10, 2025

@arturobernalg @rschmitt This timeout value / these timeout values must be settable / mutable at runtime exactly the same way as Socket read timeout. Just setting an initial value from a config bean is not good enough. At the client level the request execution pipeline could use that API to apply RequestConfig#responseTimeout at the initiation of the message exchange

@arturobernalg
Copy link
Member Author

@arturobernalg @rschmitt This timeout value / these timeout values must be settable / mutable at runtime exactly the same way as Socket read timeout. Just setting an initial value from a config bean is not good enough. At the client level the request execution pipeline could use that API to apply RequestConfig#responseTimeout at the initiation of the message exchange

@ok2c Makes sense. I can treat the timeouts in H2Config as defaults and keep the actual per-stream timeout state on H2Stream.
I will expose a small control API on H2StreamChannel so the client execution pipeline can set / mutate idle / lifetime timeouts at the start of an exchange and map RequestConfig#responseTimeout to the stream lifetime there.

@ok2c
Copy link
Member

ok2c commented Dec 10, 2025

I can treat the timeouts in H2Config as defaults

@arturobernalg I agree with @rschmitt that is the wrong place for timeout settings. Those are meant to be H2 protocol related configs. Please use connection socket timeout as an initial / default value.

@arturobernalg arturobernalg force-pushed the h2Stream branch 2 times, most recently from 2f5d095 to 31bbf5b Compare December 10, 2025 18:56
@arturobernalg
Copy link
Member Author

I can treat the timeouts in H2Config as defaults

@arturobernalg I agree with @rschmitt that is the wrong place for timeout settings. Those are meant to be H2 protocol related configs. Please use connection socket timeout as an initial / default value.

@rschmitt @ok2c please another pass

@rschmitt
Copy link
Contributor

I'd like to see HTTP/2 test coverage added to httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/TestAsyncSocketTimeout.java in the client integration tests. This would provide at least some assurance that we are implementing a consistent contract across the various transports and protocols.

@ok2c
Copy link
Member

ok2c commented Dec 10, 2025

I'd like to see HTTP/2 test coverage added to httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/TestAsyncSocketTimeout.java in the client integration tests. This would provide at least some assurance that we are implementing a consistent contract across the various transports and protocols.

@rschmitt That is fair, but the feature would need to have been made available in an alpha release of core or the timeout tests would need to be ported to core.

@arturobernalg
Copy link
Member Author

I'd like to see HTTP/2 test coverage added to httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/TestAsyncSocketTimeout.java in the client integration tests. This would provide at least some assurance that we are implementing a consistent contract across the various transports and protocols.

@rschmitt I’ve added an async core HTTP/2 socket-timeout test in httpcore5-testing (AsyncH2SocketTimeoutCoreTest) that mirrors the client-side TestAsyncSocketTimeout behaviour. It verifies that a per-request timeout over HTTP/2 results in HttpStreamResetException, so I believe this covers the contract you were asking for – let me know if you had something different in mind.

@ok2c
Copy link
Member

ok2c commented Dec 14, 2025

@arturobernalg Please rebase this change-set off the latest master and I will review it

Expose configuration via H2Config, throw H2StreamTimeoutException on expiry and keep the connection alive
Extend test coverage and add an example client demonstrating timed-out and successful streams
@arturobernalg
Copy link
Member Author

@arturobernalg Please rebase this change-set off the latest master and I will review it

@ok2c please take a look

@arturobernalg arturobernalg force-pushed the h2Stream branch 2 times, most recently from fae999f to 62ef875 Compare December 15, 2025 11:51
@rschmitt
Copy link
Contributor

@rschmitt I’ve added an async core HTTP/2 socket-timeout test in httpcore5-testing (AsyncH2SocketTimeoutCoreTest) that mirrors the client-side TestAsyncSocketTimeout behaviour. It verifies that a per-request timeout over HTTP/2 results in HttpStreamResetException, so I believe this covers the contract you were asking for – let me know if you had something different in mind.

Ideally I'd like to see contract testing, i.e. the same test cases running for HTTP, HTTPS, h2, and h2c. This ensures that the high-level API behaves consistently across the various implementations; it's also a very effective way of asserting that the various config options are being propagated and applied correctly throughout the client's internals. This is most effectively done through tests in httpclient5 (which has the issue that Oleg pointed out, unless you are backfilling a feature into existing APIs).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants