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

Add higher-level HTTP upgrade support to Client and Server #1563

Merged
merged 1 commit into from
Jun 14, 2018

Conversation

seanmonstar
Copy link
Member

@seanmonstar seanmonstar commented Jun 11, 2018

This proposes a way to offer an easier way to manage HTTP upgrades than using the lower-level client::conn and server::conn APIs. This includes both Upgrade + 101 Switching Protocols, and CONNECT tunneling.

Body::on_upgrade

It makes use of the Body type that is returned by hyper. When the HTTP1 state machine notices an attempt to trigger an upgrade in either the client or the server, the returned Body will have some additional state to allow getting a Future of an eventual upgrade: Body::on_upgrade(self) -> impl Future.

This on_upgrade future, if successful, will return a new hyper::upgrade::Upgraded type that fully owns the original IO transport, after the upgrade. The Upgraded can itself be used as an AsyncRead/AsyncWrite for convenience (and automatically including any buffered bytes as part of the reads). However, if desired, you can also try to deconstruct/downcast from an Upgraded into hyper::upgrade::Parts<T>.

hyper::upgrade::Upgraded

An added benefit to this API is that it allows adding support for CONNECT over HTTP2 without breaking backwards compatibility. The tricky part there normally is that CONNECT in HTTP2 doesn't actually take control of the IO transport, but instead continues to send DATA frames over the h2 stream. So, once the h2 library adds support, hyper can encapsulate that support in a private impl AsyncRead + AsyncWrite implementation, and still just return an Upgraded.

A papercut in the proposal is that Upgraded needs to put the IO in a trait object that can be put Body, which is Send. There was a missing I: Send bounds on server::conn::Connection, which makes it impossible to use this new API when using the lower-level Http::server_connection. The proposed solution is to add Connection::with_upgrades() that converts into a type that does have a Send bound, and allows using this API.

Any usage of serve_connection that forgets to add with_upgrades will not have upgrades work. The on_upgrade future will yield an Error. This error however, does include specific messaging when an upgrade was expected to work, but couldn't because of use of lower-level APIs. Additionally, this means that manual upgrades with the lower-level APIs still work correctly.

TODO

  • Implement the feature
  • Write tests
  • Include an example in the examples directory of both client and server usage.
  • Document the hyper::upgrade module.
  • Document the server::conn::Connection::with_upgrades method.
  • Investigate performance implications of adding upgrade machinery.
  • Consider if messaging needs improving for anyone doing manual upgrades.

Closes #1395

- Adds `Body::on_upgrade()` that returns an `OnUpgrade` future.
- Adds `hyper::upgrade` module containing types for dealing with
  upgrades.
- Adds `server::conn::Connection::with_upgrades()` method to enable
  these upgrades when using lower-level API (because of a missing
  `Send` bound on the transport generic).
- Client connections are automatically enabled.
- Optimizes request parsing, to make up for extra work to look for
  upgrade requests.
  - Returns a smaller `DecodedLength` type instead of the fatter
    `Decoder`, which should also allow a couple fewer branches.
  - Removes the `Decode::Ignore` wrapper enum, and instead ignoring
    1xx responses is handled directly in the response parsing code.
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.

1 participant