feat(server): Make the server code an optional feature (#2334)
				
					
				
			cc #2223 BREAKING CHANGE: The HTTP server code is now an optional feature. To enable the server, add `features = ["server"]` to the dependency in your `Cargo.toml`.
This commit is contained in:
		| @@ -57,6 +57,7 @@ where | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[cfg(feature = "server")] | ||||
|     pub fn set_flush_pipeline(&mut self, enabled: bool) { | ||||
|         self.io.set_flush_pipeline(enabled); | ||||
|     } | ||||
| @@ -83,6 +84,7 @@ where | ||||
|         self.state.title_case_headers = true; | ||||
|     } | ||||
|  | ||||
|     #[cfg(feature = "server")] | ||||
|     pub(crate) fn set_allow_half_close(&mut self) { | ||||
|         self.state.allow_half_close = true; | ||||
|     } | ||||
| @@ -485,6 +487,7 @@ where | ||||
|             Encode { | ||||
|                 head: &mut head, | ||||
|                 body, | ||||
|                 #[cfg(feature = "server")] | ||||
|                 keep_alive: self.state.wants_keep_alive(), | ||||
|                 req_method: &mut self.state.method, | ||||
|                 title_case_headers: self.state.title_case_headers, | ||||
| @@ -690,6 +693,7 @@ where | ||||
|         self.state.close_write(); | ||||
|     } | ||||
|  | ||||
|     #[cfg(feature = "server")] | ||||
|     pub fn disable_keep_alive(&mut self) { | ||||
|         if self.state.is_idle() { | ||||
|             trace!("disable_keep_alive; closing idle connection"); | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| use std::error::Error as StdError; | ||||
|  | ||||
| use bytes::{Buf, Bytes}; | ||||
| use http::{Request, StatusCode}; | ||||
| use http::Request; | ||||
| use tokio::io::{AsyncRead, AsyncWrite}; | ||||
|  | ||||
| use super::{Http1Transaction, Wants}; | ||||
| @@ -10,7 +10,6 @@ use crate::common::{task, Future, Pin, Poll, Unpin}; | ||||
| use crate::proto::{ | ||||
|     BodyLength, Conn, Dispatched, MessageHead, RequestHead, | ||||
| }; | ||||
| use crate::service::HttpService; | ||||
|  | ||||
| pub(crate) struct Dispatcher<D, Bs: HttpBody, I, T> { | ||||
|     conn: Conn<I, Bs::Data, T>, | ||||
| @@ -34,9 +33,13 @@ pub(crate) trait Dispatch { | ||||
|     fn should_poll(&self) -> bool; | ||||
| } | ||||
|  | ||||
| pub struct Server<S: HttpService<B>, B> { | ||||
|     in_flight: Pin<Box<Option<S::Future>>>, | ||||
|     pub(crate) service: S, | ||||
| cfg_server! { | ||||
|     use crate::service::HttpService; | ||||
|  | ||||
|     pub struct Server<S: HttpService<B>, B> { | ||||
|         in_flight: Pin<Box<Option<S::Future>>>, | ||||
|         pub(crate) service: S, | ||||
|     } | ||||
| } | ||||
|  | ||||
| cfg_client! { | ||||
| @@ -74,6 +77,7 @@ where | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[cfg(feature = "server")] | ||||
|     pub fn disable_keep_alive(&mut self) { | ||||
|         self.conn.disable_keep_alive(); | ||||
|         if self.conn.is_write_closed() { | ||||
| @@ -441,84 +445,86 @@ impl<'a, T> Drop for OptGuard<'a, T> { | ||||
|  | ||||
| // ===== impl Server ===== | ||||
|  | ||||
| impl<S, B> Server<S, B> | ||||
| where | ||||
|     S: HttpService<B>, | ||||
| { | ||||
|     pub fn new(service: S) -> Server<S, B> { | ||||
|         Server { | ||||
|             in_flight: Box::pin(None), | ||||
|             service, | ||||
| cfg_server! { | ||||
|     impl<S, B> Server<S, B> | ||||
|     where | ||||
|         S: HttpService<B>, | ||||
|     { | ||||
|         pub fn new(service: S) -> Server<S, B> { | ||||
|             Server { | ||||
|                 in_flight: Box::pin(None), | ||||
|                 service, | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         pub fn into_service(self) -> S { | ||||
|             self.service | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn into_service(self) -> S { | ||||
|         self.service | ||||
|     } | ||||
| } | ||||
|     // Service is never pinned | ||||
|     impl<S: HttpService<B>, B> Unpin for Server<S, B> {} | ||||
|  | ||||
| // Service is never pinned | ||||
| impl<S: HttpService<B>, B> Unpin for Server<S, B> {} | ||||
|     impl<S, Bs> Dispatch for Server<S, Body> | ||||
|     where | ||||
|         S: HttpService<Body, ResBody = Bs>, | ||||
|         S::Error: Into<Box<dyn StdError + Send + Sync>>, | ||||
|         Bs: HttpBody, | ||||
|     { | ||||
|         type PollItem = MessageHead<http::StatusCode>; | ||||
|         type PollBody = Bs; | ||||
|         type PollError = S::Error; | ||||
|         type RecvItem = RequestHead; | ||||
|  | ||||
| impl<S, Bs> Dispatch for Server<S, Body> | ||||
| where | ||||
|     S: HttpService<Body, ResBody = Bs>, | ||||
|     S::Error: Into<Box<dyn StdError + Send + Sync>>, | ||||
|     Bs: HttpBody, | ||||
| { | ||||
|     type PollItem = MessageHead<StatusCode>; | ||||
|     type PollBody = Bs; | ||||
|     type PollError = S::Error; | ||||
|     type RecvItem = RequestHead; | ||||
|  | ||||
|     fn poll_msg( | ||||
|         mut self: Pin<&mut Self>, | ||||
|         cx: &mut task::Context<'_>, | ||||
|     ) -> Poll<Option<Result<(Self::PollItem, Self::PollBody), Self::PollError>>> { | ||||
|         let mut this = self.as_mut(); | ||||
|         let ret = if let Some(ref mut fut) = this.in_flight.as_mut().as_pin_mut() { | ||||
|             let resp = ready!(fut.as_mut().poll(cx)?); | ||||
|             let (parts, body) = resp.into_parts(); | ||||
|             let head = MessageHead { | ||||
|                 version: parts.version, | ||||
|                 subject: parts.status, | ||||
|                 headers: parts.headers, | ||||
|         fn poll_msg( | ||||
|             mut self: Pin<&mut Self>, | ||||
|             cx: &mut task::Context<'_>, | ||||
|         ) -> Poll<Option<Result<(Self::PollItem, Self::PollBody), Self::PollError>>> { | ||||
|             let mut this = self.as_mut(); | ||||
|             let ret = if let Some(ref mut fut) = this.in_flight.as_mut().as_pin_mut() { | ||||
|                 let resp = ready!(fut.as_mut().poll(cx)?); | ||||
|                 let (parts, body) = resp.into_parts(); | ||||
|                 let head = MessageHead { | ||||
|                     version: parts.version, | ||||
|                     subject: parts.status, | ||||
|                     headers: parts.headers, | ||||
|                 }; | ||||
|                 Poll::Ready(Some(Ok((head, body)))) | ||||
|             } else { | ||||
|                 unreachable!("poll_msg shouldn't be called if no inflight"); | ||||
|             }; | ||||
|             Poll::Ready(Some(Ok((head, body)))) | ||||
|         } else { | ||||
|             unreachable!("poll_msg shouldn't be called if no inflight"); | ||||
|         }; | ||||
|  | ||||
|         // Since in_flight finished, remove it | ||||
|         this.in_flight.set(None); | ||||
|         ret | ||||
|     } | ||||
|  | ||||
|     fn recv_msg(&mut self, msg: crate::Result<(Self::RecvItem, Body)>) -> crate::Result<()> { | ||||
|         let (msg, body) = msg?; | ||||
|         let mut req = Request::new(body); | ||||
|         *req.method_mut() = msg.subject.0; | ||||
|         *req.uri_mut() = msg.subject.1; | ||||
|         *req.headers_mut() = msg.headers; | ||||
|         *req.version_mut() = msg.version; | ||||
|         let fut = self.service.call(req); | ||||
|         self.in_flight.set(Some(fut)); | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), ()>> { | ||||
|         if self.in_flight.is_some() { | ||||
|             Poll::Pending | ||||
|         } else { | ||||
|             self.service.poll_ready(cx).map_err(|_e| { | ||||
|                 // FIXME: return error value. | ||||
|                 trace!("service closed"); | ||||
|             }) | ||||
|             // Since in_flight finished, remove it | ||||
|             this.in_flight.set(None); | ||||
|             ret | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn should_poll(&self) -> bool { | ||||
|         self.in_flight.is_some() | ||||
|         fn recv_msg(&mut self, msg: crate::Result<(Self::RecvItem, Body)>) -> crate::Result<()> { | ||||
|             let (msg, body) = msg?; | ||||
|             let mut req = Request::new(body); | ||||
|             *req.method_mut() = msg.subject.0; | ||||
|             *req.uri_mut() = msg.subject.1; | ||||
|             *req.headers_mut() = msg.headers; | ||||
|             *req.version_mut() = msg.version; | ||||
|             let fut = self.service.call(req); | ||||
|             self.in_flight.set(Some(fut)); | ||||
|             Ok(()) | ||||
|         } | ||||
|  | ||||
|         fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), ()>> { | ||||
|             if self.in_flight.is_some() { | ||||
|                 Poll::Pending | ||||
|             } else { | ||||
|                 self.service.poll_ready(cx).map_err(|_e| { | ||||
|                     // FIXME: return error value. | ||||
|                     trace!("service closed"); | ||||
|                 }) | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         fn should_poll(&self) -> bool { | ||||
|             self.in_flight.is_some() | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -35,6 +35,7 @@ enum Kind { | ||||
|     /// | ||||
|     /// This is mostly only used with HTTP/1.0 with a length. This kind requires | ||||
|     /// the connection to be closed when the body is finished. | ||||
|     #[cfg(feature = "server")] | ||||
|     CloseDelimited, | ||||
| } | ||||
|  | ||||
| @@ -61,6 +62,7 @@ impl Encoder { | ||||
|         Encoder::new(Kind::Length(len)) | ||||
|     } | ||||
|  | ||||
|     #[cfg(feature = "server")] | ||||
|     pub fn close_delimited() -> Encoder { | ||||
|         Encoder::new(Kind::CloseDelimited) | ||||
|     } | ||||
| @@ -72,6 +74,7 @@ impl Encoder { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[cfg(feature = "server")] | ||||
|     pub fn set_last(mut self, is_last: bool) -> Self { | ||||
|         self.is_last = is_last; | ||||
|         self | ||||
| @@ -83,6 +86,7 @@ impl Encoder { | ||||
|  | ||||
|     pub fn is_close_delimited(&self) -> bool { | ||||
|         match self.kind { | ||||
|             #[cfg(feature = "server")] | ||||
|             Kind::CloseDelimited => true, | ||||
|             _ => false, | ||||
|         } | ||||
| @@ -94,6 +98,7 @@ impl Encoder { | ||||
|             Kind::Chunked => Ok(Some(EncodedBuf { | ||||
|                 kind: BufKind::ChunkedEnd(b"0\r\n\r\n"), | ||||
|             })), | ||||
|             #[cfg(feature = "server")] | ||||
|             Kind::CloseDelimited => Ok(None), | ||||
|             Kind::Length(_) => Err(NotEof), | ||||
|         } | ||||
| @@ -125,6 +130,7 @@ impl Encoder { | ||||
|                     BufKind::Exact(msg) | ||||
|                 } | ||||
|             } | ||||
|             #[cfg(feature = "server")] | ||||
|             Kind::CloseDelimited => { | ||||
|                 trace!("close delimited write {}B", len); | ||||
|                 BufKind::Exact(msg) | ||||
| @@ -168,6 +174,7 @@ impl Encoder { | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             #[cfg(feature = "server")] | ||||
|             Kind::CloseDelimited => { | ||||
|                 trace!("close delimited write {}B", len); | ||||
|                 dst.buffer(msg); | ||||
|   | ||||
| @@ -66,6 +66,7 @@ where | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[cfg(feature = "server")] | ||||
|     pub fn set_flush_pipeline(&mut self, enabled: bool) { | ||||
|         debug_assert!(!self.write_buf.has_remaining()); | ||||
|         self.flush_pipeline = enabled; | ||||
|   | ||||
| @@ -18,12 +18,15 @@ mod encode; | ||||
| mod io; | ||||
| mod role; | ||||
|  | ||||
| pub(crate) type ServerTransaction = role::Server; | ||||
|  | ||||
| cfg_client! { | ||||
|     pub(crate) type ClientTransaction = role::Client; | ||||
| } | ||||
|  | ||||
| cfg_server! { | ||||
|     pub(crate) type ServerTransaction = role::Server; | ||||
| } | ||||
|  | ||||
| pub(crate) trait Http1Transaction { | ||||
|     type Incoming; | ||||
|     type Outgoing: Default; | ||||
| @@ -73,6 +76,7 @@ pub(crate) struct ParseContext<'a> { | ||||
| pub(crate) struct Encode<'a, T> { | ||||
|     head: &'a mut MessageHead<T>, | ||||
|     body: Option<BodyLength>, | ||||
|     #[cfg(feature = "server")] | ||||
|     keep_alive: bool, | ||||
|     req_method: &'a mut Option<Method>, | ||||
|     title_case_headers: bool, | ||||
|   | ||||
| @@ -10,6 +10,7 @@ use http::header::{self, Entry, HeaderName, HeaderValue}; | ||||
| use http::{HeaderMap, Method, StatusCode, Version}; | ||||
|  | ||||
| use crate::body::DecodedLength; | ||||
| #[cfg(feature = "server")] | ||||
| use crate::common::date; | ||||
| use crate::error::Parse; | ||||
| use crate::headers; | ||||
| @@ -94,10 +95,13 @@ where | ||||
|  | ||||
| // There are 2 main roles, Client and Server. | ||||
|  | ||||
| #[cfg(feature = "client")] | ||||
| pub(crate) enum Client {} | ||||
|  | ||||
| #[cfg(feature = "server")] | ||||
| pub(crate) enum Server {} | ||||
|  | ||||
| #[cfg(feature = "server")] | ||||
| impl Http1Transaction for Server { | ||||
|     type Incoming = RequestLine; | ||||
|     type Outgoing = StatusCode; | ||||
| @@ -618,6 +622,7 @@ impl Http1Transaction for Server { | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "server")] | ||||
| impl Server { | ||||
|     fn can_have_body(method: &Option<Method>, status: StatusCode) -> bool { | ||||
|         Server::can_chunked(method, status) | ||||
| @@ -640,6 +645,7 @@ impl Server { | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "client")] | ||||
| impl Http1Transaction for Client { | ||||
|     type Incoming = StatusCode; | ||||
|     type Outgoing = RequestLine; | ||||
| @@ -779,6 +785,7 @@ impl Http1Transaction for Client { | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "client")] | ||||
| impl Client { | ||||
|     /// Returns Some(length, wants_upgrade) if successful. | ||||
|     /// | ||||
| @@ -846,9 +853,6 @@ impl Client { | ||||
|             Ok(Some((DecodedLength::CLOSE_DELIMITED, false))) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Client { | ||||
|     fn set_length(head: &mut RequestHead, body: Option<BodyLength>) -> Encoder { | ||||
|         let body = if let Some(body) = body { | ||||
|             body | ||||
|   | ||||
| @@ -14,14 +14,16 @@ use crate::common::{task, Future, Pin, Poll}; | ||||
| use crate::headers::content_length_parse_all; | ||||
|  | ||||
| pub(crate) mod ping; | ||||
| pub(crate) mod server; | ||||
|  | ||||
| cfg_client! { | ||||
|     pub(crate) mod client; | ||||
|     pub(crate) use self::client::ClientTask; | ||||
| } | ||||
|  | ||||
| pub(crate) use self::server::Server; | ||||
| cfg_server! { | ||||
|     pub(crate) mod server; | ||||
|     pub(crate) use self::server::Server; | ||||
| } | ||||
|  | ||||
| /// Default initial stream window size defined in HTTP2 spec. | ||||
| pub(crate) const SPEC_WINDOW_SIZE: u32 = 65_535; | ||||
|   | ||||
| @@ -3,10 +3,12 @@ | ||||
| cfg_http1! { | ||||
|     pub(crate) mod h1; | ||||
|  | ||||
|     pub(crate) use self::h1::{Conn, ServerTransaction}; | ||||
|     pub(crate) use self::h1::Conn; | ||||
|  | ||||
|     #[cfg(feature = "client")] | ||||
|     pub(crate) use self::h1::dispatch; | ||||
|     #[cfg(feature = "server")] | ||||
|     pub(crate) use self::h1::ServerTransaction; | ||||
| } | ||||
|  | ||||
| cfg_http2! { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user