diff --git a/Cargo.toml b/Cargo.toml index d69999b..5421fa3 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 36610a1..4a3dd0b 100644 --- a/src/server/conn/auto.rs +++ b/src/server/conn/auto.rs @@ -15,23 +15,52 @@ 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}; + +#[cfg(any(not(feature = "http2"), not(feature = "http1")))] +use std::marker::PhantomData; + 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"; +/// Exactly equivalent to [`Http2ServerConnExec`]. +#[cfg(feature = "http2")] +pub trait HttpServerConnExec: Http2ServerConnExec {} + +#[cfg(feature = "http2")] +impl> HttpServerConnExec for T {} + +/// Exactly equivalent to [`Http2ServerConnExec`]. +#[cfg(not(feature = "http2"))] +pub trait HttpServerConnExec {} + +#[cfg(not(feature = "http2"))] +impl HttpServerConnExec for T {} + /// 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 { @@ -52,17 +81,23 @@ 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 } } @@ -76,7 +111,7 @@ impl Builder { B: Body + 'static, B::Error: Into>, I: Read + Write + Unpin + 'static, - E: Http2ServerConnExec, + E: HttpServerConnExec, { Connection { state: ConnState::ReadVersion { @@ -102,7 +137,7 @@ impl Builder { B: Body + 'static, B::Error: Into>, I: Read + Write + Unpin + Send + 'static, - E: Http2ServerConnExec, + E: HttpServerConnExec, { UpgradeableConnection { state: UpgradeableConnState::ReadVersion { @@ -113,12 +148,24 @@ impl Builder { } } } + #[derive(Copy, Clone)] 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"), + } + } +} + fn read_version(io: I) -> ReadVersion where I: Read + Unpin, @@ -202,6 +249,18 @@ pin_project! { } } +#[cfg(feature = "http1")] +type Http1Connection = hyper::server::conn::http1::Connection, S>; + +#[cfg(not(feature = "http1"))] +type Http1Connection = (PhantomData, PhantomData); + +#[cfg(feature = "http2")] +type Http2Connection = hyper::server::conn::http2::Connection, S, E>; + +#[cfg(not(feature = "http2"))] +type Http2Connection = (PhantomData, PhantomData, PhantomData); + pin_project! { #[project = ConnStateProj] enum ConnState<'a, I, S, E> @@ -216,11 +275,11 @@ pin_project! { }, H1 { #[pin] - conn: hyper::server::conn::http1::Connection, S>, + conn: Http1Connection, }, H2 { #[pin] - conn: hyper::server::conn::http2::Connection, S, E>, + conn: Http2Connection, }, } } @@ -232,7 +291,7 @@ where I: Read + Write + Unpin, B: Body + 'static, B::Error: Into>, - E: Http2ServerConnExec, + E: HttpServerConnExec, { /// Start a graceful shutdown process for this connection. /// @@ -245,8 +304,12 @@ where pub fn graceful_shutdown(self: Pin<&mut Self>) { match self.project().state.project() { ConnStateProj::ReadVersion { .. } => {} + #[cfg(feature = "http1")] ConnStateProj::H1 { conn } => conn.graceful_shutdown(), + #[cfg(feature = "http2")] ConnStateProj::H2 { conn } => conn.graceful_shutdown(), + #[cfg(any(not(feature = "http1"), not(feature = "http2")))] + _ => unreachable!(), } } } @@ -259,7 +322,7 @@ where B: Body + 'static, B::Error: Into>, I: Read + Write + Unpin + 'static, - E: Http2ServerConnExec, + E: HttpServerConnExec, { type Output = Result<()>; @@ -276,22 +339,30 @@ where let (version, io) = ready!(read_version.poll(cx))?; let service = service.take().unwrap(); match version { + #[cfg(feature = "http1")] Version::H1 => { let conn = builder.http1.serve_connection(io, service); this.state.set(ConnState::H1 { conn }); } + #[cfg(feature = "http2")] Version::H2 => { let conn = builder.http2.serve_connection(io, service); this.state.set(ConnState::H2 { conn }); } + #[cfg(any(not(feature = "http1"), not(feature = "http2")))] + _ => return Poll::Ready(Err(version.unsupported())), } } + #[cfg(feature = "http1")] ConnStateProj::H1 { conn } => { return conn.poll(cx).map_err(Into::into); } + #[cfg(feature = "http2")] ConnStateProj::H2 { conn } => { return conn.poll(cx).map_err(Into::into); } + #[cfg(any(not(feature = "http1"), not(feature = "http2")))] + _ => unreachable!(), } } } @@ -308,6 +379,12 @@ pin_project! { } } +#[cfg(feature = "http1")] +type Http1UpgradeableConnection = hyper::server::conn::http1::UpgradeableConnection; + +#[cfg(not(feature = "http1"))] +type Http1UpgradeableConnection = (PhantomData, PhantomData); + pin_project! { #[project = UpgradeableConnStateProj] enum UpgradeableConnState<'a, I, S, E> @@ -322,11 +399,11 @@ pin_project! { }, H1 { #[pin] - conn: hyper::server::conn::http1::UpgradeableConnection, S>, + conn: Http1UpgradeableConnection, S>, }, H2 { #[pin] - conn: hyper::server::conn::http2::Connection, S, E>, + conn: Http2Connection, }, } } @@ -338,7 +415,7 @@ where I: Read + Write + Unpin, B: Body + 'static, B::Error: Into>, - E: Http2ServerConnExec, + E: HttpServerConnExec, { /// Start a graceful shutdown process for this connection. /// @@ -351,8 +428,12 @@ where pub fn graceful_shutdown(self: Pin<&mut Self>) { match self.project().state.project() { UpgradeableConnStateProj::ReadVersion { .. } => {} + #[cfg(feature = "http1")] UpgradeableConnStateProj::H1 { conn } => conn.graceful_shutdown(), + #[cfg(feature = "http2")] UpgradeableConnStateProj::H2 { conn } => conn.graceful_shutdown(), + #[cfg(any(not(feature = "http1"), not(feature = "http2")))] + _ => unreachable!(), } } } @@ -365,7 +446,7 @@ where B: Body + 'static, B::Error: Into>, I: Read + Write + Unpin + Send + 'static, - E: Http2ServerConnExec, + E: HttpServerConnExec, { type Output = Result<()>; @@ -382,34 +463,45 @@ where let (version, io) = ready!(read_version.poll(cx))?; let service = service.take().unwrap(); match version { + #[cfg(feature = "http1")] Version::H1 => { let conn = builder.http1.serve_connection(io, service).with_upgrades(); this.state.set(UpgradeableConnState::H1 { conn }); } + #[cfg(feature = "http2")] Version::H2 => { let conn = builder.http2.serve_connection(io, service); this.state.set(UpgradeableConnState::H2 { conn }); } + #[cfg(any(not(feature = "http1"), not(feature = "http2")))] + _ => return Poll::Ready(Err(version.unsupported())), } } + #[cfg(feature = "http1")] UpgradeableConnStateProj::H1 { conn } => { return conn.poll(cx).map_err(Into::into); } + #[cfg(feature = "http2")] UpgradeableConnStateProj::H2 { conn } => { return conn.poll(cx).map_err(Into::into); } + #[cfg(any(not(feature = "http1"), not(feature = "http2")))] + _ => unreachable!(), } } } } /// 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, @@ -524,6 +616,22 @@ 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>, + S::Future: 'static, + S::Error: Into>, + B: Body + 'static, + B::Error: Into>, + I: Read + Write + Unpin + 'static, + E: HttpServerConnExec, + { + 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>, @@ -532,18 +640,20 @@ impl Http1Builder<'_, E> { B: Body + 'static, B::Error: Into>, I: Read + Write + Unpin + 'static, - E: Http2ServerConnExec, { 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 { @@ -679,7 +789,7 @@ impl Http2Builder<'_, E> { B: Body + 'static, B::Error: Into>, I: Read + Write + Unpin + 'static, - E: Http2ServerConnExec, + E: HttpServerConnExec, { self.inner.serve_connection(io, service).await } 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;