Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(http1): graceful shutdown first byte timeout #3808

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

chinedufn
Copy link

This commit introduces a new Connection::graceful_shutdown_with_config method that gives users control over the HTTP/1 graceful process.

Before this commit, if a graceful shutdown was initiated on an inactive connection hyper would immediately close it.

As of this commit the GracefulShutdownConfig::first_byte_read_timeout method can be used to give inactive connections a grace period where, should the server begin receiving bytes from them before the deadline, the request will be processed.

HTTP/2 Graceful Shutdowns

This commit does not modify hyper's HTTP/2 graceful shutdown process.

hyper already uses the HTTP/2 GOAWAY frame, meaning that hyper already gives inactive connections a brief period during which they can transmit their final requests.

Note that while this commit enables slightly more graceful shutdowns for HTTP/1 servers, HTTP/2 graceful shutdowns are still superior.

HTTP/2's GOAWAY frame allows the server to finish processing a last batch of multiple incoming requests from the client, whereas the new graceful shutdown configuration in this commit only allows the server to wait for one final incoming request to be received.

This limitations stems from a limitation in HTTP/1, where there is nothing like the GOAWAY frame that can be used to coordinate the graceful shutdown process with the client in the face of multiple concurrent incoming requests.

Instead for HTTP/1 connections hyper gracefully shuts down by disabling Keep-Alive, at which point the server will only receive at most one new request, even if the client has multiple requests that are moments from reaching the server.

Motivating Use Case

I'm working on a server that is being designed to handle a large amount of traffic from a large number of clients.

It is expected that many clients will open many TCP connections with the server every second.

As a server receives more traffic it becomes increasingly likely that at the point that it begins gracefully shutting down there are connections that were just opened, but the client's bytes have not yet been seen by the server.

Before this commit, calling Connection::graceful_shutdown on such a freshly opened HTTP/1 connection will immediately close it. This means that the client will get an error, despite the server having been perfectly capable of handling a final request before closing the connection.

This commit solves this problem for HTTP/1 clients that tend to send one request at a time.
By setting a GracefulShutdownConfig::first_byte_read_timeout of, say, 1 second, the server will wait a moment to see if any of the client's bytes have been received.
During this period, the client will have been informed that Keep-Alive is now disabled, meaning that at most one more request will be processed.

Clients that have multiple in-flight requests that have not yet reached the server will have at most one of those requests handled, even if all of them reach the server before the first_byte_read_timeout. This is a limitation of HTTP/1.

Work to do in other Crates

hyper-util

To expose this to users that use hyper-util, a method should be added to hyper-util's Connection type.
This new hyper-util Connection::graceful_shutdown_with_config method would expose a http1_first_byte_read_timeout method that would lead hyper-util to set hyper GracefulShutdownConfig::first_byte_read_timeout.


Closes #3792

This commit introduces a new `Connection::graceful_shutdown_with_config`
method that gives users control over the HTTP/1 graceful process.

Before this commit, if a graceful shutdown was initiated on an inactive
connection hyper would immediately close it.

As of this commit the `GracefulShutdownConfig::first_byte_read_timeout`
method can be used to give inactive connections a grace period where,
should the server begin receiving bytes from them before the deadline,
the request will be processed.

## HTTP/2 Graceful Shutdowns

This commit does not modify hyper's HTTP/2 graceful shutdown process.

`hyper` already uses the HTTP/2 `GOAWAY` frame, meaning that `hyper`
already gives inactive connections a brief period during which they can
transmit their final requests.

Note that while this commit enables slightly more graceful shutdowns for
HTTP/1 servers, HTTP/2 graceful shutdowns are still superior.

HTTP/2's `GOAWAY` frame allows the server to finish processing a last
batch of multiple incoming requests from the client, whereas the new
graceful shutdown configuration in this commit only allows the server to
wait for one final incoming request to be received.

This limitations stems from a limitation in HTTP/1, where there is
nothing like the `GOAWAY` frame that can be used to coordinate the
graceful shutdown process with the client in the face of multiple
concurrent incoming requests.

Instead for HTTP/1 connections `hyper` gracefully shuts down by
disabling Keep-Alive, at which point the server will only receive at
most one new request, even if the client has multiple requests that are
moments from reaching the server.

## Motivating Use Case

I'm working on a server that is being designed to handle a large amount
of traffic from a large number of clients.

It is expected that many clients will open many TCP connections with the
server every second.

As a server receives more traffic it becomes increasingly likely that at
the point that it begins gracefully shutting down there are connections
that were just opened, but the client's bytes have not yet been seen by
the server.

Before this commit, calling `Connection::graceful_shutdown` on such a
freshly opened HTTP/1 connection will immediately close it. This means
that the client will get an error, despite the server having been
perfectly capable of handling a final request before closing the
connection.

This commit solves this problem for HTTP/1 clients that tend to send one
request at a time.
By setting a `GracefulShutdownConfig::first_byte_read_timeout` of, say,
1 second, the server will wait a moment to see if any of the client's
bytes have been received.
During this period, the client will have been informed that Keep-Alive
is now disabled, meaning that at most one more request will be
processed.

Clients that have multiple in-flight requests that have not yet reached
the server will have at most one of those requests handled, even if all
of them reach the server before the `first_byte_read_timeout`.
This is a limitation of HTTP/1.

## Work to do in other Crates

#### hyper-util

To expose this to users that use `hyper-util`, a method should be added
to `hyper-util`'s `Connection` type.
This new `hyper-util Connection::graceful_shutdown_with_config` method
would expose a `http1_first_byte_read_timeout` method that would lead
`hyper-util` to set `hyper
GracefulShutdownConfig::first_byte_read_timeout`.

---

Closes hyperium#3792
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.

Gracefully shutting down a TLS server sometimes leads to the client not receiving a response
1 participant