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