refactor(lib): Switch from pin-project to pin-project-lite
This commit is contained in:
committed by
Sean McArthur
parent
9dff00425d
commit
43412a950f
@@ -9,7 +9,7 @@
|
||||
#[cfg(feature = "stream")]
|
||||
use futures_core::Stream;
|
||||
#[cfg(feature = "stream")]
|
||||
use pin_project::pin_project;
|
||||
use pin_project_lite::pin_project;
|
||||
|
||||
use crate::common::{
|
||||
task::{self, Poll},
|
||||
@@ -86,8 +86,12 @@ pub fn from_stream<S, IO, E>(stream: S) -> impl Accept<Conn = IO, Error = E>
|
||||
where
|
||||
S: Stream<Item = Result<IO, E>>,
|
||||
{
|
||||
#[pin_project]
|
||||
struct FromStream<S>(#[pin] S);
|
||||
pin_project! {
|
||||
struct FromStream<S> {
|
||||
#[pin]
|
||||
stream: S,
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, IO, E> Accept for FromStream<S>
|
||||
where
|
||||
@@ -99,9 +103,9 @@ where
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut task::Context<'_>,
|
||||
) -> Poll<Option<Result<Self::Conn, Self::Error>>> {
|
||||
self.project().0.poll_next(cx)
|
||||
self.project().stream.poll_next(cx)
|
||||
}
|
||||
}
|
||||
|
||||
FromStream(stream)
|
||||
FromStream { stream }
|
||||
}
|
||||
|
||||
@@ -45,7 +45,6 @@
|
||||
|
||||
use std::error::Error as StdError;
|
||||
use std::fmt;
|
||||
#[cfg(feature = "http1")]
|
||||
use std::marker::PhantomData;
|
||||
#[cfg(feature = "tcp")]
|
||||
use std::net::SocketAddr;
|
||||
@@ -53,7 +52,7 @@ use std::net::SocketAddr;
|
||||
use std::time::Duration;
|
||||
|
||||
use bytes::Bytes;
|
||||
use pin_project::pin_project;
|
||||
use pin_project_lite::pin_project;
|
||||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
|
||||
use super::accept::Accept;
|
||||
@@ -109,77 +108,85 @@ enum ConnectionMode {
|
||||
Fallback,
|
||||
}
|
||||
|
||||
/// A stream mapping incoming IOs to new services.
|
||||
///
|
||||
/// Yields `Connecting`s that are futures that should be put on a reactor.
|
||||
#[must_use = "streams do nothing unless polled"]
|
||||
#[pin_project]
|
||||
#[derive(Debug)]
|
||||
pub(super) struct Serve<I, S, E = Exec> {
|
||||
#[pin]
|
||||
incoming: I,
|
||||
make_service: S,
|
||||
protocol: Http<E>,
|
||||
}
|
||||
|
||||
/// A future building a new `Service` to a `Connection`.
|
||||
///
|
||||
/// Wraps the future returned from `MakeService` into one that returns
|
||||
/// a `Connection`.
|
||||
#[must_use = "futures do nothing unless polled"]
|
||||
#[pin_project]
|
||||
#[derive(Debug)]
|
||||
pub struct Connecting<I, F, E = Exec> {
|
||||
#[pin]
|
||||
future: F,
|
||||
io: Option<I>,
|
||||
protocol: Http<E>,
|
||||
}
|
||||
|
||||
#[must_use = "futures do nothing unless polled"]
|
||||
#[pin_project]
|
||||
#[derive(Debug)]
|
||||
pub(super) struct SpawnAll<I, S, E> {
|
||||
// TODO: re-add `pub(super)` once rustdoc can handle this.
|
||||
//
|
||||
// See https://github.com/rust-lang/rust/issues/64705
|
||||
#[pin]
|
||||
pub(super) serve: Serve<I, S, E>,
|
||||
}
|
||||
|
||||
/// A future binding a connection with a Service.
|
||||
///
|
||||
/// Polling this future will drive HTTP forward.
|
||||
#[must_use = "futures do nothing unless polled"]
|
||||
#[pin_project]
|
||||
pub struct Connection<T, S, E = Exec>
|
||||
where
|
||||
S: HttpService<Body>,
|
||||
{
|
||||
pub(super) conn: Option<ProtoServer<T, S::ResBody, S, E>>,
|
||||
#[cfg(all(feature = "http1", feature = "http2"))]
|
||||
fallback: Fallback<E>,
|
||||
}
|
||||
|
||||
#[pin_project(project = ProtoServerProj)]
|
||||
pub(super) enum ProtoServer<T, B, S, E = Exec>
|
||||
where
|
||||
S: HttpService<Body>,
|
||||
B: HttpBody,
|
||||
{
|
||||
#[cfg(feature = "http1")]
|
||||
H1(
|
||||
pin_project! {
|
||||
/// A stream mapping incoming IOs to new services.
|
||||
///
|
||||
/// Yields `Connecting`s that are futures that should be put on a reactor.
|
||||
#[must_use = "streams do nothing unless polled"]
|
||||
#[derive(Debug)]
|
||||
pub(super) struct Serve<I, S, E = Exec> {
|
||||
#[pin]
|
||||
proto::h1::Dispatcher<
|
||||
proto::h1::dispatch::Server<S, Body>,
|
||||
B,
|
||||
T,
|
||||
proto::ServerTransaction,
|
||||
>,
|
||||
PhantomData<E>,
|
||||
),
|
||||
#[cfg(feature = "http2")]
|
||||
H2(#[pin] proto::h2::Server<Rewind<T>, S, B, E>),
|
||||
incoming: I,
|
||||
make_service: S,
|
||||
protocol: Http<E>,
|
||||
}
|
||||
}
|
||||
|
||||
pin_project! {
|
||||
/// A future building a new `Service` to a `Connection`.
|
||||
///
|
||||
/// Wraps the future returned from `MakeService` into one that returns
|
||||
/// a `Connection`.
|
||||
#[must_use = "futures do nothing unless polled"]
|
||||
#[derive(Debug)]
|
||||
pub struct Connecting<I, F, E = Exec> {
|
||||
#[pin]
|
||||
future: F,
|
||||
io: Option<I>,
|
||||
protocol: Http<E>,
|
||||
}
|
||||
}
|
||||
|
||||
pin_project! {
|
||||
#[must_use = "futures do nothing unless polled"]
|
||||
#[derive(Debug)]
|
||||
pub(super) struct SpawnAll<I, S, E> {
|
||||
// TODO: re-add `pub(super)` once rustdoc can handle this.
|
||||
//
|
||||
// See https://github.com/rust-lang/rust/issues/64705
|
||||
#[pin]
|
||||
pub(super) serve: Serve<I, S, E>,
|
||||
}
|
||||
}
|
||||
|
||||
pin_project! {
|
||||
/// A future binding a connection with a Service.
|
||||
///
|
||||
/// Polling this future will drive HTTP forward.
|
||||
#[must_use = "futures do nothing unless polled"]
|
||||
pub struct Connection<T, S, E = Exec>
|
||||
where
|
||||
S: HttpService<Body>,
|
||||
{
|
||||
pub(super) conn: Option<ProtoServer<T, S::ResBody, S, E>>,
|
||||
fallback: Fallback<E>,
|
||||
}
|
||||
}
|
||||
|
||||
pin_project! {
|
||||
#[project = ProtoServerProj]
|
||||
pub(super) enum ProtoServer<T, B, S, E = Exec>
|
||||
where
|
||||
S: HttpService<Body>,
|
||||
B: HttpBody,
|
||||
{
|
||||
#[cfg(feature = "http1")]
|
||||
H1 {
|
||||
#[pin]
|
||||
h1: proto::h1::Dispatcher<
|
||||
proto::h1::dispatch::Server<S, Body>,
|
||||
B,
|
||||
T,
|
||||
proto::ServerTransaction,
|
||||
>,
|
||||
_phantom: PhantomData<E>,
|
||||
},
|
||||
#[cfg(feature = "http2")]
|
||||
H2 {
|
||||
#[pin]
|
||||
h2: proto::h2::Server<Rewind<T>, S, B, E>,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "http1", feature = "http2"))]
|
||||
@@ -189,6 +196,10 @@ enum Fallback<E> {
|
||||
Http1Only,
|
||||
}
|
||||
|
||||
#[cfg(not(all(feature = "http1", feature = "http2")))]
|
||||
#[derive(Clone, Debug)]
|
||||
struct Fallback<E>(PhantomData<E>);
|
||||
|
||||
#[cfg(all(feature = "http1", feature = "http2"))]
|
||||
impl<E> Fallback<E> {
|
||||
fn to_h2(&self) -> bool {
|
||||
@@ -519,7 +530,10 @@ impl<E> Http<E> {
|
||||
conn.set_max_buf_size(max);
|
||||
}
|
||||
let sd = proto::h1::dispatch::Server::new(service);
|
||||
ProtoServer::H1(proto::h1::Dispatcher::new(sd, conn), PhantomData)
|
||||
ProtoServer::H1 {
|
||||
h1: proto::h1::Dispatcher::new(sd, conn),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
@@ -535,7 +549,7 @@ impl<E> Http<E> {
|
||||
let rewind_io = Rewind::new(io);
|
||||
let h2 =
|
||||
proto::h2::Server::new(rewind_io, service, &self.h2_builder, self.exec.clone());
|
||||
ProtoServer::H2(h2)
|
||||
ProtoServer::H2 { h2 }
|
||||
}
|
||||
};
|
||||
|
||||
@@ -590,14 +604,14 @@ where
|
||||
/// This should only be called while the `Connection` future is still
|
||||
/// pending. If called after `Connection::poll` has resolved, this does
|
||||
/// nothing.
|
||||
pub fn graceful_shutdown(self: Pin<&mut Self>) {
|
||||
match self.project().conn {
|
||||
pub fn graceful_shutdown(mut self: Pin<&mut Self>) {
|
||||
match self.conn {
|
||||
#[cfg(feature = "http1")]
|
||||
Some(ProtoServer::H1(ref mut h1, _)) => {
|
||||
Some(ProtoServer::H1 { ref mut h1, .. }) => {
|
||||
h1.disable_keep_alive();
|
||||
}
|
||||
#[cfg(feature = "http2")]
|
||||
Some(ProtoServer::H2(ref mut h2)) => {
|
||||
Some(ProtoServer::H2 { ref mut h2 }) => {
|
||||
h2.graceful_shutdown();
|
||||
}
|
||||
None => (),
|
||||
@@ -624,7 +638,7 @@ where
|
||||
pub fn try_into_parts(self) -> Option<Parts<I, S>> {
|
||||
match self.conn.unwrap() {
|
||||
#[cfg(feature = "http1")]
|
||||
ProtoServer::H1(h1, _) => {
|
||||
ProtoServer::H1 { h1, .. } => {
|
||||
let (io, read_buf, dispatch) = h1.into_inner();
|
||||
Some(Parts {
|
||||
io,
|
||||
@@ -634,7 +648,7 @@ where
|
||||
})
|
||||
}
|
||||
#[cfg(feature = "http2")]
|
||||
ProtoServer::H2(_h2) => None,
|
||||
ProtoServer::H2 { .. } => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -658,7 +672,7 @@ where
|
||||
loop {
|
||||
match *self.conn.as_mut().unwrap() {
|
||||
#[cfg(feature = "http1")]
|
||||
ProtoServer::H1(ref mut h1, _) => match ready!(h1.poll_without_shutdown(cx)) {
|
||||
ProtoServer::H1 { ref mut h1, .. } => match ready!(h1.poll_without_shutdown(cx)) {
|
||||
Ok(()) => return Poll::Ready(Ok(())),
|
||||
Err(e) => {
|
||||
#[cfg(feature = "http2")]
|
||||
@@ -674,7 +688,7 @@ where
|
||||
}
|
||||
},
|
||||
#[cfg(feature = "http2")]
|
||||
ProtoServer::H2(ref mut h2) => return Pin::new(h2).poll(cx).map_ok(|_| ()),
|
||||
ProtoServer::H2 { ref mut h2 } => return Pin::new(h2).poll(cx).map_ok(|_| ()),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -700,8 +714,8 @@ where
|
||||
let conn = self.conn.take();
|
||||
|
||||
let (io, read_buf, dispatch) = match conn.unwrap() {
|
||||
ProtoServer::H1(h1, _) => h1.into_inner(),
|
||||
ProtoServer::H2(_h2) => {
|
||||
ProtoServer::H1 { h1, .. } => h1.into_inner(),
|
||||
ProtoServer::H2 { .. } => {
|
||||
panic!("h2 cannot into_inner");
|
||||
}
|
||||
};
|
||||
@@ -714,7 +728,7 @@ where
|
||||
let h2 = proto::h2::Server::new(rewind_io, dispatch.into_service(), builder, exec.clone());
|
||||
|
||||
debug_assert!(self.conn.is_none());
|
||||
self.conn = Some(ProtoServer::H2(h2));
|
||||
self.conn = Some(ProtoServer::H2 { h2 });
|
||||
}
|
||||
|
||||
/// Enable this connection to support higher-level HTTP upgrades.
|
||||
@@ -948,9 +962,9 @@ 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),
|
||||
ProtoServerProj::H1 { h1, .. } => h1.poll(cx),
|
||||
#[cfg(feature = "http2")]
|
||||
ProtoServerProj::H2(s) => s.poll(cx),
|
||||
ProtoServerProj::H2 { h2 } => h2.poll(cx),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -964,7 +978,7 @@ pub(crate) mod spawn_all {
|
||||
use crate::common::exec::ConnStreamExec;
|
||||
use crate::common::{task, Future, Pin, Poll, Unpin};
|
||||
use crate::service::HttpService;
|
||||
use pin_project::pin_project;
|
||||
use pin_project_lite::pin_project;
|
||||
|
||||
// Used by `SpawnAll` to optionally watch a `Connection` future.
|
||||
//
|
||||
@@ -1009,23 +1023,36 @@ pub(crate) mod spawn_all {
|
||||
// Users cannot import this type, nor the associated `NewSvcExec`. Instead,
|
||||
// a blanket implementation for `Executor<impl Future>` is sufficient.
|
||||
|
||||
#[pin_project]
|
||||
#[allow(missing_debug_implementations)]
|
||||
pub struct NewSvcTask<I, N, S: HttpService<Body>, E, W: Watcher<I, S, E>> {
|
||||
#[pin]
|
||||
state: State<I, N, S, E, W>,
|
||||
pin_project! {
|
||||
#[allow(missing_debug_implementations)]
|
||||
pub struct NewSvcTask<I, N, S: HttpService<Body>, E, W: Watcher<I, S, E>> {
|
||||
#[pin]
|
||||
state: State<I, N, S, E, W>,
|
||||
}
|
||||
}
|
||||
|
||||
#[pin_project(project = StateProj)]
|
||||
pub(super) enum State<I, N, S: HttpService<Body>, E, W: Watcher<I, S, E>> {
|
||||
Connecting(#[pin] Connecting<I, N, E>, W),
|
||||
Connected(#[pin] W::Future),
|
||||
pin_project! {
|
||||
#[project = StateProj]
|
||||
pub(super) enum State<I, N, S: HttpService<Body>, E, W: Watcher<I, S, E>> {
|
||||
Connecting {
|
||||
#[pin]
|
||||
connecting: Connecting<I, N, E>,
|
||||
watcher: W,
|
||||
},
|
||||
Connected {
|
||||
#[pin]
|
||||
future: W::Future,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, N, S: HttpService<Body>, E, W: Watcher<I, S, E>> NewSvcTask<I, N, S, E, W> {
|
||||
pub(super) fn new(connecting: Connecting<I, N, E>, watcher: W) -> Self {
|
||||
NewSvcTask {
|
||||
state: State::Connecting(connecting, watcher),
|
||||
state: State::Connecting {
|
||||
connecting,
|
||||
watcher,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1052,7 +1079,10 @@ pub(crate) mod spawn_all {
|
||||
loop {
|
||||
let next = {
|
||||
match me.state.as_mut().project() {
|
||||
StateProj::Connecting(connecting, watcher) => {
|
||||
StateProj::Connecting {
|
||||
connecting,
|
||||
watcher,
|
||||
} => {
|
||||
let res = ready!(connecting.poll(cx));
|
||||
let conn = match res {
|
||||
Ok(conn) => conn,
|
||||
@@ -1062,10 +1092,10 @@ pub(crate) mod spawn_all {
|
||||
return Poll::Ready(());
|
||||
}
|
||||
};
|
||||
let connected = watcher.watch(conn.with_upgrades());
|
||||
State::Connected(connected)
|
||||
let future = watcher.watch(conn.with_upgrades());
|
||||
State::Connected { future }
|
||||
}
|
||||
StateProj::Connected(future) => {
|
||||
StateProj::Connected { future } => {
|
||||
return future.poll(cx).map(|res| {
|
||||
if let Err(err) = res {
|
||||
debug!("connection error: {}", err);
|
||||
@@ -1133,7 +1163,7 @@ mod upgrades {
|
||||
#[cfg(feature = "http1")]
|
||||
Ok(proto::Dispatched::Upgrade(pending)) => {
|
||||
match self.inner.conn.take() {
|
||||
Some(ProtoServer::H1(h1, _)) => {
|
||||
Some(ProtoServer::H1 { h1, .. }) => {
|
||||
let (io, buf, _) = h1.into_inner();
|
||||
pending.fulfill(Upgraded::new(io, buf));
|
||||
return Poll::Ready(Ok(()));
|
||||
|
||||
@@ -6,7 +6,7 @@ use std::net::{SocketAddr, TcpListener as StdTcpListener};
|
||||
#[cfg(feature = "tcp")]
|
||||
use std::time::Duration;
|
||||
|
||||
use pin_project::pin_project;
|
||||
use pin_project_lite::pin_project;
|
||||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
|
||||
use super::accept::Accept;
|
||||
@@ -21,16 +21,17 @@ use super::shutdown::{Graceful, GracefulWatcher};
|
||||
#[cfg(feature = "tcp")]
|
||||
use super::tcp::AddrIncoming;
|
||||
|
||||
/// A listening HTTP server that accepts connections in both HTTP1 and HTTP2 by default.
|
||||
///
|
||||
/// `Server` is a `Future` mapping a bound listener with a set of service
|
||||
/// handlers. It is built using the [`Builder`](Builder), and the future
|
||||
/// completes when the server has been shutdown. It should be run by an
|
||||
/// `Executor`.
|
||||
#[pin_project]
|
||||
pub struct Server<I, S, E = Exec> {
|
||||
#[pin]
|
||||
spawn_all: SpawnAll<I, S, E>,
|
||||
pin_project! {
|
||||
/// A listening HTTP server that accepts connections in both HTTP1 and HTTP2 by default.
|
||||
///
|
||||
/// `Server` is a `Future` mapping a bound listener with a set of service
|
||||
/// handlers. It is built using the [`Builder`](Builder), and the future
|
||||
/// completes when the server has been shutdown. It should be run by an
|
||||
/// `Executor`.
|
||||
pub struct Server<I, S, E = Exec> {
|
||||
#[pin]
|
||||
spawn_all: SpawnAll<I, S, E>,
|
||||
}
|
||||
}
|
||||
|
||||
/// A builder for a [`Server`](Server).
|
||||
|
||||
@@ -1,33 +1,36 @@
|
||||
use std::error::Error as StdError;
|
||||
|
||||
use pin_project::pin_project;
|
||||
use pin_project_lite::pin_project;
|
||||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
|
||||
use super::conn::{SpawnAll, UpgradeableConnection, Watcher};
|
||||
use super::accept::Accept;
|
||||
use super::conn::{SpawnAll, UpgradeableConnection, Watcher};
|
||||
use crate::body::{Body, HttpBody};
|
||||
use crate::common::drain::{self, Draining, Signal, Watch, Watching};
|
||||
use crate::common::exec::{ConnStreamExec, NewSvcExec};
|
||||
use crate::common::{task, Future, Pin, Poll, Unpin};
|
||||
use crate::service::{HttpService, MakeServiceRef};
|
||||
|
||||
#[allow(missing_debug_implementations)]
|
||||
#[pin_project]
|
||||
pub struct Graceful<I, S, F, E> {
|
||||
#[pin]
|
||||
state: State<I, S, F, E>,
|
||||
pin_project! {
|
||||
#[allow(missing_debug_implementations)]
|
||||
pub struct Graceful<I, S, F, E> {
|
||||
#[pin]
|
||||
state: State<I, S, F, E>,
|
||||
}
|
||||
}
|
||||
|
||||
#[pin_project(project = StateProj)]
|
||||
pub(super) enum State<I, S, F, E> {
|
||||
Running {
|
||||
drain: Option<(Signal, Watch)>,
|
||||
#[pin]
|
||||
spawn_all: SpawnAll<I, S, E>,
|
||||
#[pin]
|
||||
signal: F,
|
||||
},
|
||||
Draining(Draining),
|
||||
pin_project! {
|
||||
#[project = StateProj]
|
||||
pub(super) enum State<I, S, F, E> {
|
||||
Running {
|
||||
drain: Option<(Signal, Watch)>,
|
||||
#[pin]
|
||||
spawn_all: SpawnAll<I, S, E>,
|
||||
#[pin]
|
||||
signal: F,
|
||||
},
|
||||
Draining { draining: Draining },
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, S, F, E> Graceful<I, S, F, E> {
|
||||
@@ -71,14 +74,16 @@ where
|
||||
Poll::Ready(()) => {
|
||||
debug!("signal received, starting graceful shutdown");
|
||||
let sig = drain.take().expect("drain channel").0;
|
||||
State::Draining(sig.drain())
|
||||
State::Draining {
|
||||
draining: sig.drain(),
|
||||
}
|
||||
}
|
||||
Poll::Pending => {
|
||||
let watch = drain.as_ref().expect("drain channel").1.clone();
|
||||
return spawn_all.poll_watch(cx, &GracefulWatcher(watch));
|
||||
}
|
||||
},
|
||||
StateProj::Draining(ref mut draining) => {
|
||||
StateProj::Draining { ref mut draining } => {
|
||||
return Pin::new(draining).poll(cx).map(Ok);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,13 +229,14 @@ mod addr_stream {
|
||||
|
||||
use crate::common::{task, Pin, Poll};
|
||||
|
||||
/// A transport returned yieled by `AddrIncoming`.
|
||||
#[pin_project::pin_project]
|
||||
#[derive(Debug)]
|
||||
pub struct AddrStream {
|
||||
#[pin]
|
||||
inner: TcpStream,
|
||||
pub(super) remote_addr: SocketAddr,
|
||||
pin_project_lite::pin_project! {
|
||||
/// A transport returned yieled by `AddrIncoming`.
|
||||
#[derive(Debug)]
|
||||
pub struct AddrStream {
|
||||
#[pin]
|
||||
inner: TcpStream,
|
||||
pub(super) remote_addr: SocketAddr,
|
||||
}
|
||||
}
|
||||
|
||||
impl AddrStream {
|
||||
|
||||
Reference in New Issue
Block a user