feat(http1): Add higher-level HTTP upgrade support to Client and Server (#1563)
- 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.
Ref #1563
Closes #1395
This commit is contained in:
@@ -10,6 +10,7 @@ use http::HeaderMap;
|
||||
use common::Never;
|
||||
use super::{Chunk, Payload};
|
||||
use super::internal::{FullDataArg, FullDataRet};
|
||||
use upgrade::OnUpgrade;
|
||||
|
||||
type BodySender = mpsc::Sender<Result<Chunk, ::Error>>;
|
||||
|
||||
@@ -21,15 +22,9 @@ type BodySender = mpsc::Sender<Result<Chunk, ::Error>>;
|
||||
#[must_use = "streams do nothing unless polled"]
|
||||
pub struct Body {
|
||||
kind: Kind,
|
||||
/// Allow the client to pass a future to delay the `Body` from returning
|
||||
/// EOF. This allows the `Client` to try to put the idle connection
|
||||
/// back into the pool before the body is "finished".
|
||||
///
|
||||
/// The reason for this is so that creating a new request after finishing
|
||||
/// streaming the body of a response could sometimes result in creating
|
||||
/// a brand new connection, since the pool didn't know about the idle
|
||||
/// connection yet.
|
||||
delayed_eof: Option<DelayEof>,
|
||||
/// Keep the extra bits in an `Option<Box<Extra>>`, so that
|
||||
/// Body stays small in the common case (no extras needed).
|
||||
extra: Option<Box<Extra>>,
|
||||
}
|
||||
|
||||
enum Kind {
|
||||
@@ -43,6 +38,19 @@ enum Kind {
|
||||
Wrapped(Box<Stream<Item=Chunk, Error=Box<::std::error::Error + Send + Sync>> + Send>),
|
||||
}
|
||||
|
||||
struct Extra {
|
||||
/// Allow the client to pass a future to delay the `Body` from returning
|
||||
/// EOF. This allows the `Client` to try to put the idle connection
|
||||
/// back into the pool before the body is "finished".
|
||||
///
|
||||
/// The reason for this is so that creating a new request after finishing
|
||||
/// streaming the body of a response could sometimes result in creating
|
||||
/// a brand new connection, since the pool didn't know about the idle
|
||||
/// connection yet.
|
||||
delayed_eof: Option<DelayEof>,
|
||||
on_upgrade: OnUpgrade,
|
||||
}
|
||||
|
||||
type DelayEofUntil = oneshot::Receiver<Never>;
|
||||
|
||||
enum DelayEof {
|
||||
@@ -89,7 +97,6 @@ impl Body {
|
||||
Self::new_channel(None)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn new_channel(content_length: Option<u64>) -> (Sender, Body) {
|
||||
let (tx, rx) = mpsc::channel(0);
|
||||
let (abort_tx, abort_rx) = oneshot::channel();
|
||||
@@ -139,10 +146,20 @@ impl Body {
|
||||
Body::new(Kind::Wrapped(Box::new(mapped)))
|
||||
}
|
||||
|
||||
/// Converts this `Body` into a `Future` of a pending HTTP upgrade.
|
||||
///
|
||||
/// See [the `upgrade` module](::upgrade) for more.
|
||||
pub fn on_upgrade(self) -> OnUpgrade {
|
||||
self
|
||||
.extra
|
||||
.map(|ex| ex.on_upgrade)
|
||||
.unwrap_or_else(OnUpgrade::none)
|
||||
}
|
||||
|
||||
fn new(kind: Kind) -> Body {
|
||||
Body {
|
||||
kind: kind,
|
||||
delayed_eof: None,
|
||||
extra: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,23 +167,46 @@ impl Body {
|
||||
Body::new(Kind::H2(recv))
|
||||
}
|
||||
|
||||
pub(crate) fn set_on_upgrade(&mut self, upgrade: OnUpgrade) {
|
||||
debug_assert!(!upgrade.is_none(), "set_on_upgrade with empty upgrade");
|
||||
let extra = self.extra_mut();
|
||||
debug_assert!(extra.on_upgrade.is_none(), "set_on_upgrade twice");
|
||||
extra.on_upgrade = upgrade;
|
||||
}
|
||||
|
||||
pub(crate) fn delayed_eof(&mut self, fut: DelayEofUntil) {
|
||||
self.delayed_eof = Some(DelayEof::NotEof(fut));
|
||||
self.extra_mut().delayed_eof = Some(DelayEof::NotEof(fut));
|
||||
}
|
||||
|
||||
fn take_delayed_eof(&mut self) -> Option<DelayEof> {
|
||||
self
|
||||
.extra
|
||||
.as_mut()
|
||||
.and_then(|extra| extra.delayed_eof.take())
|
||||
}
|
||||
|
||||
fn extra_mut(&mut self) -> &mut Extra {
|
||||
self
|
||||
.extra
|
||||
.get_or_insert_with(|| Box::new(Extra {
|
||||
delayed_eof: None,
|
||||
on_upgrade: OnUpgrade::none(),
|
||||
}))
|
||||
}
|
||||
|
||||
fn poll_eof(&mut self) -> Poll<Option<Chunk>, ::Error> {
|
||||
match self.delayed_eof.take() {
|
||||
match self.take_delayed_eof() {
|
||||
Some(DelayEof::NotEof(mut delay)) => {
|
||||
match self.poll_inner() {
|
||||
ok @ Ok(Async::Ready(Some(..))) |
|
||||
ok @ Ok(Async::NotReady) => {
|
||||
self.delayed_eof = Some(DelayEof::NotEof(delay));
|
||||
self.extra_mut().delayed_eof = Some(DelayEof::NotEof(delay));
|
||||
ok
|
||||
},
|
||||
Ok(Async::Ready(None)) => match delay.poll() {
|
||||
Ok(Async::Ready(never)) => match never {},
|
||||
Ok(Async::NotReady) => {
|
||||
self.delayed_eof = Some(DelayEof::Eof(delay));
|
||||
self.extra_mut().delayed_eof = Some(DelayEof::Eof(delay));
|
||||
Ok(Async::NotReady)
|
||||
},
|
||||
Err(_done) => {
|
||||
@@ -180,7 +220,7 @@ impl Body {
|
||||
match delay.poll() {
|
||||
Ok(Async::Ready(never)) => match never {},
|
||||
Ok(Async::NotReady) => {
|
||||
self.delayed_eof = Some(DelayEof::Eof(delay));
|
||||
self.extra_mut().delayed_eof = Some(DelayEof::Eof(delay));
|
||||
Ok(Async::NotReady)
|
||||
},
|
||||
Err(_done) => {
|
||||
|
||||
Reference in New Issue
Block a user