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