diff --git a/Cargo.toml b/Cargo.toml index 841c4b9..b20d732 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ authors = ["Sean McArthur "] keywords = ["http", "hyper", "hyperium"] categories = ["network-programming", "web-programming::http-client", "web-programming::http-server"] edition = "2018" +resolver = "2" [package.metadata.docs.rs] features = ["full"] diff --git a/src/common/mod.rs b/src/common/mod.rs index 160df72..496a887 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -1,5 +1,6 @@ #![allow(missing_docs)] +#[allow(unused)] macro_rules! ready { ($e:expr) => { match $e { @@ -9,6 +10,7 @@ macro_rules! ready { }; } +#[allow(unused)] pub(crate) use ready; pub(crate) mod exec; #[cfg(feature = "client")] diff --git a/src/server/conn/auto.rs b/src/server/conn/auto.rs index 2747191..0de9d3e 100644 --- a/src/server/conn/auto.rs +++ b/src/server/conn/auto.rs @@ -13,23 +13,35 @@ use http::{Request, Response}; use http_body::Body; use hyper::{ body::Incoming, - rt::{bounds::Http2ServerConnExec, Read, ReadBuf, Timer, Write}, - server::conn::{http1, http2}, + rt::{Read, ReadBuf, Timer, Write}, service::Service, }; + +#[cfg(feature = "http1")] +use hyper::server::conn::http1; + +#[cfg(feature = "http2")] +use hyper::{rt::bounds::Http2ServerConnExec, server::conn::http2}; + use pin_project_lite::pin_project; use crate::common::rewind::Rewind; -type Result = std::result::Result>; +type Error = Box; + +type Result = std::result::Result; const H2_PREFACE: &[u8] = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"; /// Http1 or Http2 connection builder. #[derive(Clone, Debug)] pub struct Builder { + #[cfg(feature = "http1")] http1: http1::Builder, + #[cfg(feature = "http2")] http2: http2::Builder, + #[cfg(not(feature = "http2"))] + _executor: E, } impl Builder { @@ -50,22 +62,29 @@ impl Builder { /// ``` pub fn new(executor: E) -> Self { Self { + #[cfg(feature = "http1")] http1: http1::Builder::new(), + #[cfg(feature = "http2")] http2: http2::Builder::new(executor), + #[cfg(not(feature = "http2"))] + _executor: executor, } } /// Http1 configuration. + #[cfg(feature = "http1")] pub fn http1(&mut self) -> Http1Builder<'_, E> { Http1Builder { inner: self } } /// Http2 configuration. + #[cfg(feature = "http2")] pub fn http2(&mut self) -> Http2Builder<'_, E> { Http2Builder { inner: self } } /// Bind a connection together with a [`Service`]. + #[cfg(feature = "http2")] pub async fn serve_connection(&self, io: I, service: S) -> Result<()> where S: Service, Response = Response>, @@ -78,8 +97,32 @@ impl Builder { { let (version, io) = read_version(io).await?; match version { + #[cfg(feature = "http1")] Version::H1 => self.http1.serve_connection(io, service).await?, Version::H2 => self.http2.serve_connection(io, service).await?, + #[cfg(any(not(feature = "http2"), not(feature = "http1")))] + version => return Err(version.unsupported()), + } + + Ok(()) + } + + /// Bind a connection together with a [`Service`]. + #[cfg(not(feature = "http2"))] + pub async fn serve_connection(&self, io: I, service: S) -> Result<()> + where + S: Service, Response = Response>, + S::Future: 'static, + S::Error: Into>, + B: Body + 'static, + B::Error: Into>, + I: Read + Write + Unpin + 'static, + { + let (version, io) = read_version(io).await?; + match version { + #[cfg(feature = "http1")] + Version::H1 => self.http1.serve_connection(io, service).await?, + version => return Err(version.unsupported()), } Ok(()) @@ -88,6 +131,7 @@ impl Builder { /// Bind a connection together with a [`Service`], with the ability to /// handle HTTP upgrades. This requires that the IO object implements /// `Send`. + #[cfg(feature = "http2")] pub async fn serve_connection_with_upgrades(&self, io: I, service: S) -> Result<()> where S: Service, Response = Response>, @@ -100,6 +144,7 @@ impl Builder { { let (version, io) = read_version(io).await?; match version { + #[cfg(feature = "http1")] Version::H1 => { self.http1 .serve_connection(io, service) @@ -107,6 +152,37 @@ impl Builder { .await? } Version::H2 => self.http2.serve_connection(io, service).await?, + #[cfg(any(not(feature = "http2"), not(feature = "http1")))] + version => return Err(version.unsupported()), + } + + Ok(()) + } + + /// Bind a connection together with a [`Service`], with the ability to + /// handle HTTP upgrades. This requires that the IO object implements + /// `Send`. + #[cfg(not(feature = "http2"))] + pub async fn serve_connection_with_upgrades(&self, io: I, service: S) -> Result<()> + where + S: Service, Response = Response>, + S::Future: 'static, + S::Error: Into>, + B: Body + 'static, + B::Error: Into>, + I: Read + Write + Unpin + Send + 'static, + { + let (version, io) = read_version(io).await?; + match version { + #[cfg(feature = "http1")] + Version::H1 => { + self.http1 + .serve_connection(io, service) + .with_upgrades() + .await? + } + #[cfg(any(not(feature = "http2"), not(feature = "http1")))] + version => return Err(version.unsupported()), } Ok(()) @@ -117,6 +193,18 @@ enum Version { H1, H2, } + +impl Version { + #[must_use] + #[cfg(any(not(feature = "http2"), not(feature = "http1")))] + pub fn unsupported(self) -> Error { + match self { + Version::H1 => Error::from("HTTP/1 is not supported"), + Version::H2 => Error::from("HTTP/2 is not supported"), + } + } +} + async fn read_version<'a, A>(mut reader: A) -> IoResult<(Version, Rewind)> where A: Read + Unpin, @@ -172,12 +260,15 @@ where } /// Http1 part of builder. +#[cfg(feature = "http1")] pub struct Http1Builder<'a, E> { inner: &'a mut Builder, } +#[cfg(feature = "http1")] impl Http1Builder<'_, E> { /// Http2 configuration. + #[cfg(feature = "http2")] pub fn http2(&mut self) -> Http2Builder<'_, E> { Http2Builder { inner: &mut self.inner, @@ -292,6 +383,7 @@ impl Http1Builder<'_, E> { } /// Bind a connection together with a [`Service`]. + #[cfg(feature = "http2")] pub async fn serve_connection(&self, io: I, service: S) -> Result<()> where S: Service, Response = Response>, @@ -304,14 +396,31 @@ impl Http1Builder<'_, E> { { self.inner.serve_connection(io, service).await } + + /// Bind a connection together with a [`Service`]. + #[cfg(not(feature = "http2"))] + pub async fn serve_connection(&self, io: I, service: S) -> Result<()> + where + S: Service, Response = Response>, + S::Future: 'static, + S::Error: Into>, + B: Body + 'static, + B::Error: Into>, + I: Read + Write + Unpin + 'static, + { + self.inner.serve_connection(io, service).await + } } /// Http2 part of builder. +#[cfg(feature = "http2")] pub struct Http2Builder<'a, E> { inner: &'a mut Builder, } +#[cfg(feature = "http2")] impl Http2Builder<'_, E> { + #[cfg(feature = "http1")] /// Http1 configuration. pub fn http1(&mut self) -> Http1Builder<'_, E> { Http1Builder { diff --git a/src/server/conn/mod.rs b/src/server/conn/mod.rs index 535aee2..b23503a 100644 --- a/src/server/conn/mod.rs +++ b/src/server/conn/mod.rs @@ -1,4 +1,4 @@ //! Connection utilities. -#[cfg(feature = "server-auto")] +#[cfg(any(feature = "http1", feature = "http2"))] pub mod auto;