Skip to content

Client and server timeout support#182

Open
cpu wants to merge 5 commits into
rustls:mainfrom
cpu:cpu-lts-timeout_dev
Open

Client and server timeout support#182
cpu wants to merge 5 commits into
rustls:mainfrom
cpu:cpu-lts-timeout_dev

Conversation

@cpu

@cpu cpu commented Jun 19, 2026

Copy link
Copy Markdown
Member

This takes the client-side timeout work from #178 with some review feedback applied, and extends it to the server-side (both for the simple case, as well as the lazy acceptor machinery).

The review feedback modifications to #178 can be reviewed as unsquashed fixup commits on this branch. In summary:

  • switched an expect() to an unreachable!() with some commentary (a6d5566)
  • In-lined a single-use helper (5e7d2cf)
  • Re-ordered some elements (997ad7c)
  • Reworked the original unit test to use tokio's start_paused (4a0b85d)
  • Added a bit more coverage (90d54c6)
  • Extracted the new code somewhere I could reuse it (b966f10)

Afterwards I overlaid server support for TlsAcceptor/FallibleAccept, and then extended it for the LazyConfigAcceptor and into_stream() bits.

@cpu cpu self-assigned this Jun 19, 2026
@cpu cpu force-pushed the cpu-lts-timeout_dev branch from 2cf7b0d to cf8ec43 Compare June 19, 2026 18:38
@cpu

cpu commented Jun 19, 2026

Copy link
Copy Markdown
Member Author

Lastly, I bumped the aws-lc-rs version to fix a windows CI builder failure I was seeing otherwise.

Dropped this. I think I just had a stale base branch.

@cpu cpu requested review from ctz and djc June 19, 2026 18:42
Comment thread src/common/timeout.rs
use crate::common::{IoSession, MidHandshake};

/// A `MidHandshake` future bundled with an optional deadline.
pub(crate) struct HandshakeFuture<IS: IoSession> {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why couldn't we use a tokio::time::Timeout<MindHandshake<IS>> instead of defining this wrapper ourselves?

@cpu cpu Jun 26, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried that initially after reviewing 178 and having the same thought, but it didn't seem like a good fit to me after I tried it a bit.

  • We still end up needing a wrapper enum or similar indirection for whether there is/isn't a Timeout unless we do something hacky like always allocating a Timeout with max duration.
  • Timeout is !Unpin but MidHandshake and everything else is currently Unpin, so we need to Pin<Box<...>> it at the cost of a heap allocation and more nesting complexity.
  • Most importantly, I couldn't figure out a way to maintain Connect::get_mut() with a Pin<Box<Timeout<...>>> or the FallibleConnect implementation where we need to recover the IO without using unsafe.

I'm not particularly adept at async rust trickery, but achieving this in a semver compatible way using tokio::time::Timeout without any new deps or unsafe didn't seem tractable, and the implementation as done in this branch (and originally in 178) doesn't seem particularly complex in comparison.

If you think I'm missing something maybe we could land this approach and then refactor it down to a tokio::time::Timeout as follow-up?

@djc

djc commented Jun 24, 2026

Copy link
Copy Markdown
Member

Thanks for picking this up!

lucastemb and others added 5 commits June 26, 2026 10:37
Matches the client-side implementation. LazyConfigAcceptor/into_stream()
paths not yet supported.
Internally store an absolute `Instant` rather than a relative
`Duration`, and `sleep_until()` that deadline. The public
`new(Option<Duration>)` constructor converts eagerly so existing callers
see no behavior change.

Add a `from_deadline(Option<Instant>)` constructor for callers that
already have an absolute deadline. This is needed so
`LazyConfigAcceptor` can establish a deadline in its phase and propagate
it through `StartHandshake::into_stream()` to the post-ClientHello
Accept, keeping the two phases sharing the same wall-clock deadline.
@cpu cpu force-pushed the cpu-lts-timeout_dev branch from cf8ec43 to 044e824 Compare June 26, 2026 14:44
@thalesfragoso

thalesfragoso commented Jun 26, 2026

Copy link
Copy Markdown

I'm still not sure if this feature belongs in the crate, you don't see something like this in e.g. tokio::net::TcpStream, specifically because canceling futures is "trivial" in rust. Arguably, the only thing that can't be implemented outside of the crate is the take_io method.

Moreover, this implementation is not even optimal because we have to box the Sleep future or use unsafe. We can't even use e.g. pin-project-lite because it would make the Connect future !Unpin and that would be a breaking change.

@thalesfragoso

Copy link
Copy Markdown

I guess my suggestion is to just implement take_io in the Connect future.

@cpu

cpu commented Jun 26, 2026

Copy link
Copy Markdown
Member Author

Thanks for taking a look.

I'm still not sure if this feature belongs in the crate, you don't see something like this in e.g. tokio::net::TcpStream, specifically because canceling futures is "trivial" in rust.

Perhaps, but I'm not sure I'm completely convinced of that yet. I think the linked issues show there's some demand and having each consumer implementing the same things themselves if we can offer it sensibly once via this crate might not be optimal.

Arguably, the only thing that can't be implemented outside of the crate is the take_io method.

I agree on the connect side that there are approaches to doing most of this outside the crate but I do think the take_io() part is important, and also don't know that this line of thinking extends as clearly to the server-side portions.

Moreover, this implementation is not even optimal because we have to box the Sleep future or use unsafe. We can't even use e.g. pin-project-lite because it would make the Connect future !Unpin and that would be a breaking change.

I'm not sure this feedback applies to the code in-branch? It isn't using pin-project, or unsafe, or making any breaking changes.

I guess my suggestion is to just implement take_io in the Connect future.

If you want to open an alternative PR that achieves equivalent support for timeouts across both client/server side internally in a different way, or shows how to achieve it comprehensively from examples/ with less crate changes I'd be open to reviewing.

@cpu

cpu commented Jun 26, 2026

Copy link
Copy Markdown
Member Author

I'm still not sure if this feature belongs in the crate,

Opinions can of course change over time, but part of why I looked at this was an initial signal from Djc in discord that this crate would be an acceptable place to make a change like this.

@thalesfragoso

Copy link
Copy Markdown

I'm not sure this feedback applies to the code in-branch? It isn't using pin-project, or unsafe, or making any breaking changes.

I meant that the code in-branch is not (technically) optimal because it boxes the Sleep future only to avoid breaking changes.

If you want to open an alternative PR that achieves equivalent support for timeouts across both client/server side internally in a different way...

My questioning is exactly if we should implement this internally or not. Your implementation is perfectly fine given the constraints, and I'm not strongly opposed to merging this PR.

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.

4 participants