Skip to content

HTTP/2 server: announce a graceful shutdown without starting it #4259

Open

Description

@derekargueta

http2::Connection::graceful_shutdown sends the first GOAWAY (with stream ID 2^31-1) and then the final GOAWAY one round trip later. Proxies that drain connections want to send the first GOAWAY when draining starts, keep serving the requests that were already in flight, and send the final GOAWAY later. Envoy, for example, waits until its drain_timeout has passed. hyper can't do this today.

I've proposed server::Connection::shutdown_notice() in h2 (hyperium/h2#1001). On top of it, hyper would add http2::Connection::shutdown_notice(self: Pin<&mut Self>), and hyper-util would add a matching method to the auto connections. Is this something you'd take? I have a branch ready.

Activity

  1. seanmonstar commented on Nov 6, 2026

    @seanmonstar
    Member

    Proxies that drain connections want to send the first GOAWAY when draining starts, keep serving the requests that were already in flight, and send the final GOAWAY later.

    That's basically what hyper does already. The difference is it sends a the second GOAWAY earlier, but it will continue to allow you serve the accepted requests. It won't tear down the connection until all the handles have been dropped.

    The spec suggests this as a way to handle graceful shutdown:

    Activity on streams numbered lower than or equal to the last stream identifier might still complete successfully. The sender of a GOAWAY frame might gracefully shut down a connection by sending a GOAWAY frame, maintaining the connection in an "open" state until all in-progress streams complete.

    If a different implementation is killing the connection immediately upon seeing the GOAWAY of NO_ERROR, that seems wrong.

    Or is there something else involved?

  2. derekargueta commented on Nov 6, 2026

    @derekargueta
    Author

    Yeah sorry I worded that badly. You're right that in-flight requests are fine.

    What I actually mean is new streams. After the first GOAWAY hyper only accepts new streams for about an RTT, then the final GOAWAY goes out and anything opened after that gets refused. I'd like to keep accepting new streams for longer and send the final GOAWAY when I decide to.

    The use case is basically #3961: we're behind an LB, and on deploy there's a window between "you're being drained" and the LB actually sending traffic elsewhere. Envoy handles this with drain_timeout (first GOAWAY at drain start, final one when the timeout fires). With hyper, clients that keep opening streams during that window get refused and have to retry.

    Not a correctness issue since those streams are retryable, more about avoiding a bunch of retries on every deploy. If you don't think it's worth new API I get it. If you're open to it, hyperium/h2#1001 adds shutdown_notice() on the h2 side, and the hyper part would just expose that on http2::Connection.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions