refactor OpenWaitingConnection guard. use pin_project_lite

This commit is contained in:
fakeshadow 2021-02-08 13:32:22 -08:00
parent dddb623a11
commit 36830b98f6
20 changed files with 624 additions and 562 deletions

View File

@ -19,6 +19,7 @@
* `client::error::ConnectError` Resolver variant contains `Box<dyn std::error::Error>` type [#1905]
* `client::ConnectorConfig` default timeout changed to 5 seconds. [#1905]
* Simplify `BlockingError` type to a struct. It's only triggered with blocking thread pool is dead. [#1957]
* `body::ResponseBody` enum use named field.
### Removed
* `ResponseBuilder::set`; use `ResponseBuilder::insert_header`. [#1869]

View File

@ -69,7 +69,7 @@ language-tags = "0.2"
log = "0.4"
mime = "0.3"
percent-encoding = "2.1"
pin-project = "1.0.0"
pin-project-lite = "0.2"
rand = "0.8"
regex = "1.3"
serde = "1.0"

View File

@ -4,7 +4,7 @@ use std::{fmt, mem};
use bytes::{Bytes, BytesMut};
use futures_core::{ready, Stream};
use pin_project::pin_project;
use pin_project_lite::pin_project;
use crate::error::Error;
@ -63,31 +63,33 @@ impl<T: MessageBody + Unpin> MessageBody for Box<T> {
}
}
#[pin_project(project = ResponseBodyProj)]
pub enum ResponseBody<B> {
Body(#[pin] B),
Other(Body),
pin_project! {
#[project = ResponseBodyProj]
pub enum ResponseBody<B> {
Body { #[pin] body: B },
Other { body: Body }
}
}
impl ResponseBody<Body> {
pub fn into_body<B>(self) -> ResponseBody<B> {
match self {
ResponseBody::Body(b) => ResponseBody::Other(b),
ResponseBody::Other(b) => ResponseBody::Other(b),
ResponseBody::Body { body } => ResponseBody::Other { body },
ResponseBody::Other { body } => ResponseBody::Other { body },
}
}
}
impl<B> ResponseBody<B> {
pub fn take_body(&mut self) -> ResponseBody<B> {
std::mem::replace(self, ResponseBody::Other(Body::None))
std::mem::replace(self, ResponseBody::Other { body: Body::None })
}
}
impl<B: MessageBody> ResponseBody<B> {
pub fn as_ref(&self) -> Option<&B> {
if let ResponseBody::Body(ref b) = self {
Some(b)
if let ResponseBody::Body { body } = self {
Some(body)
} else {
None
}
@ -97,8 +99,8 @@ impl<B: MessageBody> ResponseBody<B> {
impl<B: MessageBody> MessageBody for ResponseBody<B> {
fn size(&self) -> BodySize {
match self {
ResponseBody::Body(ref body) => body.size(),
ResponseBody::Other(ref body) => body.size(),
ResponseBody::Body { body } => body.size(),
ResponseBody::Other { body } => body.size(),
}
}
@ -107,8 +109,8 @@ impl<B: MessageBody> MessageBody for ResponseBody<B> {
cx: &mut Context<'_>,
) -> Poll<Option<Result<Bytes, Error>>> {
match self.project() {
ResponseBodyProj::Body(body) => body.poll_next(cx),
ResponseBodyProj::Other(body) => Pin::new(body).poll_next(cx),
ResponseBodyProj::Body { body } => body.poll_next(cx),
ResponseBodyProj::Other { body } => Pin::new(body).poll_next(cx),
}
}
}
@ -121,8 +123,8 @@ impl<B: MessageBody> Stream for ResponseBody<B> {
cx: &mut Context<'_>,
) -> Poll<Option<Self::Item>> {
match self.project() {
ResponseBodyProj::Body(body) => body.poll_next(cx),
ResponseBodyProj::Other(body) => Pin::new(body).poll_next(cx),
ResponseBodyProj::Body { body } => body.poll_next(cx),
ResponseBodyProj::Other { body } => Pin::new(body).poll_next(cx),
}
}
}
@ -468,8 +470,8 @@ mod tests {
impl ResponseBody<Body> {
pub(crate) fn get_ref(&self) -> &[u8] {
match *self {
ResponseBody::Body(ref b) => b.get_ref(),
ResponseBody::Other(ref b) => b.get_ref(),
ResponseBody::Body { ref body } => body.get_ref(),
ResponseBody::Other { ref body } => body.get_ref(),
}
}
}

View File

@ -10,7 +10,7 @@ use bytes::Bytes;
use futures_core::future::LocalBoxFuture;
use futures_util::future::{err, Either, FutureExt, Ready};
use h2::client::SendRequest;
use pin_project::pin_project;
use pin_project_lite::pin_project;
use crate::body::MessageBody;
use crate::h1::ClientCodec;
@ -245,25 +245,30 @@ where
EitherConnection::A(con) => con
.open_tunnel(head)
.map(|res| {
res.map(|(head, framed)| (head, framed.into_map_io(EitherIo::A)))
res.map(|(head, framed)| {
(head, framed.into_map_io(|a| EitherIo::A { a }))
})
})
.boxed_local(),
EitherConnection::B(con) => con
.open_tunnel(head)
.map(|res| {
res.map(|(head, framed)| (head, framed.into_map_io(EitherIo::B)))
res.map(|(head, framed)| {
(head, framed.into_map_io(|b| EitherIo::B { b }))
})
})
.boxed_local(),
}
}
}
#[pin_project(project = EitherIoProj)]
pub enum EitherIo<A, B> {
A(#[pin] A),
B(#[pin] B),
pin_project! {
#[project = EitherIoProj]
pub enum EitherIo<A, B> {
A { #[pin] a: A },
B { #[pin] b: B}
}
}
impl<A, B> AsyncRead for EitherIo<A, B>
where
A: AsyncRead,
@ -275,8 +280,8 @@ where
buf: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
match self.project() {
EitherIoProj::A(val) => val.poll_read(cx, buf),
EitherIoProj::B(val) => val.poll_read(cx, buf),
EitherIoProj::A { a } => a.poll_read(cx, buf),
EitherIoProj::B { b } => b.poll_read(cx, buf),
}
}
}
@ -292,15 +297,15 @@ where
buf: &[u8],
) -> Poll<io::Result<usize>> {
match self.project() {
EitherIoProj::A(val) => val.poll_write(cx, buf),
EitherIoProj::B(val) => val.poll_write(cx, buf),
EitherIoProj::A { a } => a.poll_write(cx, buf),
EitherIoProj::B { b } => b.poll_write(cx, buf),
}
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
match self.project() {
EitherIoProj::A(val) => val.poll_flush(cx),
EitherIoProj::B(val) => val.poll_flush(cx),
EitherIoProj::A { a } => a.poll_flush(cx),
EitherIoProj::B { b } => b.poll_flush(cx),
}
}
@ -309,8 +314,8 @@ where
cx: &mut Context<'_>,
) -> Poll<io::Result<()>> {
match self.project() {
EitherIoProj::A(val) => val.poll_shutdown(cx),
EitherIoProj::B(val) => val.poll_shutdown(cx),
EitherIoProj::A { a } => a.poll_shutdown(cx),
EitherIoProj::B { b } => b.poll_shutdown(cx),
}
}
}

View File

@ -416,6 +416,7 @@ mod connect_impl {
use futures_core::ready;
use futures_util::future::Either;
use pin_project_lite::pin_project;
use super::*;
use crate::client::connection::EitherConnection;
@ -478,15 +479,20 @@ mod connect_impl {
}
}
#[pin_project::pin_project]
pub(crate) struct InnerConnectorResponseA<T, Io1, Io2>
where
Io1: AsyncRead + AsyncWrite + Unpin + 'static,
T: Service<Connect, Response = (Io1, Protocol), Error = ConnectError> + 'static,
{
#[pin]
fut: <ConnectionPool<T, Io1> as Service<Connect>>::Future,
_phantom: PhantomData<Io2>,
pin_project! {
pub(crate) struct InnerConnectorResponseA<T, Io1, Io2>
where
Io1: AsyncRead,
Io1: AsyncWrite,
Io1: Unpin,
Io1: 'static,
T: Service<Connect, Response = (Io1, Protocol), Error = ConnectError>,
T: 'static
{
#[pin]
fut: <ConnectionPool<T, Io1> as Service<Connect>>::Future,
_phantom: PhantomData<Io2>
}
}
impl<T, Io1, Io2> Future for InnerConnectorResponseA<T, Io1, Io2>
@ -505,15 +511,20 @@ mod connect_impl {
}
}
#[pin_project::pin_project]
pub(crate) struct InnerConnectorResponseB<T, Io1, Io2>
where
Io2: AsyncRead + AsyncWrite + Unpin + 'static,
T: Service<Connect, Response = (Io2, Protocol), Error = ConnectError> + 'static,
{
#[pin]
fut: <ConnectionPool<T, Io2> as Service<Connect>>::Future,
_phantom: PhantomData<Io1>,
pin_project! {
pub(crate) struct InnerConnectorResponseB<T, Io1, Io2>
where
Io2: AsyncRead,
Io2: AsyncWrite,
Io2: Unpin,
Io2: 'static,
T: Service<Connect, Response = (Io2, Protocol), Error = ConnectError>,
T: 'static
{
#[pin]
fut: <ConnectionPool<T, Io2> as Service<Connect>>::Future,
_phantom: PhantomData<Io1>
}
}
impl<T, Io1, Io2> Future for InnerConnectorResponseB<T, Io1, Io2>

View File

@ -9,6 +9,7 @@ use bytes::{Bytes, BytesMut};
use futures_core::Stream;
use futures_util::future::poll_fn;
use futures_util::{pin_mut, SinkExt, StreamExt};
use pin_project_lite::pin_project;
use crate::error::PayloadError;
use crate::h1;
@ -237,10 +238,11 @@ impl<T: AsyncRead + AsyncWrite + Unpin + 'static> AsyncWrite for H1Connection<T>
}
}
#[pin_project::pin_project]
pub(crate) struct PlStream<Io> {
#[pin]
framed: Option<Framed<Io, h1::ClientPayloadCodec>>,
pin_project! {
pub(crate) struct PlStream<Io> {
#[pin]
framed: Option<Framed<Io, h1::ClientPayloadCodec>>,
}
}
impl<Io: ConnectionLifetime> PlStream<Io> {

View File

@ -11,14 +11,12 @@ use actix_rt::time::{sleep, Sleep};
use actix_service::Service;
use actix_utils::task::LocalWaker;
use ahash::AHashMap;
use bytes::Bytes;
use futures_channel::oneshot;
use futures_core::future::LocalBoxFuture;
use futures_util::future::{poll_fn, FutureExt};
use h2::client::{Connection, SendRequest};
use http::uri::Authority;
use indexmap::IndexSet;
use pin_project::pin_project;
use pin_project_lite::pin_project;
use slab::Slab;
use super::config::ConnectorConfig;
@ -386,11 +384,12 @@ where
}
}
#[pin_project::pin_project]
struct CloseConnection<T> {
io: T,
#[pin]
timeout: Sleep,
pin_project! {
struct CloseConnection<T> {
io: T,
#[pin]
timeout: Sleep
}
}
impl<T> CloseConnection<T>
@ -424,7 +423,6 @@ where
}
}
#[pin_project]
struct ConnectorPoolSupport<T, Io>
where
Io: AsyncRead + AsyncWrite + Unpin + 'static,
@ -442,9 +440,9 @@ where
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project();
let this = self.get_mut();
if Rc::strong_count(this.inner) == 1 {
if Rc::strong_count(&this.inner) == 1 {
// If we are last copy of Inner<Io> it means the ConnectionPool is already gone
// and we are safe to exit.
return Poll::Ready(());
@ -498,55 +496,75 @@ where
}
}
#[pin_project::pin_project(PinnedDrop)]
struct OpenWaitingConnection<F, Io>
struct OpenWaitingConnection<Io>
where
Io: AsyncRead + AsyncWrite + Unpin + 'static,
{
#[pin]
fut: F,
key: Key,
h2: Option<
LocalBoxFuture<
'static,
Result<(SendRequest<Bytes>, Connection<Io, Bytes>), h2::Error>,
>,
>,
rx: Option<oneshot::Sender<Result<IoConnection<Io>, ConnectError>>>,
inner: Option<Rc<RefCell<Inner<Io>>>>,
config: ConnectorConfig,
}
impl<F, Io> OpenWaitingConnection<F, Io>
impl<Io> OpenWaitingConnection<Io>
where
F: Future<Output = Result<(Io, Protocol), ConnectError>> + 'static,
Io: AsyncRead + AsyncWrite + Unpin + 'static,
{
fn spawn(
fn spawn<F>(
key: Key,
rx: oneshot::Sender<Result<IoConnection<Io>, ConnectError>>,
inner: Rc<RefCell<Inner<Io>>>,
fut: F,
config: ConnectorConfig,
) {
actix_rt::spawn(OpenWaitingConnection {
key,
fut,
h2: None,
rx: Some(rx),
inner: Some(inner),
config,
) where
F: Future<Output = Result<(Io, Protocol), ConnectError>> + 'static,
{
// OpenWaitingConnection would guard the spawn task and release
// permission/wake up support future when spawn task is canceled/generated error.
let mut guard = OpenWaitingConnection { inner: Some(inner) };
actix_rt::spawn(async move {
let (io, proto) = match fut.await {
Ok((io, proto)) => (io, proto),
Err(e) => {
let _ = Option::take(&mut guard.inner);
let _ = rx.send(Err(e));
return;
}
};
match proto {
Protocol::Http1 => {
let inner = Option::take(&mut guard.inner);
let _ = rx.send(Ok(IoConnection::new(
ConnectionType::H1(io),
Instant::now(),
Some(Acquired(key, inner)),
)));
}
_ => match handshake(io, &config).await {
Ok((sender, connection)) => {
let inner = Option::take(&mut guard.inner);
let _ = rx.send(Ok(IoConnection::new(
ConnectionType::H2(H2Connection::new(sender, connection)),
Instant::now(),
Some(Acquired(key, inner)),
)));
}
Err(err) => {
let _ = Option::take(&mut guard.inner);
let _ = rx.send(Err(ConnectError::H2(err)));
}
},
}
});
}
}
#[pin_project::pinned_drop]
impl<F, Io> PinnedDrop for OpenWaitingConnection<F, Io>
impl<Io> Drop for OpenWaitingConnection<Io>
where
Io: AsyncRead + AsyncWrite + Unpin + 'static,
{
fn drop(self: Pin<&mut Self>) {
if let Some(inner) = self.project().inner.take() {
fn drop(&mut self) {
// if inner is some it means OpenWaitingConnection did not finish
// it's task. release permission and try to wake up support future.
if let Some(inner) = self.inner.take() {
let mut inner = inner.as_ref().borrow_mut();
inner.release();
inner.check_availability();
@ -554,65 +572,6 @@ where
}
}
impl<F, Io> Future for OpenWaitingConnection<F, Io>
where
F: Future<Output = Result<(Io, Protocol), ConnectError>>,
Io: AsyncRead + AsyncWrite + Unpin,
{
type Output = ();
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.as_mut().project();
if let Some(ref mut h2) = this.h2 {
return match Pin::new(h2).poll(cx) {
Poll::Ready(Ok((sender, connection))) => {
let rx = this.rx.take().unwrap();
let _ = rx.send(Ok(IoConnection::new(
ConnectionType::H2(H2Connection::new(sender, connection)),
Instant::now(),
Some(Acquired(this.key.clone(), this.inner.take())),
)));
Poll::Ready(())
}
Poll::Pending => Poll::Pending,
Poll::Ready(Err(err)) => {
let _ = this.inner.take();
if let Some(rx) = this.rx.take() {
let _ = rx.send(Err(ConnectError::H2(err)));
}
Poll::Ready(())
}
};
}
match this.fut.poll(cx) {
Poll::Ready(Err(err)) => {
let _ = this.inner.take();
if let Some(rx) = this.rx.take() {
let _ = rx.send(Err(err));
}
Poll::Ready(())
}
Poll::Ready(Ok((io, proto))) => {
if proto == Protocol::Http1 {
let rx = this.rx.take().unwrap();
let _ = rx.send(Ok(IoConnection::new(
ConnectionType::H1(io),
Instant::now(),
Some(Acquired(this.key.clone(), this.inner.take())),
)));
Poll::Ready(())
} else {
*this.h2 = Some(handshake(io, this.config).boxed_local());
self.poll(cx)
}
}
Poll::Pending => Poll::Pending,
}
}
}
pub(crate) struct Acquired<T>(Key, Option<Rc<RefCell<Inner<T>>>>);
impl<T> Acquired<T>

View File

@ -9,7 +9,7 @@ use brotli2::write::BrotliEncoder;
use bytes::Bytes;
use flate2::write::{GzEncoder, ZlibEncoder};
use futures_core::ready;
use pin_project::pin_project;
use pin_project_lite::pin_project;
use crate::body::{Body, BodySize, MessageBody, ResponseBody};
use crate::http::header::{ContentEncoding, CONTENT_ENCODING};
@ -21,13 +21,14 @@ use crate::error::BlockingError;
const INPLACE: usize = 1024;
#[pin_project]
pub struct Encoder<B> {
eof: bool,
#[pin]
body: EncoderBody<B>,
encoder: Option<ContentEncoder>,
fut: Option<JoinHandle<Result<ContentEncoder, io::Error>>>,
pin_project! {
pub struct Encoder<B> {
eof: bool,
#[pin]
body: EncoderBody<B>,
encoder: Option<ContentEncoder>,
fut: Option<JoinHandle<Result<ContentEncoder, io::Error>>>,
}
}
impl<B: MessageBody> Encoder<B> {
@ -43,19 +44,21 @@ impl<B: MessageBody> Encoder<B> {
|| encoding == ContentEncoding::Auto);
let body = match body {
ResponseBody::Other(b) => match b {
Body::None => return ResponseBody::Other(Body::None),
Body::Empty => return ResponseBody::Other(Body::Empty),
Body::Bytes(buf) => {
ResponseBody::Other { body } => match body {
Body::None => return ResponseBody::Other { body: Body::None },
Body::Empty => return ResponseBody::Other { body: Body::Empty },
Body::Bytes(bytes) => {
if can_encode {
EncoderBody::Bytes(buf)
EncoderBody::Bytes { bytes }
} else {
return ResponseBody::Other(Body::Bytes(buf));
return ResponseBody::Other {
body: Body::Bytes(bytes),
};
}
}
Body::Message(stream) => EncoderBody::BoxedStream(stream),
Body::Message(stream) => EncoderBody::BoxedStream { stream },
},
ResponseBody::Body(stream) => EncoderBody::Stream(stream),
ResponseBody::Body { body } => EncoderBody::Stream { stream: body },
};
if can_encode {
@ -63,36 +66,42 @@ impl<B: MessageBody> Encoder<B> {
if let Some(enc) = ContentEncoder::encoder(encoding) {
update_head(encoding, head);
head.no_chunking(false);
return ResponseBody::Body(Encoder {
body,
eof: false,
fut: None,
encoder: Some(enc),
});
return ResponseBody::Body {
body: Encoder {
body,
eof: false,
fut: None,
encoder: Some(enc),
},
};
}
}
ResponseBody::Body(Encoder {
body,
eof: false,
fut: None,
encoder: None,
})
ResponseBody::Body {
body: Encoder {
body,
eof: false,
fut: None,
encoder: None,
},
}
}
}
#[pin_project(project = EncoderBodyProj)]
enum EncoderBody<B> {
Bytes(Bytes),
Stream(#[pin] B),
BoxedStream(Box<dyn MessageBody + Unpin>),
pin_project! {
#[project = EncoderBodyProj]
enum EncoderBody<B> {
Bytes { bytes: Bytes },
Stream { #[pin] stream: B },
BoxedStream { stream: Box<dyn MessageBody + Unpin> }
}
}
impl<B: MessageBody> MessageBody for EncoderBody<B> {
fn size(&self) -> BodySize {
match self {
EncoderBody::Bytes(ref b) => b.size(),
EncoderBody::Stream(ref b) => b.size(),
EncoderBody::BoxedStream(ref b) => b.size(),
EncoderBody::Bytes { ref bytes } => bytes.size(),
EncoderBody::Stream { ref stream } => stream.size(),
EncoderBody::BoxedStream { ref stream } => stream.size(),
}
}
@ -101,16 +110,16 @@ impl<B: MessageBody> MessageBody for EncoderBody<B> {
cx: &mut Context<'_>,
) -> Poll<Option<Result<Bytes, Error>>> {
match self.project() {
EncoderBodyProj::Bytes(b) => {
if b.is_empty() {
EncoderBodyProj::Bytes { bytes } => {
if bytes.is_empty() {
Poll::Ready(None)
} else {
Poll::Ready(Some(Ok(std::mem::take(b))))
Poll::Ready(Some(Ok(std::mem::take(bytes))))
}
}
EncoderBodyProj::Stream(b) => b.poll_next(cx),
EncoderBodyProj::BoxedStream(ref mut b) => {
Pin::new(b.as_mut()).poll_next(cx)
EncoderBodyProj::Stream { stream } => stream.poll_next(cx),
EncoderBodyProj::BoxedStream { stream } => {
Pin::new(stream.as_mut()).poll_next(cx)
}
}
}

View File

@ -14,7 +14,7 @@ use actix_service::Service;
use bitflags::bitflags;
use bytes::{Buf, BytesMut};
use log::{error, trace};
use pin_project::pin_project;
use pin_project_lite::pin_project;
use crate::body::{Body, BodySize, MessageBody, ResponseBody};
use crate::config::ServiceConfig;
@ -45,70 +45,92 @@ bitflags! {
}
}
#[pin_project::pin_project]
/// Dispatcher for HTTP/1.1 protocol
pub struct Dispatcher<T, S, B, X, U>
where
S: Service<Request>,
S::Error: Into<Error>,
B: MessageBody,
X: Service<Request, Response = Request>,
X::Error: Into<Error>,
U: Service<(Request, Framed<T, Codec>), Response = ()>,
U::Error: fmt::Display,
{
#[pin]
inner: DispatcherState<T, S, B, X, U>,
#[cfg(test)]
poll_count: u64,
#[cfg(test)]
pin_project! {
/// Dispatcher for HTTP/1.1 protocol
pub struct Dispatcher<T, S, B, X, U>
where
S: Service<Request>,
S::Error: Into<Error>,
B: MessageBody,
X: Service<Request, Response = Request>,
X::Error: Into<Error>,
U: Service<(Request, Framed<T, Codec>), Response = ()>,
U::Error: fmt::Display
{
#[pin]
inner: DispatcherState<T, S, B, X, U>,
poll_count: u64
}
}
#[pin_project(project = DispatcherStateProj)]
enum DispatcherState<T, S, B, X, U>
where
S: Service<Request>,
S::Error: Into<Error>,
B: MessageBody,
X: Service<Request, Response = Request>,
X::Error: Into<Error>,
U: Service<(Request, Framed<T, Codec>), Response = ()>,
U::Error: fmt::Display,
{
Normal(#[pin] InnerDispatcher<T, S, B, X, U>),
Upgrade(#[pin] U::Future),
#[cfg(not(test))]
pin_project! {
/// Dispatcher for HTTP/1.1 protocol
pub struct Dispatcher<T, S, B, X, U>
where
S: Service<Request>,
S::Error: Into<Error>,
B: MessageBody,
X: Service<Request, Response = Request>,
X::Error: Into<Error>,
U: Service<(Request, Framed<T, Codec>), Response = ()>,
U::Error: fmt::Display
{
#[pin]
inner: DispatcherState<T, S, B, X, U>
}
}
#[pin_project(project = InnerDispatcherProj)]
struct InnerDispatcher<T, S, B, X, U>
where
S: Service<Request>,
S::Error: Into<Error>,
B: MessageBody,
X: Service<Request, Response = Request>,
X::Error: Into<Error>,
U: Service<(Request, Framed<T, Codec>), Response = ()>,
U::Error: fmt::Display,
{
flow: Rc<HttpFlow<S, X, U>>,
on_connect_data: OnConnectData,
flags: Flags,
peer_addr: Option<net::SocketAddr>,
error: Option<DispatchError>,
pin_project! {
#[project = DispatcherStateProj]
enum DispatcherState<T, S, B, X, U>
where
S: Service<Request>,
S::Error: Into<Error>,
B: MessageBody,
X: Service<Request, Response = Request>,
X::Error: Into<Error>,
U: Service<(Request, Framed<T, Codec>), Response = ()>,
U::Error: fmt::Display
{
Normal { #[pin] inner: InnerDispatcher<T, S, B, X, U>},
Upgrade { #[pin] upgrade: U::Future }
}
}
#[pin]
state: State<S, B, X>,
payload: Option<PayloadSender>,
messages: VecDeque<DispatcherMessage>,
pin_project! {
#[project = InnerDispatcherProj]
struct InnerDispatcher<T, S, B, X, U>
where
S: Service<Request>,
S::Error: Into<Error>,
B: MessageBody,
X: Service<Request, Response = Request>,
X::Error: Into<Error>,
U: Service<(Request, Framed<T, Codec>), Response = ()>,
U::Error: fmt::Display
{
flow: Rc<HttpFlow<S, X, U>>,
on_connect_data: OnConnectData,
flags: Flags,
peer_addr: Option<net::SocketAddr>,
error: Option<DispatchError>,
ka_expire: Instant,
#[pin]
ka_timer: Option<Sleep>,
#[pin]
state: State<S, B, X>,
payload: Option<PayloadSender>,
messages: VecDeque<DispatcherMessage>,
io: Option<T>,
read_buf: BytesMut,
write_buf: BytesMut,
codec: Codec,
ka_expire: Instant,
#[pin]
ka_timer: Option<Sleep>,
io: Option<T>,
read_buf: BytesMut,
write_buf: BytesMut,
codec: Codec
}
}
enum DispatcherMessage {
@ -117,17 +139,19 @@ enum DispatcherMessage {
Error(Response<()>),
}
#[pin_project(project = StateProj)]
enum State<S, B, X>
where
S: Service<Request>,
X: Service<Request, Response = Request>,
B: MessageBody,
{
None,
ExpectCall(#[pin] X::Future),
ServiceCall(#[pin] S::Future),
SendPayload(#[pin] ResponseBody<B>),
pin_project! {
#[project = StateProj]
enum State<S, B, X>
where
S: Service<Request>,
X: Service<Request, Response = Request>,
B: MessageBody
{
None,
ExpectCall { #[pin] fut: X::Future },
ServiceCall { #[pin] fut: S::Future },
SendPayload { #[pin] body: ResponseBody<B> }
}
}
impl<S, B, X> State<S, B, X>
@ -141,7 +165,7 @@ where
}
fn is_call(&self) -> bool {
matches!(self, State::ServiceCall(_))
matches!(self, State::ServiceCall { .. })
}
}
enum PollResponse {
@ -220,22 +244,24 @@ where
};
Dispatcher {
inner: DispatcherState::Normal(InnerDispatcher {
write_buf: BytesMut::with_capacity(HW_BUFFER_SIZE),
payload: None,
state: State::None,
error: None,
messages: VecDeque::new(),
io: Some(io),
codec,
read_buf,
flow: services,
on_connect_data,
flags,
peer_addr,
ka_expire,
ka_timer,
}),
inner: DispatcherState::Normal {
inner: InnerDispatcher {
write_buf: BytesMut::with_capacity(HW_BUFFER_SIZE),
payload: None,
state: State::None,
error: None,
messages: VecDeque::new(),
io: Some(io),
codec,
read_buf,
flow: services,
on_connect_data,
flags,
peer_addr,
ka_expire,
ka_timer,
},
},
#[cfg(test)]
poll_count: 0,
@ -337,7 +363,7 @@ where
this.flags.set(Flags::KEEPALIVE, this.codec.keepalive());
match body.size() {
BodySize::None | BodySize::Empty => this.state.set(State::None),
_ => this.state.set(State::SendPayload(body)),
_ => this.state.set(State::SendPayload { body }),
};
Ok(())
}
@ -363,8 +389,10 @@ where
true
}
Some(DispatcherMessage::Error(res)) => {
self.as_mut()
.send_response(res, ResponseBody::Other(Body::Empty))?;
self.as_mut().send_response(
res,
ResponseBody::Other { body: Body::Empty },
)?;
true
}
Some(DispatcherMessage::Upgrade(req)) => {
@ -372,12 +400,12 @@ where
}
None => false,
},
StateProj::ExpectCall(fut) => match fut.poll(cx) {
StateProj::ExpectCall { fut } => match fut.poll(cx) {
Poll::Ready(Ok(req)) => {
self.as_mut().send_continue();
this = self.as_mut().project();
let fut = this.flow.service.call(req);
this.state.set(State::ServiceCall(fut));
this.state.set(State::ServiceCall { fut });
continue;
}
Poll::Ready(Err(e)) => {
@ -388,7 +416,7 @@ where
}
Poll::Pending => false,
},
StateProj::ServiceCall(fut) => match fut.poll(cx) {
StateProj::ServiceCall { fut } => match fut.poll(cx) {
Poll::Ready(Ok(res)) => {
let (res, body) = res.into().replace_body(());
self.as_mut().send_response(res, body)?;
@ -402,10 +430,10 @@ where
}
Poll::Pending => false,
},
StateProj::SendPayload(mut stream) => {
StateProj::SendPayload { mut body } => {
loop {
if this.write_buf.len() < super::payload::MAX_BUFFER_SIZE {
match stream.as_mut().poll_next(cx) {
match body.as_mut().poll_next(cx) {
Poll::Ready(Some(Ok(item))) => {
this.codec.encode(
Message::Chunk(Some(item)),
@ -466,26 +494,26 @@ where
if req.head().expect() {
// set dispatcher state so the future is pinned.
let mut this = self.as_mut().project();
let task = this.flow.expect.call(req);
this.state.set(State::ExpectCall(task));
let fut = this.flow.expect.call(req);
this.state.set(State::ExpectCall { fut });
} else {
// the same as above.
let mut this = self.as_mut().project();
let task = this.flow.service.call(req);
this.state.set(State::ServiceCall(task));
let fut = this.flow.service.call(req);
this.state.set(State::ServiceCall { fut });
};
// eagerly poll the future for once(or twice if expect is resolved immediately).
loop {
match self.as_mut().project().state.project() {
StateProj::ExpectCall(fut) => {
StateProj::ExpectCall { fut } => {
match fut.poll(cx) {
// expect is resolved. continue loop and poll the service call branch.
Poll::Ready(Ok(req)) => {
self.as_mut().send_continue();
let mut this = self.as_mut().project();
let task = this.flow.service.call(req);
this.state.set(State::ServiceCall(task));
let fut = this.flow.service.call(req);
this.state.set(State::ServiceCall { fut });
continue;
}
// future is pending. return Ok(()) to notify that a new state is
@ -502,7 +530,7 @@ where
}
}
}
StateProj::ServiceCall(fut) => {
StateProj::ServiceCall { fut } => {
// return no matter the service call future's result.
return match fut.poll(cx) {
// future is resolved. send response and return a result. On success
@ -707,7 +735,7 @@ where
trace!("Slow request timeout");
let _ = self.as_mut().send_response(
Response::RequestTimeout().finish().drop_body(),
ResponseBody::Other(Body::Empty),
ResponseBody::Other { body: Body::Empty },
);
this = self.project();
} else {
@ -832,7 +860,7 @@ where
}
match this.inner.project() {
DispatcherStateProj::Normal(mut inner) => {
DispatcherStateProj::Normal { mut inner } => {
inner.as_mut().poll_keepalive(cx)?;
if inner.flags.contains(Flags::SHUTDOWN) {
@ -876,7 +904,7 @@ where
self.as_mut()
.project()
.inner
.set(DispatcherState::Upgrade(upgrade));
.set(DispatcherState::Upgrade { upgrade });
return self.poll(cx);
}
};
@ -928,7 +956,7 @@ where
}
}
}
DispatcherStateProj::Upgrade(fut) => fut.poll(cx).map_err(|e| {
DispatcherStateProj::Upgrade { upgrade } => upgrade.poll(cx).map_err(|e| {
error!("Upgrade handler error: {}", e);
DispatchError::Upgrade
}),
@ -1017,7 +1045,7 @@ mod tests {
Poll::Ready(res) => assert!(res.is_err()),
}
if let DispatcherStateProj::Normal(inner) = h1.project().inner.project() {
if let DispatcherStateProj::Normal { inner } = h1.project().inner.project() {
assert!(inner.flags.contains(Flags::READ_DISCONNECT));
assert_eq!(
&inner.project().io.take().unwrap().write_buf[..26],
@ -1052,7 +1080,7 @@ mod tests {
futures_util::pin_mut!(h1);
assert!(matches!(&h1.inner, DispatcherState::Normal(_)));
assert!(matches!(&h1.inner, DispatcherState::Normal { .. }));
match h1.as_mut().poll(cx) {
Poll::Pending => panic!("first poll should not be pending"),
@ -1062,7 +1090,7 @@ mod tests {
// polls: initial => shutdown
assert_eq!(h1.poll_count, 2);
if let DispatcherStateProj::Normal(inner) = h1.project().inner.project() {
if let DispatcherStateProj::Normal { inner } = h1.project().inner.project() {
let res = &mut inner.project().io.take().unwrap().write_buf[..];
stabilize_date_header(res);
@ -1106,7 +1134,7 @@ mod tests {
futures_util::pin_mut!(h1);
assert!(matches!(&h1.inner, DispatcherState::Normal(_)));
assert!(matches!(&h1.inner, DispatcherState::Normal { .. }));
match h1.as_mut().poll(cx) {
Poll::Pending => panic!("first poll should not be pending"),
@ -1116,7 +1144,7 @@ mod tests {
// polls: initial => shutdown
assert_eq!(h1.poll_count, 1);
if let DispatcherStateProj::Normal(inner) = h1.project().inner.project() {
if let DispatcherStateProj::Normal { inner } = h1.project().inner.project() {
let res = &mut inner.project().io.take().unwrap().write_buf[..];
stabilize_date_header(res);
@ -1166,13 +1194,13 @@ mod tests {
futures_util::pin_mut!(h1);
assert!(h1.as_mut().poll(cx).is_pending());
assert!(matches!(&h1.inner, DispatcherState::Normal(_)));
assert!(matches!(&h1.inner, DispatcherState::Normal { .. }));
// polls: manual
assert_eq!(h1.poll_count, 1);
eprintln!("poll count: {}", h1.poll_count);
if let DispatcherState::Normal(ref inner) = h1.inner {
if let DispatcherState::Normal { ref inner } = h1.inner {
let io = inner.io.as_ref().unwrap();
let res = &io.write_buf()[..];
assert_eq!(
@ -1187,7 +1215,7 @@ mod tests {
// polls: manual manual shutdown
assert_eq!(h1.poll_count, 3);
if let DispatcherState::Normal(ref inner) = h1.inner {
if let DispatcherState::Normal { ref inner } = h1.inner {
let io = inner.io.as_ref().unwrap();
let mut res = (&io.write_buf()[..]).to_owned();
stabilize_date_header(&mut res);
@ -1238,12 +1266,12 @@ mod tests {
futures_util::pin_mut!(h1);
assert!(h1.as_mut().poll(cx).is_ready());
assert!(matches!(&h1.inner, DispatcherState::Normal(_)));
assert!(matches!(&h1.inner, DispatcherState::Normal { .. }));
// polls: manual shutdown
assert_eq!(h1.poll_count, 2);
if let DispatcherState::Normal(ref inner) = h1.inner {
if let DispatcherState::Normal { ref inner } = h1.inner {
let io = inner.io.as_ref().unwrap();
let mut res = (&io.write_buf()[..]).to_owned();
stabilize_date_header(&mut res);
@ -1299,7 +1327,7 @@ mod tests {
futures_util::pin_mut!(h1);
assert!(h1.as_mut().poll(cx).is_ready());
assert!(matches!(&h1.inner, DispatcherState::Upgrade(_)));
assert!(matches!(&h1.inner, DispatcherState::Upgrade { .. }));
// polls: manual shutdown
assert_eq!(h1.poll_count, 2);

View File

@ -10,6 +10,7 @@ use actix_rt::net::TcpStream;
use actix_service::{pipeline_factory, IntoServiceFactory, Service, ServiceFactory};
use futures_core::ready;
use futures_util::future::ready;
use pin_project_lite::pin_project;
use crate::body::MessageBody;
use crate::config::ServiceConfig;
@ -275,31 +276,32 @@ where
}
}
#[doc(hidden)]
#[pin_project::pin_project]
pub struct H1ServiceResponse<T, S, B, X, U>
where
S: ServiceFactory<Request>,
S::Error: Into<Error>,
S::InitError: fmt::Debug,
X: ServiceFactory<Request, Response = Request>,
X::Error: Into<Error>,
X::InitError: fmt::Debug,
U: ServiceFactory<(Request, Framed<T, Codec>), Response = ()>,
U::Error: fmt::Display,
U::InitError: fmt::Debug,
{
#[pin]
fut: S::Future,
#[pin]
fut_ex: Option<X::Future>,
#[pin]
fut_upg: Option<U::Future>,
expect: Option<X::Service>,
upgrade: Option<U::Service>,
on_connect_ext: Option<Rc<ConnectCallback<T>>>,
cfg: Option<ServiceConfig>,
_phantom: PhantomData<B>,
pin_project! {
#[doc(hidden)]
pub struct H1ServiceResponse<T, S, B, X, U>
where
S: ServiceFactory<Request>,
S::Error: Into<Error>,
S::InitError: fmt::Debug,
X: ServiceFactory<Request, Response = Request>,
X::Error: Into<Error>,
X::InitError: fmt::Debug,
U: ServiceFactory<(Request, Framed<T, Codec>), Response = ()>,
U::Error: fmt::Display,
U::InitError: fmt::Debug
{
#[pin]
fut: S::Future,
#[pin]
fut_ex: Option<X::Future>,
#[pin]
fut_upg: Option<U::Future>,
expect: Option<X::Service>,
upgrade: Option<U::Service>,
on_connect_ext: Option<Rc<ConnectCallback<T>>>,
cfg: Option<ServiceConfig>,
_phantom: PhantomData<B>
}
}
impl<T, S, B, X, U> Future for H1ServiceResponse<T, S, B, X, U>

View File

@ -3,20 +3,22 @@ use std::pin::Pin;
use std::task::{Context, Poll};
use actix_codec::{AsyncRead, AsyncWrite, Framed};
use pin_project_lite::pin_project;
use crate::body::{BodySize, MessageBody, ResponseBody};
use crate::error::Error;
use crate::h1::{Codec, Message};
use crate::response::Response;
/// Send HTTP/1 response
#[pin_project::pin_project]
pub struct SendResponse<T, B> {
res: Option<Message<(Response<()>, BodySize)>>,
#[pin]
body: Option<ResponseBody<B>>,
#[pin]
framed: Option<Framed<T, Codec>>,
pin_project! {
/// Send HTTP/1 response
pub struct SendResponse<T, B> {
res: Option<Message<(Response<()>, BodySize)>>,
#[pin]
body: Option<ResponseBody<B>>,
#[pin]
framed: Option<Framed<T, Codec>>
}
}
impl<T, B> SendResponse<T, B>

View File

@ -15,6 +15,7 @@ use h2::server::{Connection, SendResponse};
use h2::SendStream;
use http::header::{HeaderValue, CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING};
use log::{error, trace};
use pin_project_lite::pin_project;
use crate::body::{BodySize, MessageBody, ResponseBody};
use crate::config::ServiceConfig;
@ -28,22 +29,25 @@ use crate::OnConnectData;
const CHUNK_SIZE: usize = 16_384;
/// Dispatcher for HTTP/2 protocol.
#[pin_project::pin_project]
pub struct Dispatcher<T, S, B, X, U>
where
T: AsyncRead + AsyncWrite + Unpin,
S: Service<Request>,
B: MessageBody,
{
flow: Rc<HttpFlow<S, X, U>>,
connection: Connection<T, Bytes>,
on_connect_data: OnConnectData,
config: ServiceConfig,
peer_addr: Option<net::SocketAddr>,
ka_expire: Instant,
ka_timer: Option<Sleep>,
_phantom: PhantomData<B>,
pin_project! {
/// Dispatcher for HTTP/2 protocol.
pub struct Dispatcher<T, S, B, X, U>
where
T: AsyncRead,
T: AsyncWrite,
T: Unpin,
S: Service<Request>,
B: MessageBody
{
flow: Rc<HttpFlow<S, X, U>>,
connection: Connection<T, Bytes>,
on_connect_data: OnConnectData,
config: ServiceConfig,
peer_addr: Option<net::SocketAddr>,
ka_expire: Instant,
ka_timer: Option<Sleep>,
_phantom: PhantomData<B>
}
}
impl<T, S, B, X, U> Dispatcher<T, S, B, X, U>
@ -136,10 +140,10 @@ where
this.on_connect_data.merge_into(&mut req);
let svc = ServiceResponse::<S::Future, S::Response, S::Error, B> {
state: ServiceResponseState::ServiceCall(
this.flow.service.call(req),
Some(res),
),
state: ServiceResponseState::ServiceCall {
fut: this.flow.service.call(req),
sender: Some(res),
},
config: this.config.clone(),
buffer: None,
_phantom: PhantomData,
@ -152,19 +156,30 @@ where
}
}
#[pin_project::pin_project]
struct ServiceResponse<F, I, E, B> {
#[pin]
state: ServiceResponseState<F, B>,
config: ServiceConfig,
buffer: Option<Bytes>,
_phantom: PhantomData<(I, E)>,
pin_project! {
struct ServiceResponse<F, I, E, B> {
#[pin]
state: ServiceResponseState<F, B>,
config: ServiceConfig,
buffer: Option<Bytes>,
_phantom: PhantomData<(I, E)>,
}
}
#[pin_project::pin_project(project = ServiceResponseStateProj)]
enum ServiceResponseState<F, B> {
ServiceCall(#[pin] F, Option<SendResponse<Bytes>>),
SendPayload(SendStream<Bytes>, #[pin] ResponseBody<B>),
pin_project! {
#[project = ServiceResponseStateProj]
enum ServiceResponseState<F, B> {
ServiceCall {
#[pin]
fut: F,
sender: Option<SendResponse<Bytes>>
},
SendPayload {
stream: SendStream<Bytes>,
#[pin]
body: ResponseBody<B>
},
}
}
impl<F, I, E, B> ServiceResponse<F, I, E, B>
@ -250,12 +265,12 @@ where
let mut this = self.as_mut().project();
match this.state.project() {
ServiceResponseStateProj::ServiceCall(call, send) => {
match ready!(call.poll(cx)) {
ServiceResponseStateProj::ServiceCall { fut, sender } => {
match ready!(fut.poll(cx)) {
Ok(res) => {
let (res, body) = res.into().replace_body(());
let mut send = send.take().unwrap();
let mut send = sender.take().unwrap();
let mut size = body.size();
let h2_res =
self.as_mut().prepare_response(res.head(), &mut size);
@ -273,7 +288,7 @@ where
Poll::Ready(())
} else {
this.state
.set(ServiceResponseState::SendPayload(stream, body));
.set(ServiceResponseState::SendPayload { stream, body });
self.poll(cx)
}
}
@ -282,7 +297,7 @@ where
let res: Response = e.into().into();
let (res, body) = res.replace_body(());
let mut send = send.take().unwrap();
let mut send = sender.take().unwrap();
let mut size = body.size();
let h2_res =
self.as_mut().prepare_response(res.head(), &mut size);
@ -299,72 +314,65 @@ where
if size.is_eof() {
Poll::Ready(())
} else {
this.state.set(ServiceResponseState::SendPayload(
this.state.set(ServiceResponseState::SendPayload {
stream,
body.into_body(),
));
body: body.into_body(),
});
self.poll(cx)
}
}
}
}
ServiceResponseStateProj::SendPayload(ref mut stream, ref mut body) => {
ServiceResponseStateProj::SendPayload { stream, mut body } => loop {
loop {
loop {
match this.buffer {
Some(ref mut buffer) => {
match ready!(stream.poll_capacity(cx)) {
None => return Poll::Ready(()),
match this.buffer {
Some(ref mut buffer) => match ready!(stream.poll_capacity(cx)) {
None => return Poll::Ready(()),
Some(Ok(cap)) => {
let len = buffer.len();
let bytes = buffer.split_to(cmp::min(cap, len));
Some(Ok(cap)) => {
let len = buffer.len();
let bytes = buffer.split_to(cmp::min(cap, len));
if let Err(e) = stream.send_data(bytes, false) {
warn!("{:?}", e);
return Poll::Ready(());
} else if !buffer.is_empty() {
let cap = cmp::min(buffer.len(), CHUNK_SIZE);
stream.reserve_capacity(cap);
} else {
this.buffer.take();
}
}
Some(Err(e)) => {
warn!("{:?}", e);
return Poll::Ready(());
}
if let Err(e) = stream.send_data(bytes, false) {
warn!("{:?}", e);
return Poll::Ready(());
} else if !buffer.is_empty() {
let cap = cmp::min(buffer.len(), CHUNK_SIZE);
stream.reserve_capacity(cap);
} else {
this.buffer.take();
}
}
None => match ready!(body.as_mut().poll_next(cx)) {
None => {
if let Err(e) = stream.send_data(Bytes::new(), true)
{
warn!("{:?}", e);
}
return Poll::Ready(());
}
Some(Err(e)) => {
warn!("{:?}", e);
return Poll::Ready(());
}
},
Some(Ok(chunk)) => {
stream.reserve_capacity(cmp::min(
chunk.len(),
CHUNK_SIZE,
));
*this.buffer = Some(chunk);
None => match ready!(body.as_mut().poll_next(cx)) {
None => {
if let Err(e) = stream.send_data(Bytes::new(), true) {
warn!("{:?}", e);
}
return Poll::Ready(());
}
Some(Err(e)) => {
error!("Response payload stream error: {:?}", e);
return Poll::Ready(());
}
},
}
Some(Ok(chunk)) => {
stream
.reserve_capacity(cmp::min(chunk.len(), CHUNK_SIZE));
*this.buffer = Some(chunk);
}
Some(Err(e)) => {
error!("Response payload stream error: {:?}", e);
return Poll::Ready(());
}
},
}
}
}
},
}
}
}

View File

@ -15,6 +15,7 @@ use futures_core::ready;
use futures_util::future::ok;
use h2::server::{self, Handshake};
use log::error;
use pin_project_lite::pin_project;
use crate::body::MessageBody;
use crate::config::ServiceConfig;
@ -205,17 +206,18 @@ where
}
}
#[doc(hidden)]
#[pin_project::pin_project]
pub struct H2ServiceResponse<T, S, B>
where
S: ServiceFactory<Request>,
{
#[pin]
fut: S::Future,
cfg: Option<ServiceConfig>,
on_connect_ext: Option<Rc<ConnectCallback<T>>>,
_phantom: PhantomData<B>,
pin_project! {
#[doc(hidden)]
pub struct H2ServiceResponse<T, S, B>
where
S: ServiceFactory<Request>,
{
#[pin]
fut: S::Future,
cfg: Option<ServiceConfig>,
on_connect_ext: Option<Rc<ConnectCallback<T>>>,
_phantom: PhantomData<B>
}
}
impl<T, S, B> Future for H2ServiceResponse<T, S, B>

View File

@ -49,7 +49,7 @@ impl Response<Body> {
pub fn new(status: StatusCode) -> Response {
Response {
head: BoxedResponseHead::new(status),
body: ResponseBody::Body(Body::Empty),
body: ResponseBody::Body { body: Body::Empty },
error: None,
}
}
@ -67,14 +67,14 @@ impl Response<Body> {
/// Convert response to response with body
pub fn into_body<B>(self) -> Response<B> {
let b = match self.body {
ResponseBody::Body(b) => b,
ResponseBody::Other(b) => b,
let body = match self.body {
ResponseBody::Body { body } => body,
ResponseBody::Other { body } => body,
};
Response {
head: self.head,
error: self.error,
body: ResponseBody::Other(b),
body: ResponseBody::Other { body },
}
}
}
@ -85,7 +85,7 @@ impl<B> Response<B> {
pub fn with_body(status: StatusCode, body: B) -> Response<B> {
Response {
head: BoxedResponseHead::new(status),
body: ResponseBody::Body(body),
body: ResponseBody::Body { body },
error: None,
}
}
@ -210,7 +210,7 @@ impl<B> Response<B> {
pub fn set_body<B2>(self, body: B2) -> Response<B2> {
Response {
head: self.head,
body: ResponseBody::Body(body),
body: ResponseBody::Body { body },
error: None,
}
}
@ -220,7 +220,7 @@ impl<B> Response<B> {
(
Response {
head: self.head,
body: ResponseBody::Body(()),
body: ResponseBody::Body { body: () },
error: self.error,
},
self.body,
@ -231,7 +231,7 @@ impl<B> Response<B> {
pub fn drop_body(self) -> Response<()> {
Response {
head: self.head,
body: ResponseBody::Body(()),
body: ResponseBody::Body { body: () },
error: None,
}
}
@ -241,7 +241,7 @@ impl<B> Response<B> {
(
Response {
head: self.head,
body: ResponseBody::Body(body),
body: ResponseBody::Body { body },
error: self.error,
},
self.body,
@ -635,7 +635,7 @@ impl ResponseBuilder {
Response {
head: response,
body: ResponseBody::Body(body),
body: ResponseBody::Body { body },
error: None,
}
}

View File

@ -9,7 +9,7 @@ use actix_service::{pipeline_factory, IntoServiceFactory, Service, ServiceFactor
use bytes::Bytes;
use futures_core::{ready, Future};
use h2::server::{self, Handshake};
use pin_project::pin_project;
use pin_project_lite::pin_project;
use crate::body::MessageBody;
use crate::builder::HttpServiceBuilder;
@ -351,25 +351,26 @@ where
}
}
#[doc(hidden)]
#[pin_project]
pub struct HttpServiceResponse<T, S, B, X, U>
where
S: ServiceFactory<Request>,
X: ServiceFactory<Request>,
U: ServiceFactory<(Request, Framed<T, h1::Codec>)>,
{
#[pin]
fut: S::Future,
#[pin]
fut_ex: Option<X::Future>,
#[pin]
fut_upg: Option<U::Future>,
expect: Option<X::Service>,
upgrade: Option<U::Service>,
on_connect_ext: Option<Rc<ConnectCallback<T>>>,
cfg: ServiceConfig,
_phantom: PhantomData<B>,
pin_project! {
#[doc(hidden)]
pub struct HttpServiceResponse<T, S, B, X, U>
where
S: ServiceFactory<Request>,
X: ServiceFactory<Request>,
U: ServiceFactory<(Request, Framed<T, h1::Codec>)>
{
#[pin]
fut: S::Future,
#[pin]
fut_ex: Option<X::Future>,
#[pin]
fut_upg: Option<U::Future>,
expect: Option<X::Service>,
upgrade: Option<U::Service>,
on_connect_ext: Option<Rc<ConnectCallback<T>>>,
cfg: ServiceConfig,
_phantom: PhantomData<B>
}
}
impl<T, S, B, X, U> Future for HttpServiceResponse<T, S, B, X, U>
@ -561,23 +562,27 @@ where
match proto {
Protocol::Http2 => HttpServiceHandlerResponse {
state: State::H2Handshake(Some((
server::handshake(io),
self.cfg.clone(),
self.flow.clone(),
on_connect_data,
peer_addr,
))),
state: State::H2Handshake {
hds: Some((
server::handshake(io),
self.cfg.clone(),
self.flow.clone(),
on_connect_data,
peer_addr,
)),
},
},
Protocol::Http1 => HttpServiceHandlerResponse {
state: State::H1(h1::Dispatcher::new(
io,
self.cfg.clone(),
self.flow.clone(),
on_connect_data,
peer_addr,
)),
state: State::H1 {
dsp: h1::Dispatcher::new(
io,
self.cfg.clone(),
self.flow.clone(),
on_connect_data,
peer_addr,
),
},
},
proto => unimplemented!("Unsupported HTTP version: {:?}.", proto),
@ -585,48 +590,58 @@ where
}
}
#[pin_project(project = StateProj)]
enum State<T, S, B, X, U>
where
S: Service<Request>,
S::Future: 'static,
S::Error: Into<Error>,
T: AsyncRead + AsyncWrite + Unpin,
B: MessageBody,
X: Service<Request, Response = Request>,
X::Error: Into<Error>,
U: Service<(Request, Framed<T, h1::Codec>), Response = ()>,
U::Error: fmt::Display,
{
H1(#[pin] h1::Dispatcher<T, S, B, X, U>),
H2(#[pin] Dispatcher<T, S, B, X, U>),
H2Handshake(
Option<(
Handshake<T, Bytes>,
ServiceConfig,
Rc<HttpFlow<S, X, U>>,
OnConnectData,
Option<net::SocketAddr>,
)>,
),
pin_project! {
#[project = StateProj]
enum State<T, S, B, X, U>
where
S: Service<Request>,
S::Future: 'static,
S::Error: Into<Error>,
T: AsyncRead,
T: AsyncWrite,
T: Unpin,
B: MessageBody,
X: Service<Request, Response = Request>,
X::Error: Into<Error>,
U: Service<(Request, Framed<T, h1::Codec>), Response = ()>,
U::Error: fmt::Display
{
H1 { #[pin] dsp: h1::Dispatcher<T, S, B, X, U> },
H2 { #[pin] dsp: Dispatcher<T, S, B, X, U> },
H2Handshake {
hds: Option<(
Handshake<T, Bytes>,
ServiceConfig,
Rc<HttpFlow<S, X, U>>,
OnConnectData,
Option<net::SocketAddr>,
)>
}
}
}
#[pin_project]
pub struct HttpServiceHandlerResponse<T, S, B, X, U>
where
T: AsyncRead + AsyncWrite + Unpin,
S: Service<Request>,
S::Error: Into<Error> + 'static,
S::Future: 'static,
S::Response: Into<Response<B>> + 'static,
B: MessageBody + 'static,
X: Service<Request, Response = Request>,
X::Error: Into<Error>,
U: Service<(Request, Framed<T, h1::Codec>), Response = ()>,
U::Error: fmt::Display,
{
#[pin]
state: State<T, S, B, X, U>,
pin_project! {
pub struct HttpServiceHandlerResponse<T, S, B, X, U>
where
T: AsyncRead,
T: AsyncWrite,
T: Unpin,
S: Service<Request>,
S::Error: Into<Error>,
S::Error: 'static,
S::Future: 'static,
S::Response: Into<Response<B>>,
S::Response: 'static,
B: MessageBody,
B: 'static,
X: Service<Request, Response = Request>,
X::Error: Into<Error>,
U: Service<(Request, Framed<T, h1::Codec>), Response = ()>,
U::Error: fmt::Display
{
#[pin]
state: State<T, S, B, X, U>
}
}
impl<T, S, B, X, U> Future for HttpServiceHandlerResponse<T, S, B, X, U>
@ -646,21 +661,23 @@ where
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
match self.as_mut().project().state.project() {
StateProj::H1(disp) => disp.poll(cx),
StateProj::H2(disp) => disp.poll(cx),
StateProj::H2Handshake(data) => {
match ready!(Pin::new(&mut data.as_mut().unwrap().0).poll(cx)) {
StateProj::H1 { dsp } => dsp.poll(cx),
StateProj::H2 { dsp } => dsp.poll(cx),
StateProj::H2Handshake { hds } => {
match ready!(Pin::new(&mut hds.as_mut().unwrap().0).poll(cx)) {
Ok(conn) => {
let (_, cfg, srv, on_connect_data, peer_addr) =
data.take().unwrap();
self.as_mut().project().state.set(State::H2(Dispatcher::new(
srv,
conn,
on_connect_data,
cfg,
None,
peer_addr,
)));
hds.take().unwrap();
self.as_mut().project().state.set(State::H2 {
dsp: Dispatcher::new(
srv,
conn,
on_connect_data,
cfg,
None,
peer_addr,
),
});
self.poll(cx)
}
Err(err) => {

View File

@ -5,17 +5,21 @@ use std::task::{Context, Poll};
use actix_codec::{AsyncRead, AsyncWrite, Framed};
use actix_service::{IntoService, Service};
use actix_utils::dispatcher::{Dispatcher as InnerDispatcher, DispatcherError};
use pin_project_lite::pin_project;
use super::{Codec, Frame, Message};
#[pin_project::pin_project]
pub struct Dispatcher<S, T>
where
S: Service<Frame, Response = Message> + 'static,
T: AsyncRead + AsyncWrite,
{
#[pin]
inner: InnerDispatcher<S, T, Codec, Message>,
pin_project! {
pub struct Dispatcher<S, T>
where
S: Service<Frame, Response = Message>,
S: 'static,
T: AsyncRead,
T: AsyncWrite,
{
#[pin]
inner: InnerDispatcher<S, T, Codec, Message>
}
}
impl<S, T> Dispatcher<S, T>

View File

@ -117,7 +117,9 @@ pub trait MapServiceResponseBody {
impl<B: MessageBody + Unpin + 'static> MapServiceResponseBody for ServiceResponse<B> {
fn map_body(self) -> ServiceResponse {
self.map_body(|_, body| ResponseBody::Other(Body::from_message(body)))
self.map_body(|_, body| ResponseBody::Other {
body: Body::from_message(body),
})
}
}

View File

@ -289,13 +289,13 @@ where
let time = *this.time;
let format = this.format.take();
Poll::Ready(Ok(res.map_body(move |_, body| {
ResponseBody::Body(StreamLog {
Poll::Ready(Ok(res.map_body(move |_, body| ResponseBody::Body {
body: StreamLog {
body,
time,
format,
size: 0,
})
},
})))
}
}

View File

@ -277,7 +277,9 @@ pub(crate) mod tests {
let resp = srv.call(req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK);
match resp.response().body() {
ResponseBody::Body(Body::Bytes(ref b)) => {
ResponseBody::Body {
body: Body::Bytes(ref b),
} => {
let bytes = b.clone();
assert_eq!(bytes, Bytes::from_static(b"some"));
}
@ -292,21 +294,21 @@ pub(crate) mod tests {
impl BodyTest for ResponseBody<Body> {
fn bin_ref(&self) -> &[u8] {
match self {
ResponseBody::Body(ref b) => match b {
match *self {
ResponseBody::Body { ref body } => match body {
Body::Bytes(ref bin) => &bin,
_ => panic!(),
},
ResponseBody::Other(ref b) => match b {
ResponseBody::Other { ref body } => match body {
Body::Bytes(ref bin) => &bin,
_ => panic!(),
},
}
}
fn body(&self) -> &Body {
match self {
ResponseBody::Body(ref b) => b,
ResponseBody::Other(ref b) => b,
match *self {
ResponseBody::Body { ref body } => body,
ResponseBody::Other { ref body } => body,
}
}
}

View File

@ -748,7 +748,9 @@ mod tests {
assert_eq!(resp.status(), StatusCode::OK);
match resp.response().body() {
ResponseBody::Body(Body::Bytes(ref b)) => {
ResponseBody::Body {
body: Body::Bytes(ref b),
} => {
let bytes = b.clone();
assert_eq!(bytes, Bytes::from_static(b"project: project1"));
}
@ -849,7 +851,9 @@ mod tests {
assert_eq!(resp.status(), StatusCode::CREATED);
match resp.response().body() {
ResponseBody::Body(Body::Bytes(ref b)) => {
ResponseBody::Body {
body: Body::Bytes(ref b),
} => {
let bytes = b.clone();
assert_eq!(bytes, Bytes::from_static(b"project: project_1"));
}
@ -877,7 +881,9 @@ mod tests {
assert_eq!(resp.status(), StatusCode::CREATED);
match resp.response().body() {
ResponseBody::Body(Body::Bytes(ref b)) => {
ResponseBody::Body {
body: Body::Bytes(ref b),
} => {
let bytes = b.clone();
assert_eq!(bytes, Bytes::from_static(b"project: test - 1"));
}