mirror of https://github.com/fafhrd91/actix-web
refactor OpenWaitingConnection guard. use pin_project_lite
This commit is contained in:
parent
dddb623a11
commit
36830b98f6
|
@ -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]
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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(());
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
})
|
||||
},
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
12
src/scope.rs
12
src/scope.rs
|
@ -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"));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue