feat(http1): Make HTTP/1 support an optional feature

cc #2251

BREAKING CHANGE: This puts all HTTP/1 methods and support behind an
  `http1` cargo feature, which will not be enabled by default. To use
  HTTP/1, add `features = ["http1"]` to the hyper dependency in your
  `Cargo.toml`.
This commit is contained in:
Sean McArthur
2020-11-16 15:39:10 -08:00
parent 2f2ceb2426
commit 2a19ab74ed
31 changed files with 459 additions and 239 deletions

View File

@@ -1,4 +1,3 @@
[package] [package]
name = "hyper" name = "hyper"
version = "0.14.0-dev" # don't forget to update html_root_url version = "0.14.0-dev" # don't forget to update html_root_url
@@ -76,11 +75,11 @@ default = [
"runtime", "runtime",
"stream", "stream",
#"http1", "http1",
"http2", "http2",
] ]
full = [ full = [
#"http1", "http1",
"http2", "http2",
"stream", "stream",
"runtime", "runtime",
@@ -97,7 +96,7 @@ tcp = [
] ]
# HTTP versions # HTTP versions
#http1 = [] http1 = []
http2 = ["h2"] http2 = ["h2"]
# `impl Stream` for things # `impl Stream` for things

View File

@@ -4,19 +4,23 @@ use std::error::Error as StdError;
use std::fmt; use std::fmt;
use bytes::Bytes; use bytes::Bytes;
use futures_channel::{mpsc, oneshot}; use futures_channel::mpsc;
#[cfg(any(feature = "http1", feature = "http2"))]
use futures_channel::oneshot;
use futures_core::Stream; // for mpsc::Receiver use futures_core::Stream; // for mpsc::Receiver
#[cfg(feature = "stream")] #[cfg(feature = "stream")]
use futures_util::TryStreamExt; use futures_util::TryStreamExt;
use http::HeaderMap; use http::HeaderMap;
use http_body::{Body as HttpBody, SizeHint}; use http_body::{Body as HttpBody, SizeHint};
use super::DecodedLength;
#[cfg(feature = "stream")] #[cfg(feature = "stream")]
use crate::common::sync_wrapper::SyncWrapper; use crate::common::sync_wrapper::SyncWrapper;
use crate::common::{task, watch, Future, Never, Pin, Poll}; use crate::common::{task, watch, Pin, Poll};
#[cfg(any(feature = "http1", feature = "http2"))]
use crate::common::{Future, Never};
#[cfg(feature = "http2")] #[cfg(feature = "http2")]
use crate::proto::h2::ping; use crate::proto::h2::ping;
use crate::proto::DecodedLength;
use crate::upgrade::OnUpgrade; use crate::upgrade::OnUpgrade;
type BodySender = mpsc::Sender<Result<Bytes, crate::Error>>; type BodySender = mpsc::Sender<Result<Bytes, crate::Error>>;
@@ -67,14 +71,17 @@ struct Extra {
on_upgrade: OnUpgrade, on_upgrade: OnUpgrade,
} }
#[cfg(any(feature = "http1", feature = "http2"))]
type DelayEofUntil = oneshot::Receiver<Never>; type DelayEofUntil = oneshot::Receiver<Never>;
enum DelayEof { enum DelayEof {
/// Initial state, stream hasn't seen EOF yet. /// Initial state, stream hasn't seen EOF yet.
#[cfg(any(feature = "http1", feature = "http2"))]
NotEof(DelayEofUntil), NotEof(DelayEofUntil),
/// Transitions to this state once we've seen `poll` try to /// Transitions to this state once we've seen `poll` try to
/// return EOF (`None`). This future is then polled, and /// return EOF (`None`). This future is then polled, and
/// when it completes, the Body finally returns EOF (`None`). /// when it completes, the Body finally returns EOF (`None`).
#[cfg(any(feature = "http1", feature = "http2"))]
Eof(DelayEofUntil), Eof(DelayEofUntil),
} }
@@ -203,6 +210,7 @@ impl Body {
body body
} }
#[cfg(feature = "http1")]
pub(crate) fn set_on_upgrade(&mut self, upgrade: OnUpgrade) { pub(crate) fn set_on_upgrade(&mut self, upgrade: OnUpgrade) {
debug_assert!(!upgrade.is_none(), "set_on_upgrade with empty upgrade"); debug_assert!(!upgrade.is_none(), "set_on_upgrade with empty upgrade");
let extra = self.extra_mut(); let extra = self.extra_mut();
@@ -210,6 +218,7 @@ impl Body {
extra.on_upgrade = upgrade; extra.on_upgrade = upgrade;
} }
#[cfg(any(feature = "http1", feature = "http2"))]
pub(crate) fn delayed_eof(&mut self, fut: DelayEofUntil) { pub(crate) fn delayed_eof(&mut self, fut: DelayEofUntil) {
self.extra_mut().delayed_eof = Some(DelayEof::NotEof(fut)); self.extra_mut().delayed_eof = Some(DelayEof::NotEof(fut));
} }
@@ -220,6 +229,7 @@ impl Body {
.and_then(|extra| extra.delayed_eof.take()) .and_then(|extra| extra.delayed_eof.take())
} }
#[cfg(any(feature = "http1", feature = "http2"))]
fn extra_mut(&mut self) -> &mut Extra { fn extra_mut(&mut self) -> &mut Extra {
self.extra.get_or_insert_with(|| { self.extra.get_or_insert_with(|| {
Box::new(Extra { Box::new(Extra {
@@ -231,6 +241,7 @@ impl Body {
fn poll_eof(&mut self, cx: &mut task::Context<'_>) -> Poll<Option<crate::Result<Bytes>>> { fn poll_eof(&mut self, cx: &mut task::Context<'_>) -> Poll<Option<crate::Result<Bytes>>> {
match self.take_delayed_eof() { match self.take_delayed_eof() {
#[cfg(any(feature = "http1", feature = "http2"))]
Some(DelayEof::NotEof(mut delay)) => match self.poll_inner(cx) { Some(DelayEof::NotEof(mut delay)) => match self.poll_inner(cx) {
ok @ Poll::Ready(Some(Ok(..))) | ok @ Poll::Pending => { ok @ Poll::Ready(Some(Ok(..))) | ok @ Poll::Pending => {
self.extra_mut().delayed_eof = Some(DelayEof::NotEof(delay)); self.extra_mut().delayed_eof = Some(DelayEof::NotEof(delay));
@@ -246,6 +257,7 @@ impl Body {
}, },
Poll::Ready(Some(Err(e))) => Poll::Ready(Some(Err(e))), Poll::Ready(Some(Err(e))) => Poll::Ready(Some(Err(e))),
}, },
#[cfg(any(feature = "http1", feature = "http2"))]
Some(DelayEof::Eof(mut delay)) => match Pin::new(&mut delay).poll(cx) { Some(DelayEof::Eof(mut delay)) => match Pin::new(&mut delay).poll(cx) {
Poll::Ready(Ok(never)) => match never {}, Poll::Ready(Ok(never)) => match never {},
Poll::Pending => { Poll::Pending => {
@@ -254,6 +266,8 @@ impl Body {
} }
Poll::Ready(Err(_done)) => Poll::Ready(None), Poll::Ready(Err(_done)) => Poll::Ready(None),
}, },
#[cfg(not(any(feature = "http1", feature = "http2")))]
Some(delay_eof) => match delay_eof {},
None => self.poll_inner(cx), None => self.poll_inner(cx),
} }
} }
@@ -300,6 +314,7 @@ impl Body {
} }
} }
#[cfg(feature = "http1")]
pub(super) fn take_full_data(&mut self) -> Option<Bytes> { pub(super) fn take_full_data(&mut self) -> Option<Bytes> {
if let Kind::Once(ref mut chunk) = self.kind { if let Kind::Once(ref mut chunk) = self.kind {
chunk.take() chunk.take()
@@ -549,6 +564,7 @@ impl Sender {
.try_send(Err(crate::Error::new_body_write_aborted())); .try_send(Err(crate::Error::new_body_write_aborted()));
} }
#[cfg(feature = "http1")]
pub(crate) fn send_error(&mut self, err: crate::Error) { pub(crate) fn send_error(&mut self, err: crate::Error) {
let _ = self.tx.try_send(Err(err)); let _ = self.tx.try_send(Err(err));
} }

100
src/body/length.rs Normal file
View File

@@ -0,0 +1,100 @@
use std::fmt;
#[derive(Clone, Copy, PartialEq, Eq)]
pub(crate) struct DecodedLength(u64);
#[cfg(any(feature = "http1", feature = "http2", test))]
const MAX_LEN: u64 = std::u64::MAX - 2;
impl DecodedLength {
pub(crate) const CLOSE_DELIMITED: DecodedLength = DecodedLength(::std::u64::MAX);
pub(crate) const CHUNKED: DecodedLength = DecodedLength(::std::u64::MAX - 1);
pub(crate) const ZERO: DecodedLength = DecodedLength(0);
#[cfg(test)]
pub(crate) fn new(len: u64) -> Self {
debug_assert!(len <= MAX_LEN);
DecodedLength(len)
}
/// Takes the length as a content-length without other checks.
///
/// Should only be called if previously confirmed this isn't
/// CLOSE_DELIMITED or CHUNKED.
#[inline]
#[cfg(feature = "http1")]
pub(crate) fn danger_len(self) -> u64 {
debug_assert!(self.0 < Self::CHUNKED.0);
self.0
}
/// Converts to an Option<u64> representing a Known or Unknown length.
pub(crate) fn into_opt(self) -> Option<u64> {
match self {
DecodedLength::CHUNKED | DecodedLength::CLOSE_DELIMITED => None,
DecodedLength(known) => Some(known),
}
}
/// Checks the `u64` is within the maximum allowed for content-length.
#[cfg(any(feature = "http1", feature = "http2"))]
pub(crate) fn checked_new(len: u64) -> Result<Self, crate::error::Parse> {
if len <= MAX_LEN {
Ok(DecodedLength(len))
} else {
warn!("content-length bigger than maximum: {} > {}", len, MAX_LEN);
Err(crate::error::Parse::TooLarge)
}
}
pub(crate) fn sub_if(&mut self, amt: u64) {
match *self {
DecodedLength::CHUNKED | DecodedLength::CLOSE_DELIMITED => (),
DecodedLength(ref mut known) => {
*known -= amt;
}
}
}
}
impl fmt::Debug for DecodedLength {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
DecodedLength::CLOSE_DELIMITED => f.write_str("CLOSE_DELIMITED"),
DecodedLength::CHUNKED => f.write_str("CHUNKED"),
DecodedLength(n) => f.debug_tuple("DecodedLength").field(&n).finish(),
}
}
}
impl fmt::Display for DecodedLength {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
DecodedLength::CLOSE_DELIMITED => f.write_str("close-delimited"),
DecodedLength::CHUNKED => f.write_str("chunked encoding"),
DecodedLength::ZERO => f.write_str("empty"),
DecodedLength(n) => write!(f, "content-length ({} bytes)", n),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn sub_if_known() {
let mut len = DecodedLength::new(30);
len.sub_if(20);
assert_eq!(len.0, 10);
}
#[test]
fn sub_if_chunked() {
let mut len = DecodedLength::CHUNKED;
len.sub_if(20);
assert_eq!(len, DecodedLength::CHUNKED);
}
}

View File

@@ -20,15 +20,18 @@ pub use http_body::Body as HttpBody;
pub use self::aggregate::aggregate; pub use self::aggregate::aggregate;
pub use self::body::{Body, Sender}; pub use self::body::{Body, Sender};
pub(crate) use self::length::DecodedLength;
pub use self::to_bytes::to_bytes; pub use self::to_bytes::to_bytes;
mod aggregate; mod aggregate;
mod body; mod body;
mod length;
mod to_bytes; mod to_bytes;
/// An optimization to try to take a full body if immediately available. /// An optimization to try to take a full body if immediately available.
/// ///
/// This is currently limited to *only* `hyper::Body`s. /// This is currently limited to *only* `hyper::Body`s.
#[cfg(feature = "http1")]
pub(crate) fn take_full_data<T: HttpBody + 'static>(body: &mut T) -> Option<T::Data> { pub(crate) fn take_full_data<T: HttpBody + 'static>(body: &mut T) -> Option<T::Data> {
use std::any::{Any, TypeId}; use std::any::{Any, TypeId};

View File

@@ -1,9 +1,37 @@
macro_rules! cfg_http2 { macro_rules! cfg_any_http {
($($item:item)*) => { ($($item:item)*) => {
$( $(
#[cfg(feature = "http2")] #[cfg(any(
//#[cfg_attr(docsrs, doc(cfg(feature = "http2")))] feature = "http1",
feature = "http2",
))]
#[cfg_attr(docsrs, doc(cfg(any(
feature = "http1",
feature = "http2",
))))]
$item $item
)* )*
} }
} }
cfg_any_http! {
macro_rules! cfg_http1 {
($($item:item)*) => {
$(
#[cfg(feature = "http1")]
#[cfg_attr(docsrs, doc(cfg(feature = "http1")))]
$item
)*
}
}
macro_rules! cfg_http2 {
($($item:item)*) => {
$(
#[cfg(feature = "http2")]
#[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
$item
)*
}
}
}

View File

@@ -10,7 +10,8 @@
use std::error::Error as StdError; use std::error::Error as StdError;
use std::fmt; use std::fmt;
use std::mem; #[cfg(feature = "http2")]
use std::marker::PhantomData;
use std::sync::Arc; use std::sync::Arc;
#[cfg(feature = "runtime")] #[cfg(feature = "runtime")]
#[cfg(feature = "http2")] #[cfg(feature = "http2")]
@@ -24,11 +25,14 @@ use tower_service::Service;
use super::dispatch; use super::dispatch;
use crate::body::HttpBody; use crate::body::HttpBody;
use crate::common::{task, BoxSendFuture, Exec, Executor, Future, Pin, Poll}; use crate::common::{task, BoxSendFuture, Exec, Future, Pin, Poll};
use crate::proto; use crate::proto;
use crate::rt::Executor;
#[cfg(feature = "http1")]
use crate::upgrade::Upgraded; use crate::upgrade::Upgraded;
use crate::{Body, Request, Response}; use crate::{Body, Request, Response};
#[cfg(feature = "http1")]
type Http1Dispatcher<T, B, R> = proto::dispatch::Dispatcher<proto::dispatch::Client<B>, B, T, R>; type Http1Dispatcher<T, B, R> = proto::dispatch::Dispatcher<proto::dispatch::Client<B>, B, T, R>;
#[pin_project(project = ProtoClientProj)] #[pin_project(project = ProtoClientProj)]
@@ -36,9 +40,10 @@ enum ProtoClient<T, B>
where where
B: HttpBody, B: HttpBody,
{ {
#[cfg(feature = "http1")]
H1(#[pin] Http1Dispatcher<T, B, proto::h1::ClientTransaction>), H1(#[pin] Http1Dispatcher<T, B, proto::h1::ClientTransaction>),
#[cfg(feature = "http2")] #[cfg(feature = "http2")]
H2(#[pin] proto::h2::ClientTask<B>), H2(#[pin] proto::h2::ClientTask<B>, PhantomData<fn(T)>),
} }
/// Returns a handshake future over some IO. /// Returns a handshake future over some IO.
@@ -88,6 +93,7 @@ pub struct Builder {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
enum Proto { enum Proto {
#[cfg(feature = "http1")]
Http1, Http1,
#[cfg(feature = "http2")] #[cfg(feature = "http2")]
Http2, Http2,
@@ -353,20 +359,22 @@ where
/// ///
/// Only works for HTTP/1 connections. HTTP/2 connections will panic. /// Only works for HTTP/1 connections. HTTP/2 connections will panic.
pub fn into_parts(self) -> Parts<T> { pub fn into_parts(self) -> Parts<T> {
let (io, read_buf, _) = match self.inner.expect("already upgraded") { match self.inner.expect("already upgraded") {
ProtoClient::H1(h1) => h1.into_inner(), #[cfg(feature = "http1")]
#[cfg(feature = "http2")] ProtoClient::H1(h1) => {
ProtoClient::H2(_h2) => { let (io, read_buf, _) = h1.into_inner();
panic!("http2 cannot into_inner");
}
};
Parts { Parts {
io, io,
read_buf, read_buf,
_inner: (), _inner: (),
} }
} }
#[cfg(feature = "http2")]
ProtoClient::H2(..) => {
panic!("http2 cannot into_inner");
}
}
}
/// Poll the connection for completion, but without calling `shutdown` /// Poll the connection for completion, but without calling `shutdown`
/// on the underlying IO. /// on the underlying IO.
@@ -381,9 +389,10 @@ where
/// to work with this function; or use the `without_shutdown` wrapper. /// to work with this function; or use the `without_shutdown` wrapper.
pub fn poll_without_shutdown(&mut self, cx: &mut task::Context<'_>) -> Poll<crate::Result<()>> { pub fn poll_without_shutdown(&mut self, cx: &mut task::Context<'_>) -> Poll<crate::Result<()>> {
match *self.inner.as_mut().expect("already upgraded") { match *self.inner.as_mut().expect("already upgraded") {
#[cfg(feature = "http1")]
ProtoClient::H1(ref mut h1) => h1.poll_without_shutdown(cx), ProtoClient::H1(ref mut h1) => h1.poll_without_shutdown(cx),
#[cfg(feature = "http2")] #[cfg(feature = "http2")]
ProtoClient::H2(ref mut h2) => Pin::new(h2).poll(cx).map_ok(|_| ()), ProtoClient::H2(ref mut h2, _) => Pin::new(h2).poll(cx).map_ok(|_| ()),
} }
} }
@@ -410,16 +419,18 @@ where
fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> { fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
match ready!(Pin::new(self.inner.as_mut().unwrap()).poll(cx))? { match ready!(Pin::new(self.inner.as_mut().unwrap()).poll(cx))? {
proto::Dispatched::Shutdown => Poll::Ready(Ok(())), proto::Dispatched::Shutdown => Poll::Ready(Ok(())),
proto::Dispatched::Upgrade(pending) => { #[cfg(feature = "http1")]
let h1 = match mem::replace(&mut self.inner, None) { proto::Dispatched::Upgrade(pending) => match self.inner.take() {
Some(ProtoClient::H1(h1)) => h1, Some(ProtoClient::H1(h1)) => {
_ => unreachable!("Upgrade expects h1"),
};
let (io, buf, _) = h1.into_inner(); let (io, buf, _) = h1.into_inner();
pending.fulfill(Upgraded::new(io, buf)); pending.fulfill(Upgraded::new(io, buf));
Poll::Ready(Ok(())) Poll::Ready(Ok(()))
} }
_ => {
drop(pending);
unreachable!("Upgrade expects h1");
}
},
} }
} }
} }
@@ -448,7 +459,10 @@ impl Builder {
h1_max_buf_size: None, h1_max_buf_size: None,
#[cfg(feature = "http2")] #[cfg(feature = "http2")]
h2_builder: Default::default(), h2_builder: Default::default(),
#[cfg(feature = "http1")]
version: Proto::Http1, version: Proto::Http1,
#[cfg(not(feature = "http1"))]
version: Proto::Http2,
} }
} }
@@ -477,6 +491,7 @@ impl Builder {
self self
} }
#[cfg(feature = "http1")]
pub(super) fn h1_max_buf_size(&mut self, max: usize) -> &mut Self { pub(super) fn h1_max_buf_size(&mut self, max: usize) -> &mut Self {
assert!( assert!(
max >= proto::h1::MINIMUM_MAX_BUFFER_SIZE, max >= proto::h1::MINIMUM_MAX_BUFFER_SIZE,
@@ -494,7 +509,9 @@ impl Builder {
#[cfg(feature = "http2")] #[cfg(feature = "http2")]
#[cfg_attr(docsrs, doc(cfg(feature = "http2")))] #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
pub fn http2_only(&mut self, enabled: bool) -> &mut Builder { pub fn http2_only(&mut self, enabled: bool) -> &mut Builder {
self.version = if enabled { Proto::Http2 } else { Proto::Http1 }; if enabled {
self.version = Proto::Http2
}
self self
} }
@@ -643,6 +660,7 @@ impl Builder {
let (tx, rx) = dispatch::channel(); let (tx, rx) = dispatch::channel();
let proto = match opts.version { let proto = match opts.version {
#[cfg(feature = "http1")]
Proto::Http1 => { Proto::Http1 => {
let mut conn = proto::Conn::new(io); let mut conn = proto::Conn::new(io);
if let Some(writev) = opts.h1_writev { if let Some(writev) = opts.h1_writev {
@@ -670,7 +688,7 @@ impl Builder {
let h2 = let h2 =
proto::h2::client::handshake(io, rx, &opts.h2_builder, opts.exec.clone()) proto::h2::client::handshake(io, rx, &opts.h2_builder, opts.exec.clone())
.await?; .await?;
ProtoClient::H2(h2) ProtoClient::H2(h2, PhantomData)
} }
}; };
@@ -723,9 +741,10 @@ where
fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> { fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
match self.project() { match self.project() {
#[cfg(feature = "http1")]
ProtoClientProj::H1(c) => c.poll(cx), ProtoClientProj::H1(c) => c.poll(cx),
#[cfg(feature = "http2")] #[cfg(feature = "http2")]
ProtoClientProj::H2(c) => c.poll(cx), ProtoClientProj::H2(c, _) => c.poll(cx),
} }
} }
} }

View File

@@ -162,11 +162,13 @@ impl<T, U> Receiver<T, U> {
} }
} }
#[cfg(feature = "http1")]
pub(crate) fn close(&mut self) { pub(crate) fn close(&mut self) {
self.taker.cancel(); self.taker.cancel();
self.inner.close(); self.inner.close();
} }
#[cfg(feature = "http1")]
pub(crate) fn try_recv(&mut self) -> Option<(T, Callback<T, U>)> { pub(crate) fn try_recv(&mut self) -> Option<(T, Callback<T, U>)> {
match self.inner.try_recv() { match self.inner.try_recv() {
Ok(mut env) => env.0.take(), Ok(mut env) => env.0.take(),

View File

@@ -62,7 +62,8 @@ use http::{Method, Request, Response, Uri, Version};
use self::connect::{sealed::Connect, Alpn, Connected, Connection}; use self::connect::{sealed::Connect, Alpn, Connected, Connection};
use self::pool::{Key as PoolKey, Pool, Poolable, Pooled, Reservation}; use self::pool::{Key as PoolKey, Pool, Poolable, Pooled, Reservation};
use crate::body::{Body, HttpBody}; use crate::body::{Body, HttpBody};
use crate::common::{lazy as hyper_lazy, task, BoxSendFuture, Executor, Future, Lazy, Pin, Poll}; use crate::common::{lazy as hyper_lazy, task, BoxSendFuture, Future, Lazy, Pin, Poll};
use crate::rt::Executor;
#[cfg(feature = "tcp")] #[cfg(feature = "tcp")]
pub use self::connect::HttpConnector; pub use self::connect::HttpConnector;
@@ -1022,6 +1023,8 @@ impl Builder {
/// # Panics /// # Panics
/// ///
/// The minimum value allowed is 8192. This method panics if the passed `max` is less than the minimum. /// The minimum value allowed is 8192. This method panics if the passed `max` is less than the minimum.
#[cfg(feature = "http1")]
#[cfg_attr(docsrs, doc(cfg(feature = "http1")))]
pub fn http1_max_buf_size(&mut self, max: usize) -> &mut Self { pub fn http1_max_buf_size(&mut self, max: usize) -> &mut Self {
self.conn_builder.h1_max_buf_size(max); self.conn_builder.h1_max_buf_size(max);
self self

View File

@@ -21,6 +21,7 @@ impl<T: Buf> BufList<T> {
} }
#[inline] #[inline]
#[cfg(feature = "http1")]
pub(crate) fn bufs_cnt(&self) -> usize { pub(crate) fn bufs_cnt(&self) -> usize {
self.bufs.len() self.bufs.len()
} }

View File

@@ -10,12 +10,14 @@ use httpdate::HttpDate;
// "Sun, 06 Nov 1994 08:49:37 GMT".len() // "Sun, 06 Nov 1994 08:49:37 GMT".len()
pub const DATE_VALUE_LENGTH: usize = 29; pub const DATE_VALUE_LENGTH: usize = 29;
#[cfg(feature = "http1")]
pub fn extend(dst: &mut Vec<u8>) { pub fn extend(dst: &mut Vec<u8>) {
CACHED.with(|cache| { CACHED.with(|cache| {
dst.extend_from_slice(cache.borrow().buffer()); dst.extend_from_slice(cache.borrow().buffer());
}) })
} }
#[cfg(feature = "http1")]
pub fn update() { pub fn update() {
CACHED.with(|cache| { CACHED.with(|cache| {
cache.borrow_mut().check(); cache.borrow_mut().check();

View File

@@ -6,15 +6,10 @@ use std::sync::Arc;
use crate::body::{Body, HttpBody}; use crate::body::{Body, HttpBody};
#[cfg(feature = "http2")] #[cfg(feature = "http2")]
use crate::proto::h2::server::H2Stream; use crate::proto::h2::server::H2Stream;
use crate::rt::Executor;
use crate::server::conn::spawn_all::{NewSvcTask, Watcher}; use crate::server::conn::spawn_all::{NewSvcTask, Watcher};
use crate::service::HttpService; use crate::service::HttpService;
/// An executor of futures.
pub trait Executor<Fut> {
/// Place the future into the executor to be run.
fn execute(&self, fut: Fut);
}
pub trait ConnStreamExec<F, B: HttpBody>: Clone { pub trait ConnStreamExec<F, B: HttpBody>: Clone {
fn execute_h2stream(&mut self, fut: H2Stream<F, B>); fn execute_h2stream(&mut self, fut: H2Stream<F, B>);
} }

View File

@@ -29,7 +29,7 @@ impl<T> Rewind<T> {
} }
} }
#[cfg(any(feature = "http2", test))] #[cfg(any(all(feature = "http1", feature = "http2"), test))]
pub(crate) fn rewind(&mut self, bs: Bytes) { pub(crate) fn rewind(&mut self, bs: Bytes) {
debug_assert!(self.pre.is_none()); debug_assert!(self.pre.is_none());
self.pre = Some(bs); self.pre = Some(bs);

View File

@@ -8,9 +8,14 @@ macro_rules! ready {
} }
pub(crate) mod buf; pub(crate) mod buf;
#[cfg(any(feature = "http1", feature = "http2"))]
pub(crate) mod date;
#[cfg(any(feature = "http1", feature = "http2"))]
pub(crate) mod drain; pub(crate) mod drain;
#[cfg(any(feature = "http1", feature = "http2"))]
pub(crate) mod exec; pub(crate) mod exec;
pub(crate) mod io; pub(crate) mod io;
#[cfg(any(feature = "http1", feature = "http2"))]
mod lazy; mod lazy;
mod never; mod never;
#[cfg(feature = "stream")] #[cfg(feature = "stream")]
@@ -18,11 +23,14 @@ pub(crate) mod sync_wrapper;
pub(crate) mod task; pub(crate) mod task;
pub(crate) mod watch; pub(crate) mod watch;
pub use self::exec::Executor; #[cfg(any(feature = "http1", feature = "http2"))]
pub(crate) use self::exec::{BoxSendFuture, Exec}; pub(crate) use self::exec::{BoxSendFuture, Exec};
#[cfg(any(feature = "http1", feature = "http2"))]
pub(crate) use self::lazy::{lazy, Started as Lazy}; pub(crate) use self::lazy::{lazy, Started as Lazy};
pub use self::never::Never; pub use self::never::Never;
pub(crate) use self::task::Poll; pub(crate) use self::task::Poll;
// group up types normally needed for `Future` // group up types normally needed for `Future`
pub(crate) use std::{future::Future, marker::Unpin, pin::Pin}; #[cfg(any(feature = "http1", feature = "http2"))]
pub(crate) use std::marker::Unpin;
pub(crate) use std::{future::Future, pin::Pin};

View File

@@ -1,9 +1,11 @@
#[cfg(feature = "http1")]
use super::Never; use super::Never;
pub(crate) use std::task::{Context, Poll}; pub(crate) use std::task::{Context, Poll};
/// A function to help "yield" a future, such that it is re-scheduled immediately. /// A function to help "yield" a future, such that it is re-scheduled immediately.
/// ///
/// Useful for spin counts, so a future doesn't hog too much time. /// Useful for spin counts, so a future doesn't hog too much time.
#[cfg(feature = "http1")]
pub(crate) fn yield_now(cx: &mut Context<'_>) -> Poll<Never> { pub(crate) fn yield_now(cx: &mut Context<'_>) -> Poll<Never> {
cx.waker().wake_by_ref(); cx.waker().wake_by_ref();
Poll::Pending Poll::Pending

View File

@@ -1,7 +1,6 @@
//! Error and Result module. //! Error and Result module.
use std::error::Error as StdError; use std::error::Error as StdError;
use std::fmt; use std::fmt;
use std::io;
/// Result type often returned from methods that can have hyper `Error`s. /// Result type often returned from methods that can have hyper `Error`s.
pub type Result<T> = std::result::Result<T, Error>; pub type Result<T> = std::result::Result<T, Error>;
@@ -25,27 +24,33 @@ pub(crate) enum Kind {
/// A message reached EOF, but is not complete. /// A message reached EOF, but is not complete.
IncompleteMessage, IncompleteMessage,
/// A connection received a message (or bytes) when not waiting for one. /// A connection received a message (or bytes) when not waiting for one.
#[cfg(feature = "http1")]
UnexpectedMessage, UnexpectedMessage,
/// A pending item was dropped before ever being processed. /// A pending item was dropped before ever being processed.
Canceled, Canceled,
/// Indicates a channel (client or body sender) is closed. /// Indicates a channel (client or body sender) is closed.
ChannelClosed, ChannelClosed,
/// An `io::Error` that occurred while trying to read or write to a network stream. /// An `io::Error` that occurred while trying to read or write to a network stream.
#[cfg(any(feature = "http1", feature = "http2"))]
Io, Io,
/// Error occurred while connecting. /// Error occurred while connecting.
Connect, Connect,
/// Error creating a TcpListener. /// Error creating a TcpListener.
#[cfg(feature = "tcp")] #[cfg(all(any(feature = "http1", feature = "http2"), feature = "tcp"))]
Listen, Listen,
/// Error accepting on an Incoming stream. /// Error accepting on an Incoming stream.
#[cfg(any(feature = "http1", feature = "http2"))]
Accept, Accept,
/// Error while reading a body from connection. /// Error while reading a body from connection.
#[cfg(any(feature = "http1", feature = "http2"))]
Body, Body,
/// Error while writing a body to connection. /// Error while writing a body to connection.
#[cfg(any(feature = "http1", feature = "http2"))]
BodyWrite, BodyWrite,
/// The body write was aborted. /// The body write was aborted.
BodyWriteAborted, BodyWriteAborted,
/// Error calling AsyncWrite::shutdown() /// Error calling AsyncWrite::shutdown()
#[cfg(feature = "http1")]
Shutdown, Shutdown,
/// A general error from h2. /// A general error from h2.
@@ -57,6 +62,7 @@ pub(crate) enum Kind {
pub(crate) enum Parse { pub(crate) enum Parse {
Method, Method,
Version, Version,
#[cfg(feature = "http1")]
VersionH2, VersionH2,
Uri, Uri,
Header, Header,
@@ -67,28 +73,37 @@ pub(crate) enum Parse {
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub(crate) enum User { pub(crate) enum User {
/// Error calling user's HttpBody::poll_data(). /// Error calling user's HttpBody::poll_data().
#[cfg(any(feature = "http1", feature = "http2"))]
Body, Body,
/// Error calling user's MakeService. /// Error calling user's MakeService.
#[cfg(any(feature = "http1", feature = "http2"))]
MakeService, MakeService,
/// Error from future of user's Service. /// Error from future of user's Service.
#[cfg(any(feature = "http1", feature = "http2"))]
Service, Service,
/// User tried to send a certain header in an unexpected context. /// User tried to send a certain header in an unexpected context.
/// ///
/// For example, sending both `content-length` and `transfer-encoding`. /// For example, sending both `content-length` and `transfer-encoding`.
#[cfg(feature = "http1")]
UnexpectedHeader, UnexpectedHeader,
/// User tried to create a Request with bad version. /// User tried to create a Request with bad version.
#[cfg(any(feature = "http1", feature = "http2"))]
UnsupportedVersion, UnsupportedVersion,
/// User tried to create a CONNECT Request with the Client. /// User tried to create a CONNECT Request with the Client.
#[cfg(any(feature = "http1", feature = "http2"))]
UnsupportedRequestMethod, UnsupportedRequestMethod,
/// User tried to respond with a 1xx (not 101) response code. /// User tried to respond with a 1xx (not 101) response code.
#[cfg(feature = "http1")]
UnsupportedStatusCode, UnsupportedStatusCode,
/// User tried to send a Request with Client with non-absolute URI. /// User tried to send a Request with Client with non-absolute URI.
#[cfg(any(feature = "http1", feature = "http2"))]
AbsoluteUriRequired, AbsoluteUriRequired,
/// User tried polling for an upgrade that doesn't exist. /// User tried polling for an upgrade that doesn't exist.
NoUpgrade, NoUpgrade,
/// User polled for an upgrade, but low-level API is not using upgrades. /// User polled for an upgrade, but low-level API is not using upgrades.
#[cfg(feature = "http1")]
ManualUpgrade, ManualUpgrade,
} }
@@ -159,6 +174,7 @@ impl Error {
self self
} }
#[cfg(feature = "http1")]
pub(crate) fn kind(&self) -> &Kind { pub(crate) fn kind(&self) -> &Kind {
&self.inner.kind &self.inner.kind
} }
@@ -189,35 +205,42 @@ impl Error {
Error::new(Kind::Canceled) Error::new(Kind::Canceled)
} }
#[cfg(feature = "http1")]
pub(crate) fn new_incomplete() -> Error { pub(crate) fn new_incomplete() -> Error {
Error::new(Kind::IncompleteMessage) Error::new(Kind::IncompleteMessage)
} }
#[cfg(feature = "http1")]
pub(crate) fn new_too_large() -> Error { pub(crate) fn new_too_large() -> Error {
Error::new(Kind::Parse(Parse::TooLarge)) Error::new(Kind::Parse(Parse::TooLarge))
} }
#[cfg(feature = "http1")]
pub(crate) fn new_version_h2() -> Error { pub(crate) fn new_version_h2() -> Error {
Error::new(Kind::Parse(Parse::VersionH2)) Error::new(Kind::Parse(Parse::VersionH2))
} }
#[cfg(feature = "http1")]
pub(crate) fn new_unexpected_message() -> Error { pub(crate) fn new_unexpected_message() -> Error {
Error::new(Kind::UnexpectedMessage) Error::new(Kind::UnexpectedMessage)
} }
pub(crate) fn new_io(cause: io::Error) -> Error { #[cfg(feature = "http1")]
pub(crate) fn new_io(cause: std::io::Error) -> Error {
Error::new(Kind::Io).with(cause) Error::new(Kind::Io).with(cause)
} }
#[cfg(feature = "tcp")] #[cfg(all(any(feature = "http1", feature = "http2"), feature = "tcp"))]
pub(crate) fn new_listen<E: Into<Cause>>(cause: E) -> Error { pub(crate) fn new_listen<E: Into<Cause>>(cause: E) -> Error {
Error::new(Kind::Listen).with(cause) Error::new(Kind::Listen).with(cause)
} }
#[cfg(any(feature = "http1", feature = "http2"))]
pub(crate) fn new_accept<E: Into<Cause>>(cause: E) -> Error { pub(crate) fn new_accept<E: Into<Cause>>(cause: E) -> Error {
Error::new(Kind::Accept).with(cause) Error::new(Kind::Accept).with(cause)
} }
#[cfg(any(feature = "http1", feature = "http2"))]
pub(crate) fn new_connect<E: Into<Cause>>(cause: E) -> Error { pub(crate) fn new_connect<E: Into<Cause>>(cause: E) -> Error {
Error::new(Kind::Connect).with(cause) Error::new(Kind::Connect).with(cause)
} }
@@ -226,10 +249,12 @@ impl Error {
Error::new(Kind::ChannelClosed) Error::new(Kind::ChannelClosed)
} }
#[cfg(any(feature = "http1", feature = "http2"))]
pub(crate) fn new_body<E: Into<Cause>>(cause: E) -> Error { pub(crate) fn new_body<E: Into<Cause>>(cause: E) -> Error {
Error::new(Kind::Body).with(cause) Error::new(Kind::Body).with(cause)
} }
#[cfg(any(feature = "http1", feature = "http2"))]
pub(crate) fn new_body_write<E: Into<Cause>>(cause: E) -> Error { pub(crate) fn new_body_write<E: Into<Cause>>(cause: E) -> Error {
Error::new(Kind::BodyWrite).with(cause) Error::new(Kind::BodyWrite).with(cause)
} }
@@ -242,22 +267,27 @@ impl Error {
Error::new(Kind::User(user)) Error::new(Kind::User(user))
} }
#[cfg(feature = "http1")]
pub(crate) fn new_user_header() -> Error { pub(crate) fn new_user_header() -> Error {
Error::new_user(User::UnexpectedHeader) Error::new_user(User::UnexpectedHeader)
} }
#[cfg(any(feature = "http1", feature = "http2"))]
pub(crate) fn new_user_unsupported_version() -> Error { pub(crate) fn new_user_unsupported_version() -> Error {
Error::new_user(User::UnsupportedVersion) Error::new_user(User::UnsupportedVersion)
} }
#[cfg(any(feature = "http1", feature = "http2"))]
pub(crate) fn new_user_unsupported_request_method() -> Error { pub(crate) fn new_user_unsupported_request_method() -> Error {
Error::new_user(User::UnsupportedRequestMethod) Error::new_user(User::UnsupportedRequestMethod)
} }
#[cfg(feature = "http1")]
pub(crate) fn new_user_unsupported_status_code() -> Error { pub(crate) fn new_user_unsupported_status_code() -> Error {
Error::new_user(User::UnsupportedStatusCode) Error::new_user(User::UnsupportedStatusCode)
} }
#[cfg(feature = "http1")]
pub(crate) fn new_user_absolute_uri_required() -> Error { pub(crate) fn new_user_absolute_uri_required() -> Error {
Error::new_user(User::AbsoluteUriRequired) Error::new_user(User::AbsoluteUriRequired)
} }
@@ -266,23 +296,28 @@ impl Error {
Error::new_user(User::NoUpgrade) Error::new_user(User::NoUpgrade)
} }
#[cfg(feature = "http1")]
pub(crate) fn new_user_manual_upgrade() -> Error { pub(crate) fn new_user_manual_upgrade() -> Error {
Error::new_user(User::ManualUpgrade) Error::new_user(User::ManualUpgrade)
} }
#[cfg(any(feature = "http1", feature = "http2"))]
pub(crate) fn new_user_make_service<E: Into<Cause>>(cause: E) -> Error { pub(crate) fn new_user_make_service<E: Into<Cause>>(cause: E) -> Error {
Error::new_user(User::MakeService).with(cause) Error::new_user(User::MakeService).with(cause)
} }
#[cfg(any(feature = "http1", feature = "http2"))]
pub(crate) fn new_user_service<E: Into<Cause>>(cause: E) -> Error { pub(crate) fn new_user_service<E: Into<Cause>>(cause: E) -> Error {
Error::new_user(User::Service).with(cause) Error::new_user(User::Service).with(cause)
} }
#[cfg(any(feature = "http1", feature = "http2"))]
pub(crate) fn new_user_body<E: Into<Cause>>(cause: E) -> Error { pub(crate) fn new_user_body<E: Into<Cause>>(cause: E) -> Error {
Error::new_user(User::Body).with(cause) Error::new_user(User::Body).with(cause)
} }
pub(crate) fn new_shutdown(cause: io::Error) -> Error { #[cfg(feature = "http1")]
pub(crate) fn new_shutdown(cause: std::io::Error) -> Error {
Error::new(Kind::Shutdown).with(cause) Error::new(Kind::Shutdown).with(cause)
} }
@@ -299,38 +334,54 @@ impl Error {
match self.inner.kind { match self.inner.kind {
Kind::Parse(Parse::Method) => "invalid HTTP method parsed", Kind::Parse(Parse::Method) => "invalid HTTP method parsed",
Kind::Parse(Parse::Version) => "invalid HTTP version parsed", Kind::Parse(Parse::Version) => "invalid HTTP version parsed",
#[cfg(feature = "http1")]
Kind::Parse(Parse::VersionH2) => "invalid HTTP version parsed (found HTTP2 preface)", Kind::Parse(Parse::VersionH2) => "invalid HTTP version parsed (found HTTP2 preface)",
Kind::Parse(Parse::Uri) => "invalid URI", Kind::Parse(Parse::Uri) => "invalid URI",
Kind::Parse(Parse::Header) => "invalid HTTP header parsed", Kind::Parse(Parse::Header) => "invalid HTTP header parsed",
Kind::Parse(Parse::TooLarge) => "message head is too large", Kind::Parse(Parse::TooLarge) => "message head is too large",
Kind::Parse(Parse::Status) => "invalid HTTP status-code parsed", Kind::Parse(Parse::Status) => "invalid HTTP status-code parsed",
Kind::IncompleteMessage => "connection closed before message completed", Kind::IncompleteMessage => "connection closed before message completed",
#[cfg(feature = "http1")]
Kind::UnexpectedMessage => "received unexpected message from connection", Kind::UnexpectedMessage => "received unexpected message from connection",
Kind::ChannelClosed => "channel closed", Kind::ChannelClosed => "channel closed",
Kind::Connect => "error trying to connect", Kind::Connect => "error trying to connect",
Kind::Canceled => "operation was canceled", Kind::Canceled => "operation was canceled",
#[cfg(feature = "tcp")] #[cfg(all(any(feature = "http1", feature = "http2"), feature = "tcp"))]
Kind::Listen => "error creating server listener", Kind::Listen => "error creating server listener",
#[cfg(any(feature = "http1", feature = "http2"))]
Kind::Accept => "error accepting connection", Kind::Accept => "error accepting connection",
#[cfg(any(feature = "http1", feature = "http2"))]
Kind::Body => "error reading a body from connection", Kind::Body => "error reading a body from connection",
#[cfg(any(feature = "http1", feature = "http2"))]
Kind::BodyWrite => "error writing a body to connection", Kind::BodyWrite => "error writing a body to connection",
Kind::BodyWriteAborted => "body write aborted", Kind::BodyWriteAborted => "body write aborted",
#[cfg(feature = "http1")]
Kind::Shutdown => "error shutting down connection", Kind::Shutdown => "error shutting down connection",
#[cfg(feature = "http2")] #[cfg(feature = "http2")]
Kind::Http2 => "http2 error", Kind::Http2 => "http2 error",
#[cfg(any(feature = "http1", feature = "http2"))]
Kind::Io => "connection error", Kind::Io => "connection error",
#[cfg(any(feature = "http1", feature = "http2"))]
Kind::User(User::Body) => "error from user's HttpBody stream", Kind::User(User::Body) => "error from user's HttpBody stream",
#[cfg(any(feature = "http1", feature = "http2"))]
Kind::User(User::MakeService) => "error from user's MakeService", Kind::User(User::MakeService) => "error from user's MakeService",
#[cfg(any(feature = "http1", feature = "http2"))]
Kind::User(User::Service) => "error from user's Service", Kind::User(User::Service) => "error from user's Service",
#[cfg(feature = "http1")]
Kind::User(User::UnexpectedHeader) => "user sent unexpected header", Kind::User(User::UnexpectedHeader) => "user sent unexpected header",
#[cfg(any(feature = "http1", feature = "http2"))]
Kind::User(User::UnsupportedVersion) => "request has unsupported HTTP version", Kind::User(User::UnsupportedVersion) => "request has unsupported HTTP version",
#[cfg(any(feature = "http1", feature = "http2"))]
Kind::User(User::UnsupportedRequestMethod) => "request has unsupported HTTP method", Kind::User(User::UnsupportedRequestMethod) => "request has unsupported HTTP method",
#[cfg(feature = "http1")]
Kind::User(User::UnsupportedStatusCode) => { Kind::User(User::UnsupportedStatusCode) => {
"response has 1xx status code, not supported by server" "response has 1xx status code, not supported by server"
} }
#[cfg(any(feature = "http1", feature = "http2"))]
Kind::User(User::AbsoluteUriRequired) => "client requires absolute-form URIs", Kind::User(User::AbsoluteUriRequired) => "client requires absolute-form URIs",
Kind::User(User::NoUpgrade) => "no upgrade available", Kind::User(User::NoUpgrade) => "no upgrade available",
#[cfg(feature = "http1")]
Kind::User(User::ManualUpgrade) => "upgrade expected but low level API in use", Kind::User(User::ManualUpgrade) => "upgrade expected but low level API in use",
} }
} }

View File

@@ -1,18 +1,22 @@
#[cfg(feature = "http1")]
use bytes::BytesMut; use bytes::BytesMut;
use http::header::{HeaderValue, OccupiedEntry, ValueIter}; use http::header::CONTENT_LENGTH;
use http::header::{CONTENT_LENGTH, TRANSFER_ENCODING}; use http::header::{HeaderValue, ValueIter};
#[cfg(feature = "http2")] #[cfg(feature = "http2")]
use http::method::Method; use http::method::Method;
use http::HeaderMap; use http::HeaderMap;
#[cfg(feature = "http1")]
pub fn connection_keep_alive(value: &HeaderValue) -> bool { pub fn connection_keep_alive(value: &HeaderValue) -> bool {
connection_has(value, "keep-alive") connection_has(value, "keep-alive")
} }
#[cfg(feature = "http1")]
pub fn connection_close(value: &HeaderValue) -> bool { pub fn connection_close(value: &HeaderValue) -> bool {
connection_has(value, "close") connection_has(value, "close")
} }
#[cfg(feature = "http1")]
fn connection_has(value: &HeaderValue, needle: &str) -> bool { fn connection_has(value: &HeaderValue, needle: &str) -> bool {
if let Ok(s) = value.to_str() { if let Ok(s) = value.to_str() {
for val in s.split(',') { for val in s.split(',') {
@@ -24,6 +28,7 @@ fn connection_has(value: &HeaderValue, needle: &str) -> bool {
false false
} }
#[cfg(feature = "http1")]
pub fn content_length_parse(value: &HeaderValue) -> Option<u64> { pub fn content_length_parse(value: &HeaderValue) -> Option<u64> {
value.to_str().ok().and_then(|s| s.parse().ok()) value.to_str().ok().and_then(|s| s.parse().ok())
} }
@@ -74,10 +79,12 @@ pub fn set_content_length_if_missing(headers: &mut HeaderMap, len: u64) {
.or_insert_with(|| HeaderValue::from(len)); .or_insert_with(|| HeaderValue::from(len));
} }
#[cfg(feature = "http1")]
pub fn transfer_encoding_is_chunked(headers: &HeaderMap) -> bool { pub fn transfer_encoding_is_chunked(headers: &HeaderMap) -> bool {
is_chunked(headers.get_all(TRANSFER_ENCODING).into_iter()) is_chunked(headers.get_all(http::header::TRANSFER_ENCODING).into_iter())
} }
#[cfg(feature = "http1")]
pub fn is_chunked(mut encodings: ValueIter<'_, HeaderValue>) -> bool { pub fn is_chunked(mut encodings: ValueIter<'_, HeaderValue>) -> bool {
// chunked must always be the last encoding, according to spec // chunked must always be the last encoding, according to spec
if let Some(line) = encodings.next_back() { if let Some(line) = encodings.next_back() {
@@ -87,6 +94,7 @@ pub fn is_chunked(mut encodings: ValueIter<'_, HeaderValue>) -> bool {
false false
} }
#[cfg(feature = "http1")]
pub fn is_chunked_(value: &HeaderValue) -> bool { pub fn is_chunked_(value: &HeaderValue) -> bool {
// chunked must always be the last encoding, according to spec // chunked must always be the last encoding, according to spec
if let Ok(s) = value.to_str() { if let Ok(s) = value.to_str() {
@@ -98,7 +106,8 @@ pub fn is_chunked_(value: &HeaderValue) -> bool {
false false
} }
pub fn add_chunked(mut entry: OccupiedEntry<'_, HeaderValue>) { #[cfg(feature = "http1")]
pub fn add_chunked(mut entry: http::header::OccupiedEntry<'_, HeaderValue>) {
const CHUNKED: &str = "chunked"; const CHUNKED: &str = "chunked";
if let Some(line) = entry.iter_mut().next_back() { if let Some(line) = entry.iter_mut().next_back() {

View File

@@ -42,6 +42,7 @@
#[doc(hidden)] #[doc(hidden)]
pub use http; pub use http;
#[cfg(any(feature = "http1", feature = "http2"))]
#[macro_use] #[macro_use]
extern crate tracing; extern crate tracing;
@@ -51,23 +52,28 @@ extern crate test;
pub use http::{header, HeaderMap, Method, Request, Response, StatusCode, Uri, Version}; pub use http::{header, HeaderMap, Method, Request, Response, StatusCode, Uri, Version};
pub use crate::body::Body; pub use crate::body::Body;
pub use crate::client::Client;
pub use crate::error::{Error, Result}; pub use crate::error::{Error, Result};
pub use crate::server::Server;
#[macro_use] #[macro_use]
mod cfg; mod cfg;
#[macro_use] #[macro_use]
mod common; mod common;
pub mod body; pub mod body;
pub mod client;
#[doc(hidden)] // Mistakenly public... #[doc(hidden)] // Mistakenly public...
pub mod error; pub mod error;
mod headers;
#[cfg(test)] #[cfg(test)]
mod mock; mod mock;
mod proto; #[cfg(any(feature = "http1", feature = "http2",))]
pub mod rt; pub mod rt;
pub mod server;
pub mod service; pub mod service;
pub mod upgrade; pub mod upgrade;
cfg_any_http! {
mod headers;
mod proto;
pub mod client;
pub mod server;
pub use crate::client::Client;
pub use crate::server::Server;
}

View File

@@ -9,10 +9,10 @@ use tokio::io::{AsyncRead, AsyncWrite};
use super::io::Buffered; use super::io::Buffered;
use super::{Decoder, Encode, EncodedBuf, Encoder, Http1Transaction, ParseContext, Wants}; use super::{Decoder, Encode, EncodedBuf, Encoder, Http1Transaction, ParseContext, Wants};
use crate::body::DecodedLength;
use crate::common::{task, Pin, Poll, Unpin}; use crate::common::{task, Pin, Poll, Unpin};
use crate::headers::connection_keep_alive; use crate::headers::connection_keep_alive;
use crate::proto::{BodyLength, DecodedLength, MessageHead}; use crate::proto::{BodyLength, MessageHead};
use crate::Result;
const H2_PREFACE: &[u8] = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"; const H2_PREFACE: &[u8] = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
@@ -589,7 +589,7 @@ where
self.state.writing = state; self.state.writing = state;
} }
pub fn end_body(&mut self) -> Result<()> { pub fn end_body(&mut self) -> crate::Result<()> {
debug_assert!(self.can_write_body()); debug_assert!(self.can_write_body());
let mut res = Ok(()); let mut res = Ok(());

View File

@@ -5,10 +5,10 @@ use http::{Request, Response, StatusCode};
use tokio::io::{AsyncRead, AsyncWrite}; use tokio::io::{AsyncRead, AsyncWrite};
use super::{Http1Transaction, Wants}; use super::{Http1Transaction, Wants};
use crate::body::{Body, HttpBody}; use crate::body::{Body, DecodedLength, HttpBody};
use crate::common::{task, Future, Never, Pin, Poll, Unpin}; use crate::common::{task, Future, Never, Pin, Poll, Unpin};
use crate::proto::{ use crate::proto::{
BodyLength, Conn, DecodedLength, Dispatched, MessageHead, RequestHead, RequestLine, BodyLength, Conn, Dispatched, MessageHead, RequestHead, RequestLine,
ResponseHead, ResponseHead,
}; };
use crate::service::HttpService; use crate::service::HttpService;

View File

@@ -2,13 +2,14 @@ use std::cell::Cell;
use std::cmp; use std::cmp;
use std::fmt; use std::fmt;
use std::io::{self, IoSlice}; use std::io::{self, IoSlice};
use std::marker::Unpin;
use bytes::{Buf, BufMut, Bytes, BytesMut}; use bytes::{Buf, BufMut, Bytes, BytesMut};
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
use super::{Http1Transaction, ParseContext, ParsedMessage}; use super::{Http1Transaction, ParseContext, ParsedMessage};
use crate::common::buf::BufList; use crate::common::buf::BufList;
use crate::common::{task, Pin, Poll, Unpin}; use crate::common::{task, Pin, Poll};
/// The initial buffer size allocated before trying to read from IO. /// The initial buffer size allocated before trying to read from IO.
pub(crate) const INIT_BUFFER_SIZE: usize = 8192; pub(crate) const INIT_BUFFER_SIZE: usize = 8192;

View File

@@ -1,7 +1,8 @@
use bytes::BytesMut; use bytes::BytesMut;
use http::{HeaderMap, Method}; use http::{HeaderMap, Method};
use crate::proto::{BodyLength, DecodedLength, MessageHead}; use crate::body::DecodedLength;
use crate::proto::{BodyLength, MessageHead};
pub(crate) use self::conn::Conn; pub(crate) use self::conn::Conn;
pub use self::decode::Decoder; pub use self::decode::Decoder;
@@ -11,7 +12,6 @@ pub use self::io::Cursor; //TODO: move out of h1::io
pub use self::io::MINIMUM_MAX_BUFFER_SIZE; pub use self::io::MINIMUM_MAX_BUFFER_SIZE;
mod conn; mod conn;
pub(super) mod date;
mod decode; mod decode;
pub(crate) mod dispatch; pub(crate) mod dispatch;
mod encode; mod encode;

View File

@@ -9,12 +9,14 @@ use bytes::BytesMut;
use http::header::{self, Entry, HeaderName, HeaderValue}; use http::header::{self, Entry, HeaderName, HeaderValue};
use http::{HeaderMap, Method, StatusCode, Version}; use http::{HeaderMap, Method, StatusCode, Version};
use crate::body::DecodedLength;
use crate::common::date;
use crate::error::Parse; use crate::error::Parse;
use crate::headers; use crate::headers;
use crate::proto::h1::{ use crate::proto::h1::{
date, Encode, Encoder, Http1Transaction, ParseContext, ParseResult, ParsedMessage, Encode, Encoder, Http1Transaction, ParseContext, ParseResult, ParsedMessage,
}; };
use crate::proto::{BodyLength, DecodedLength, MessageHead, RequestHead, RequestLine}; use crate::proto::{BodyLength, MessageHead, RequestHead, RequestLine};
const MAX_HEADERS: usize = 100; const MAX_HEADERS: usize = 100;
const AVERAGE_HEADER_SIZE: usize = 30; // totally scientific const AVERAGE_HEADER_SIZE: usize = 30; // totally scientific

View File

@@ -9,8 +9,7 @@ use pin_project::pin_project;
use std::error::Error as StdError; use std::error::Error as StdError;
use std::io::IoSlice; use std::io::IoSlice;
use super::DecodedLength; use crate::body::{DecodedLength, HttpBody};
use crate::body::HttpBody;
use crate::common::{task, Future, Pin, Poll}; use crate::common::{task, Future, Pin, Poll};
use crate::headers::content_length_parse_all; use crate::headers::content_length_parse_all;

View File

@@ -11,7 +11,7 @@ use tokio::io::{AsyncRead, AsyncWrite};
use super::{decode_content_length, ping, PipeToSendStream, SendBuf}; use super::{decode_content_length, ping, PipeToSendStream, SendBuf};
use crate::body::HttpBody; use crate::body::HttpBody;
use crate::common::exec::ConnStreamExec; use crate::common::exec::ConnStreamExec;
use crate::common::{task, Future, Pin, Poll}; use crate::common::{date, task, Future, Pin, Poll};
use crate::headers; use crate::headers;
use crate::proto::Dispatched; use crate::proto::Dispatched;
use crate::service::HttpService; use crate::service::HttpService;
@@ -400,7 +400,7 @@ where
// set Date header if it isn't already set... // set Date header if it isn't already set...
res.headers_mut() res.headers_mut()
.entry(::http::header::DATE) .entry(::http::header::DATE)
.or_insert_with(crate::proto::h1::date::update_and_header_value); .or_insert_with(date::update_and_header_value);
// automatically set Content-Length from body... // automatically set Content-Length from body...
if let Some(len) = body.size_hint().exact() { if let Some(len) = body.size_hint().exact() {

View File

@@ -1,10 +1,11 @@
//! Pieces pertaining to the HTTP message protocol. //! Pieces pertaining to the HTTP message protocol.
use http::{HeaderMap, Method, StatusCode, Uri, Version};
pub(crate) use self::body_length::DecodedLength;
pub(crate) use self::h1::{dispatch, Conn, ServerTransaction};
cfg_http1! {
pub(crate) mod h1; pub(crate) mod h1;
pub(crate) use self::h1::{dispatch, Conn, ServerTransaction};
}
cfg_http2! { cfg_http2! {
pub(crate) mod h2; pub(crate) mod h2;
} }
@@ -13,23 +14,27 @@ cfg_http2! {
#[derive(Clone, Debug, Default, PartialEq)] #[derive(Clone, Debug, Default, PartialEq)]
pub struct MessageHead<S> { pub struct MessageHead<S> {
/// HTTP version of the message. /// HTTP version of the message.
pub version: Version, pub version: http::Version,
/// Subject (request line or status line) of Incoming message. /// Subject (request line or status line) of Incoming message.
pub subject: S, pub subject: S,
/// Headers of the Incoming message. /// Headers of the Incoming message.
pub headers: HeaderMap, pub headers: http::HeaderMap,
} }
/// An incoming request message. /// An incoming request message.
#[cfg(feature = "http1")]
pub type RequestHead = MessageHead<RequestLine>; pub type RequestHead = MessageHead<RequestLine>;
#[derive(Debug, Default, PartialEq)] #[derive(Debug, Default, PartialEq)]
pub struct RequestLine(pub Method, pub Uri); #[cfg(feature = "http1")]
pub struct RequestLine(pub http::Method, pub http::Uri);
/// An incoming response message. /// An incoming response message.
pub type ResponseHead = MessageHead<StatusCode>; #[cfg(feature = "http1")]
pub type ResponseHead = MessageHead<http::StatusCode>;
#[derive(Debug)] #[derive(Debug)]
#[cfg(feature = "http1")]
pub enum BodyLength { pub enum BodyLength {
/// Content-Length /// Content-Length
Known(u64), Known(u64),
@@ -42,106 +47,6 @@ pub(crate) enum Dispatched {
/// Dispatcher completely shutdown connection. /// Dispatcher completely shutdown connection.
Shutdown, Shutdown,
/// Dispatcher has pending upgrade, and so did not shutdown. /// Dispatcher has pending upgrade, and so did not shutdown.
#[cfg(feature = "http1")]
Upgrade(crate::upgrade::Pending), Upgrade(crate::upgrade::Pending),
} }
/// A separate module to encapsulate the invariants of the DecodedLength type.
mod body_length {
use std::fmt;
#[derive(Clone, Copy, PartialEq, Eq)]
pub(crate) struct DecodedLength(u64);
const MAX_LEN: u64 = std::u64::MAX - 2;
impl DecodedLength {
pub(crate) const CLOSE_DELIMITED: DecodedLength = DecodedLength(::std::u64::MAX);
pub(crate) const CHUNKED: DecodedLength = DecodedLength(::std::u64::MAX - 1);
pub(crate) const ZERO: DecodedLength = DecodedLength(0);
#[cfg(test)]
pub(crate) fn new(len: u64) -> Self {
debug_assert!(len <= MAX_LEN);
DecodedLength(len)
}
/// Takes the length as a content-length without other checks.
///
/// Should only be called if previously confirmed this isn't
/// CLOSE_DELIMITED or CHUNKED.
#[inline]
pub(crate) fn danger_len(self) -> u64 {
debug_assert!(self.0 < Self::CHUNKED.0);
self.0
}
/// Converts to an Option<u64> representing a Known or Unknown length.
pub(crate) fn into_opt(self) -> Option<u64> {
match self {
DecodedLength::CHUNKED | DecodedLength::CLOSE_DELIMITED => None,
DecodedLength(known) => Some(known),
}
}
/// Checks the `u64` is within the maximum allowed for content-length.
pub(crate) fn checked_new(len: u64) -> Result<Self, crate::error::Parse> {
if len <= MAX_LEN {
Ok(DecodedLength(len))
} else {
warn!("content-length bigger than maximum: {} > {}", len, MAX_LEN);
Err(crate::error::Parse::TooLarge)
}
}
pub(crate) fn sub_if(&mut self, amt: u64) {
match *self {
DecodedLength::CHUNKED | DecodedLength::CLOSE_DELIMITED => (),
DecodedLength(ref mut known) => {
*known -= amt;
}
}
}
}
impl fmt::Debug for DecodedLength {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
DecodedLength::CLOSE_DELIMITED => f.write_str("CLOSE_DELIMITED"),
DecodedLength::CHUNKED => f.write_str("CHUNKED"),
DecodedLength(n) => f.debug_tuple("DecodedLength").field(&n).finish(),
}
}
}
impl fmt::Display for DecodedLength {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
DecodedLength::CLOSE_DELIMITED => f.write_str("close-delimited"),
DecodedLength::CHUNKED => f.write_str("chunked encoding"),
DecodedLength::ZERO => f.write_str("empty"),
DecodedLength(n) => write!(f, "content-length ({} bytes)", n),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn sub_if_known() {
let mut len = DecodedLength::new(30);
len.sub_if(20);
assert_eq!(len.0, 10);
}
#[test]
fn sub_if_chunked() {
let mut len = DecodedLength::CHUNKED;
len.sub_if(20);
assert_eq!(len, DecodedLength::CHUNKED);
}
}
}

View File

@@ -5,4 +5,8 @@
//! If the `runtime` feature is disabled, the types in this module can be used //! If the `runtime` feature is disabled, the types in this module can be used
//! to plug in other runtimes. //! to plug in other runtimes.
pub use crate::common::Executor; /// An executor of futures.
pub trait Executor<Fut> {
/// Place the future into the executor to be run.
fn execute(&self, fut: Fut);
}

View File

@@ -11,7 +11,7 @@
//! ## Example //! ## Example
//! A simple example that uses the `Http` struct to talk HTTP over a Tokio TCP stream //! A simple example that uses the `Http` struct to talk HTTP over a Tokio TCP stream
//! ```no_run //! ```no_run
//! # #[cfg(feature = "runtime")] //! # #[cfg(all(feature = "http1", feature = "runtime"))]
//! # mod rt { //! # mod rt {
//! use http::{Request, Response, StatusCode}; //! use http::{Request, Response, StatusCode};
//! use hyper::{server::conn::Http, service::service_fn, Body}; //! use hyper::{server::conn::Http, service::service_fn, Body};
@@ -28,7 +28,7 @@
//! tokio::task::spawn(async move { //! tokio::task::spawn(async move {
//! if let Err(http_err) = Http::new() //! if let Err(http_err) = Http::new()
//! .http1_only(true) //! .http1_only(true)
//! .keep_alive(true) //! .http1_keep_alive(true)
//! .serve_connection(tcp_stream, service_fn(hello)) //! .serve_connection(tcp_stream, service_fn(hello))
//! .await { //! .await {
//! eprintln!("Error while serving HTTP connection: {}", http_err); //! eprintln!("Error while serving HTTP connection: {}", http_err);
@@ -45,8 +45,8 @@
use std::error::Error as StdError; use std::error::Error as StdError;
use std::fmt; use std::fmt;
#[cfg(feature = "http1")]
use std::marker::PhantomData; use std::marker::PhantomData;
use std::mem;
#[cfg(feature = "tcp")] #[cfg(feature = "tcp")]
use std::net::SocketAddr; use std::net::SocketAddr;
#[cfg(feature = "runtime")] #[cfg(feature = "runtime")]
@@ -63,10 +63,12 @@ use crate::common::exec::{ConnStreamExec, Exec, NewSvcExec};
#[cfg(feature = "http2")] #[cfg(feature = "http2")]
use crate::common::io::Rewind; use crate::common::io::Rewind;
use crate::common::{task, Future, Pin, Poll, Unpin}; use crate::common::{task, Future, Pin, Poll, Unpin};
#[cfg(feature = "http1")]
#[cfg(feature = "http2")] #[cfg(feature = "http2")]
use crate::error::{Kind, Parse}; use crate::error::{Kind, Parse};
use crate::proto; use crate::proto;
use crate::service::{HttpService, MakeServiceRef}; use crate::service::{HttpService, MakeServiceRef};
#[cfg(feature = "http1")]
use crate::upgrade::Upgraded; use crate::upgrade::Upgraded;
use self::spawn_all::NewSvcTask; use self::spawn_all::NewSvcTask;
@@ -100,11 +102,13 @@ pub struct Http<E = Exec> {
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
enum ConnectionMode { enum ConnectionMode {
/// Always use HTTP/1 and do not upgrade when a parse error occurs. /// Always use HTTP/1 and do not upgrade when a parse error occurs.
#[cfg(feature = "http1")]
H1Only, H1Only,
/// Always use HTTP/2. /// Always use HTTP/2.
#[cfg(feature = "http2")] #[cfg(feature = "http2")]
H2Only, H2Only,
/// Use HTTP/1 and try to upgrade to h2 when a parse error occurs. /// Use HTTP/1 and try to upgrade to h2 when a parse error occurs.
#[cfg(feature = "http1")]
#[cfg(feature = "http2")] #[cfg(feature = "http2")]
Fallback, Fallback,
} }
@@ -157,6 +161,7 @@ where
S: HttpService<Body>, S: HttpService<Body>,
{ {
pub(super) conn: Option<ProtoServer<T, S::ResBody, S, E>>, pub(super) conn: Option<ProtoServer<T, S::ResBody, S, E>>,
#[cfg(feature = "http1")]
#[cfg(feature = "http2")] #[cfg(feature = "http2")]
fallback: Fallback<E>, fallback: Fallback<E>,
} }
@@ -167,6 +172,7 @@ where
S: HttpService<Body>, S: HttpService<Body>,
B: HttpBody, B: HttpBody,
{ {
#[cfg(feature = "http1")]
H1( H1(
#[pin] #[pin]
proto::h1::Dispatcher< proto::h1::Dispatcher<
@@ -181,6 +187,7 @@ where
H2(#[pin] proto::h2::Server<Rewind<T>, S, B, E>), H2(#[pin] proto::h2::Server<Rewind<T>, S, B, E>),
} }
#[cfg(feature = "http1")]
#[cfg(feature = "http2")] #[cfg(feature = "http2")]
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
enum Fallback<E> { enum Fallback<E> {
@@ -188,6 +195,7 @@ enum Fallback<E> {
Http1Only, Http1Only,
} }
#[cfg(feature = "http1")]
#[cfg(feature = "http2")] #[cfg(feature = "http2")]
impl<E> Fallback<E> { impl<E> Fallback<E> {
fn to_h2(&self) -> bool { fn to_h2(&self) -> bool {
@@ -198,6 +206,7 @@ impl<E> Fallback<E> {
} }
} }
#[cfg(feature = "http1")]
#[cfg(feature = "http2")] #[cfg(feature = "http2")]
impl<E> Unpin for Fallback<E> {} impl<E> Unpin for Fallback<E> {}
@@ -247,6 +256,8 @@ impl<E> Http<E> {
/// Sets whether HTTP1 is required. /// Sets whether HTTP1 is required.
/// ///
/// Default is false /// Default is false
#[cfg(feature = "http1")]
#[cfg_attr(docsrs, doc(cfg(feature = "http1")))]
pub fn http1_only(&mut self, val: bool) -> &mut Self { pub fn http1_only(&mut self, val: bool) -> &mut Self {
if val { if val {
self.mode = ConnectionMode::H1Only; self.mode = ConnectionMode::H1Only;
@@ -267,6 +278,8 @@ impl<E> Http<E> {
/// detects an EOF in the middle of a request. /// detects an EOF in the middle of a request.
/// ///
/// Default is `false`. /// Default is `false`.
#[cfg(feature = "http1")]
#[cfg_attr(docsrs, doc(cfg(feature = "http1")))]
pub fn http1_half_close(&mut self, val: bool) -> &mut Self { pub fn http1_half_close(&mut self, val: bool) -> &mut Self {
self.h1_half_close = val; self.h1_half_close = val;
self self
@@ -275,18 +288,13 @@ impl<E> Http<E> {
/// Enables or disables HTTP/1 keep-alive. /// Enables or disables HTTP/1 keep-alive.
/// ///
/// Default is true. /// Default is true.
#[cfg(feature = "http1")]
#[cfg_attr(docsrs, doc(cfg(feature = "http1")))]
pub fn http1_keep_alive(&mut self, val: bool) -> &mut Self { pub fn http1_keep_alive(&mut self, val: bool) -> &mut Self {
self.h1_keep_alive = val; self.h1_keep_alive = val;
self self
} }
// renamed due different semantics of http2 keep alive
#[doc(hidden)]
#[deprecated(note = "renamed to `http1_keep_alive`")]
pub fn keep_alive(&mut self, val: bool) -> &mut Self {
self.http1_keep_alive(val)
}
/// Set whether HTTP/1 connections should try to use vectored writes, /// Set whether HTTP/1 connections should try to use vectored writes,
/// or always flatten into a single buffer. /// or always flatten into a single buffer.
/// ///
@@ -300,6 +308,8 @@ impl<E> Http<E> {
/// Default is `auto`. In this mode hyper will try to guess which /// Default is `auto`. In this mode hyper will try to guess which
/// mode to use /// mode to use
#[inline] #[inline]
#[cfg(feature = "http1")]
#[cfg_attr(docsrs, doc(cfg(feature = "http1")))]
pub fn http1_writev(&mut self, val: bool) -> &mut Self { pub fn http1_writev(&mut self, val: bool) -> &mut Self {
self.h1_writev = Some(val); self.h1_writev = Some(val);
self self
@@ -314,8 +324,11 @@ impl<E> Http<E> {
if val { if val {
self.mode = ConnectionMode::H2Only; self.mode = ConnectionMode::H2Only;
} else { } else {
#[cfg(feature = "http1")]
{
self.mode = ConnectionMode::Fallback; self.mode = ConnectionMode::Fallback;
} }
}
self self
} }
@@ -446,6 +459,8 @@ impl<E> Http<E> {
/// # Panics /// # Panics
/// ///
/// The minimum value allowed is 8192. This method panics if the passed `max` is less than the minimum. /// The minimum value allowed is 8192. This method panics if the passed `max` is less than the minimum.
#[cfg(feature = "http1")]
#[cfg_attr(docsrs, doc(cfg(feature = "http1")))]
pub fn max_buf_size(&mut self, max: usize) -> &mut Self { pub fn max_buf_size(&mut self, max: usize) -> &mut Self {
assert!( assert!(
max >= proto::h1::MINIMUM_MAX_BUFFER_SIZE, max >= proto::h1::MINIMUM_MAX_BUFFER_SIZE,
@@ -519,6 +534,7 @@ impl<E> Http<E> {
I: AsyncRead + AsyncWrite + Unpin, I: AsyncRead + AsyncWrite + Unpin,
E: ConnStreamExec<S::Future, Bd>, E: ConnStreamExec<S::Future, Bd>,
{ {
#[cfg(feature = "http1")]
macro_rules! h1 { macro_rules! h1 {
() => {{ () => {{
let mut conn = proto::Conn::new(io); let mut conn = proto::Conn::new(io);
@@ -545,9 +561,11 @@ impl<E> Http<E> {
} }
let proto = match self.mode { let proto = match self.mode {
#[cfg(feature = "http1")]
#[cfg(not(feature = "http2"))] #[cfg(not(feature = "http2"))]
ConnectionMode::H1Only => h1!(), ConnectionMode::H1Only => h1!(),
#[cfg(feature = "http2")] #[cfg(feature = "http2")]
#[cfg(feature = "http1")]
ConnectionMode::H1Only | ConnectionMode::Fallback => h1!(), ConnectionMode::H1Only | ConnectionMode::Fallback => h1!(),
#[cfg(feature = "http2")] #[cfg(feature = "http2")]
ConnectionMode::H2Only => { ConnectionMode::H2Only => {
@@ -560,6 +578,7 @@ impl<E> Http<E> {
Connection { Connection {
conn: Some(proto), conn: Some(proto),
#[cfg(feature = "http1")]
#[cfg(feature = "http2")] #[cfg(feature = "http2")]
fallback: if self.mode == ConnectionMode::Fallback { fallback: if self.mode == ConnectionMode::Fallback {
Fallback::ToHttp2(self.h2_builder.clone(), self.exec.clone()) Fallback::ToHttp2(self.h2_builder.clone(), self.exec.clone())
@@ -610,6 +629,7 @@ where
/// nothing. /// nothing.
pub fn graceful_shutdown(self: Pin<&mut Self>) { pub fn graceful_shutdown(self: Pin<&mut Self>) {
match self.project().conn { match self.project().conn {
#[cfg(feature = "http1")]
Some(ProtoServer::H1(ref mut h1, _)) => { Some(ProtoServer::H1(ref mut h1, _)) => {
h1.disable_keep_alive(); h1.disable_keep_alive();
} }
@@ -640,6 +660,7 @@ where
/// This method will return a `None` if this connection is using an h2 protocol. /// This method will return a `None` if this connection is using an h2 protocol.
pub fn try_into_parts(self) -> Option<Parts<I, S>> { pub fn try_into_parts(self) -> Option<Parts<I, S>> {
match self.conn.unwrap() { match self.conn.unwrap() {
#[cfg(feature = "http1")]
ProtoServer::H1(h1, _) => { ProtoServer::H1(h1, _) => {
let (io, read_buf, dispatch) = h1.into_inner(); let (io, read_buf, dispatch) = h1.into_inner();
Some(Parts { Some(Parts {
@@ -672,12 +693,9 @@ where
B: Unpin, B: Unpin,
{ {
loop { loop {
let polled = match *self.conn.as_mut().unwrap() { match *self.conn.as_mut().unwrap() {
ProtoServer::H1(ref mut h1, _) => h1.poll_without_shutdown(cx), #[cfg(feature = "http1")]
#[cfg(feature = "http2")] ProtoServer::H1(ref mut h1, _) => match ready!(h1.poll_without_shutdown(cx)) {
ProtoServer::H2(ref mut h2) => return Pin::new(h2).poll(cx).map_ok(|_| ()),
};
match ready!(polled) {
Ok(()) => return Poll::Ready(Ok(())), Ok(()) => return Poll::Ready(Ok(())),
Err(e) => { Err(e) => {
#[cfg(feature = "http2")] #[cfg(feature = "http2")]
@@ -691,7 +709,10 @@ where
return Poll::Ready(Err(e)); return Poll::Ready(Err(e));
} }
} },
#[cfg(feature = "http2")]
ProtoServer::H2(ref mut h2) => return Pin::new(h2).poll(cx).map_ok(|_| ()),
};
} }
} }
@@ -710,6 +731,7 @@ where
}) })
} }
#[cfg(feature = "http1")]
#[cfg(feature = "http2")] #[cfg(feature = "http2")]
fn upgrade_h2(&mut self) { fn upgrade_h2(&mut self) {
trace!("Trying to upgrade connection to h2"); trace!("Trying to upgrade connection to h2");
@@ -759,16 +781,21 @@ where
loop { loop {
match ready!(Pin::new(self.conn.as_mut().unwrap()).poll(cx)) { match ready!(Pin::new(self.conn.as_mut().unwrap()).poll(cx)) {
Ok(done) => { Ok(done) => {
if let proto::Dispatched::Upgrade(pending) = done { match done {
proto::Dispatched::Shutdown => {}
#[cfg(feature = "http1")]
proto::Dispatched::Upgrade(pending) => {
// With no `Send` bound on `I`, we can't try to do // With no `Send` bound on `I`, we can't try to do
// upgrades here. In case a user was trying to use // upgrades here. In case a user was trying to use
// `Body::on_upgrade` with this API, send a special // `Body::on_upgrade` with this API, send a special
// error letting them know about that. // error letting them know about that.
pending.manual(); pending.manual();
} }
};
return Poll::Ready(Ok(())); return Poll::Ready(Ok(()));
} }
Err(e) => { Err(e) => {
#[cfg(feature = "http1")]
#[cfg(feature = "http2")] #[cfg(feature = "http2")]
match *e.kind() { match *e.kind() {
Kind::Parse(Parse::VersionH2) if self.fallback.to_h2() => { Kind::Parse(Parse::VersionH2) if self.fallback.to_h2() => {
@@ -796,18 +823,21 @@ where
// ===== impl ConnectionMode ===== // ===== impl ConnectionMode =====
impl ConnectionMode {}
impl Default for ConnectionMode { impl Default for ConnectionMode {
#[cfg(feature = "http2")] #[cfg(all(feature = "http1", feature = "http2"))]
fn default() -> ConnectionMode { fn default() -> ConnectionMode {
ConnectionMode::Fallback ConnectionMode::Fallback
} }
#[cfg(not(feature = "http2"))] #[cfg(all(feature = "http1", not(feature = "http2")))]
fn default() -> ConnectionMode { fn default() -> ConnectionMode {
ConnectionMode::H1Only ConnectionMode::H1Only
} }
#[cfg(all(not(feature = "http1"), feature = "http2"))]
fn default() -> ConnectionMode {
ConnectionMode::H2Only
}
} }
// ===== impl Serve ===== // ===== impl Serve =====
@@ -955,6 +985,7 @@ where
fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> { fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
match self.project() { match self.project() {
#[cfg(feature = "http1")]
ProtoServerProj::H1(s, _) => s.poll(cx), ProtoServerProj::H1(s, _) => s.poll(cx),
#[cfg(feature = "http2")] #[cfg(feature = "http2")]
ProtoServerProj::H2(s) => s.poll(cx), ProtoServerProj::H2(s) => s.poll(cx),
@@ -1137,17 +1168,22 @@ mod upgrades {
loop { loop {
match ready!(Pin::new(self.inner.conn.as_mut().unwrap()).poll(cx)) { match ready!(Pin::new(self.inner.conn.as_mut().unwrap()).poll(cx)) {
Ok(proto::Dispatched::Shutdown) => return Poll::Ready(Ok(())), Ok(proto::Dispatched::Shutdown) => return Poll::Ready(Ok(())),
#[cfg(feature = "http1")]
Ok(proto::Dispatched::Upgrade(pending)) => { Ok(proto::Dispatched::Upgrade(pending)) => {
let h1 = match mem::replace(&mut self.inner.conn, None) { match self.inner.conn.take() {
Some(ProtoServer::H1(h1, _)) => h1, Some(ProtoServer::H1(h1, _)) => {
_ => unreachable!("Upgrade expects h1"),
};
let (io, buf, _) = h1.into_inner(); let (io, buf, _) = h1.into_inner();
pending.fulfill(Upgraded::new(io, buf)); pending.fulfill(Upgraded::new(io, buf));
return Poll::Ready(Ok(())); return Poll::Ready(Ok(()));
} }
_ => {
drop(pending);
unreachable!("Upgrade expects h1")
}
};
}
Err(e) => { Err(e) => {
#[cfg(feature = "http1")]
#[cfg(feature = "http2")] #[cfg(feature = "http2")]
match *e.kind() { match *e.kind() {
Kind::Parse(Parse::VersionH2) if self.inner.fallback.to_h2() => { Kind::Parse(Parse::VersionH2) if self.inner.fallback.to_h2() => {

View File

@@ -241,6 +241,8 @@ impl<I, E> Builder<I, E> {
/// Sets whether to use keep-alive for HTTP/1 connections. /// Sets whether to use keep-alive for HTTP/1 connections.
/// ///
/// Default is `true`. /// Default is `true`.
#[cfg(feature = "http1")]
#[cfg_attr(docsrs, doc(cfg(feature = "http1")))]
pub fn http1_keepalive(mut self, val: bool) -> Self { pub fn http1_keepalive(mut self, val: bool) -> Self {
self.protocol.http1_keep_alive(val); self.protocol.http1_keep_alive(val);
self self
@@ -254,6 +256,8 @@ impl<I, E> Builder<I, E> {
/// detects an EOF in the middle of a request. /// detects an EOF in the middle of a request.
/// ///
/// Default is `false`. /// Default is `false`.
#[cfg(feature = "http1")]
#[cfg_attr(docsrs, doc(cfg(feature = "http1")))]
pub fn http1_half_close(mut self, val: bool) -> Self { pub fn http1_half_close(mut self, val: bool) -> Self {
self.protocol.http1_half_close(val); self.protocol.http1_half_close(val);
self self
@@ -262,6 +266,8 @@ impl<I, E> Builder<I, E> {
/// Set the maximum buffer size. /// Set the maximum buffer size.
/// ///
/// Default is ~ 400kb. /// Default is ~ 400kb.
#[cfg(feature = "http1")]
#[cfg_attr(docsrs, doc(cfg(feature = "http1")))]
pub fn http1_max_buf_size(mut self, val: usize) -> Self { pub fn http1_max_buf_size(mut self, val: usize) -> Self {
self.protocol.max_buf_size(val); self.protocol.max_buf_size(val);
self self
@@ -272,6 +278,7 @@ impl<I, E> Builder<I, E> {
// This isn't really desirable in most cases, only really being useful in // This isn't really desirable in most cases, only really being useful in
// silly pipeline benchmarks. // silly pipeline benchmarks.
#[doc(hidden)] #[doc(hidden)]
#[cfg(feature = "http1")]
pub fn http1_pipeline_flush(mut self, val: bool) -> Self { pub fn http1_pipeline_flush(mut self, val: bool) -> Self {
self.protocol.pipeline_flush(val); self.protocol.pipeline_flush(val);
self self
@@ -290,7 +297,9 @@ impl<I, E> Builder<I, E> {
/// which may eliminate unnecessary cloning on some TLS backends /// which may eliminate unnecessary cloning on some TLS backends
/// ///
/// Default is `auto`. In this mode hyper will try to guess which /// Default is `auto`. In this mode hyper will try to guess which
/// mode to use /// mode to use.
#[cfg(feature = "http1")]
#[cfg_attr(docsrs, doc(cfg(feature = "http1")))]
pub fn http1_writev(mut self, val: bool) -> Self { pub fn http1_writev(mut self, val: bool) -> Self {
self.protocol.http1_writev(val); self.protocol.http1_writev(val);
self self
@@ -299,6 +308,8 @@ impl<I, E> Builder<I, E> {
/// Sets whether HTTP/1 is required. /// Sets whether HTTP/1 is required.
/// ///
/// Default is `false`. /// Default is `false`.
#[cfg(feature = "http1")]
#[cfg_attr(docsrs, doc(cfg(feature = "http1")))]
pub fn http1_only(mut self, val: bool) -> Self { pub fn http1_only(mut self, val: bool) -> Self {
self.protocol.http1_only(val); self.protocol.http1_only(val);
self self

View File

@@ -39,11 +39,14 @@ pub use tower_service::Service;
mod http; mod http;
mod make; mod make;
#[cfg(any(feature = "http1", feature = "http2"))]
mod oneshot; mod oneshot;
mod util; mod util;
pub(crate) use self::http::HttpService; pub(crate) use self::http::HttpService;
#[cfg(any(feature = "http1", feature = "http2"))]
pub(crate) use self::make::{MakeConnection, MakeServiceRef}; pub(crate) use self::make::{MakeConnection, MakeServiceRef};
#[cfg(any(feature = "http1", feature = "http2"))]
pub(crate) use self::oneshot::{oneshot, Oneshot}; pub(crate) use self::oneshot::{oneshot, Oneshot};
pub use self::make::make_service_fn; pub use self::make::make_service_fn;

View File

@@ -57,6 +57,7 @@ pub struct Parts<T> {
_inner: (), _inner: (),
} }
#[cfg(feature = "http1")]
pub(crate) struct Pending { pub(crate) struct Pending {
tx: oneshot::Sender<crate::Result<Upgraded>>, tx: oneshot::Sender<crate::Result<Upgraded>>,
} }
@@ -68,6 +69,7 @@ pub(crate) struct Pending {
#[derive(Debug)] #[derive(Debug)]
struct UpgradeExpected(()); struct UpgradeExpected(());
#[cfg(feature = "http1")]
pub(crate) fn pending() -> (Pending, OnUpgrade) { pub(crate) fn pending() -> (Pending, OnUpgrade) {
let (tx, rx) = oneshot::channel(); let (tx, rx) = oneshot::channel();
(Pending { tx }, OnUpgrade { rx: Some(rx) }) (Pending { tx }, OnUpgrade { rx: Some(rx) })
@@ -76,6 +78,7 @@ pub(crate) fn pending() -> (Pending, OnUpgrade) {
// ===== impl Upgraded ===== // ===== impl Upgraded =====
impl Upgraded { impl Upgraded {
#[cfg(any(feature = "http1", test))]
pub(crate) fn new<T>(io: T, read_buf: Bytes) -> Self pub(crate) fn new<T>(io: T, read_buf: Bytes) -> Self
where where
T: AsyncRead + AsyncWrite + Unpin + Send + 'static, T: AsyncRead + AsyncWrite + Unpin + Send + 'static,
@@ -145,6 +148,7 @@ impl OnUpgrade {
OnUpgrade { rx: None } OnUpgrade { rx: None }
} }
#[cfg(feature = "http1")]
pub(crate) fn is_none(&self) -> bool { pub(crate) fn is_none(&self) -> bool {
self.rx.is_none() self.rx.is_none()
} }
@@ -175,6 +179,7 @@ impl fmt::Debug for OnUpgrade {
// ===== impl Pending ===== // ===== impl Pending =====
#[cfg(feature = "http1")]
impl Pending { impl Pending {
pub(crate) fn fulfill(self, upgraded: Upgraded) { pub(crate) fn fulfill(self, upgraded: Upgraded) {
trace!("pending upgrade fulfill"); trace!("pending upgrade fulfill");

View File

@@ -1078,6 +1078,7 @@ async fn nonempty_parse_eof_returns_error() {
.expect_err("partial parse eof is error"); .expect_err("partial parse eof is error");
} }
#[cfg(feature = "http1")]
#[tokio::test] #[tokio::test]
async fn http1_allow_half_close() { async fn http1_allow_half_close() {
let _ = pretty_env_logger::try_init(); let _ = pretty_env_logger::try_init();
@@ -1111,6 +1112,7 @@ async fn http1_allow_half_close() {
t1.join().expect("client thread"); t1.join().expect("client thread");
} }
#[cfg(feature = "http1")]
#[tokio::test] #[tokio::test]
async fn disconnect_after_reading_request_before_responding() { async fn disconnect_after_reading_request_before_responding() {
let _ = pretty_env_logger::try_init(); let _ = pretty_env_logger::try_init();
@@ -1524,18 +1526,22 @@ async fn illegal_request_length_returns_400_response() {
.expect_err("illegal Content-Length should error"); .expect_err("illegal Content-Length should error");
} }
#[cfg(feature = "http1")]
#[test] #[test]
#[should_panic] #[should_panic]
fn max_buf_size_panic_too_small() { fn max_buf_size_panic_too_small() {
const MAX: usize = 8191; const MAX: usize = 8191;
Http::new().max_buf_size(MAX); Http::new().max_buf_size(MAX);
} }
#[cfg(feature = "http1")]
#[test] #[test]
fn max_buf_size_no_panic() { fn max_buf_size_no_panic() {
const MAX: usize = 8193; const MAX: usize = 8193;
Http::new().max_buf_size(MAX); Http::new().max_buf_size(MAX);
} }
#[cfg(feature = "http1")]
#[tokio::test] #[tokio::test]
async fn max_buf_size() { async fn max_buf_size() {
let _ = pretty_env_logger::try_init(); let _ = pretty_env_logger::try_init();
@@ -2277,7 +2283,7 @@ impl ServeOptions {
fn serve(self) -> Serve { fn serve(self) -> Serve {
let _ = pretty_env_logger::try_init(); let _ = pretty_env_logger::try_init();
let options = self; let _options = self;
let (addr_tx, addr_rx) = mpsc::channel(); let (addr_tx, addr_rx) = mpsc::channel();
let (msg_tx, msg_rx) = mpsc::channel(); let (msg_tx, msg_rx) = mpsc::channel();
@@ -2306,11 +2312,15 @@ impl ServeOptions {
}) })
}); });
let server = Server::bind(&addr) let builder = Server::bind(&addr);
.http1_only(options.http1_only)
.http1_keepalive(options.keep_alive) #[cfg(feature = "http1")]
.http1_pipeline_flush(options.pipeline) let builder = builder
.serve(service); .http1_only(_options.http1_only)
.http1_keepalive(_options.keep_alive)
.http1_pipeline_flush(_options.pipeline);
let server = builder.serve(service);
addr_tx.send(server.local_addr()).expect("server addr tx"); addr_tx.send(server.local_addr()).expect("server addr tx");