mirror of
https://github.com/fafhrd91/actix-web
synced 2025-07-01 08:45:10 +02:00
make actix-http compile with std::future
This commit is contained in:
@ -1,15 +1,17 @@
|
||||
use std::collections::VecDeque;
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
use std::time::Instant;
|
||||
use std::{fmt, io, net};
|
||||
use std::{fmt, io, io::Write, net};
|
||||
|
||||
use actix_codec::{Decoder, Encoder, Framed, FramedParts};
|
||||
use actix_codec::{AsyncRead, AsyncWrite, Decoder, Encoder, Framed, FramedParts};
|
||||
use actix_server_config::IoStream;
|
||||
use actix_service::Service;
|
||||
use bitflags::bitflags;
|
||||
use bytes::{BufMut, BytesMut};
|
||||
use futures::{Async, Future, Poll};
|
||||
use log::{error, trace};
|
||||
use tokio_timer::Delay;
|
||||
use tokio_timer::{delay, Delay};
|
||||
|
||||
use crate::body::{Body, BodySize, MessageBody, ResponseBody};
|
||||
use crate::cloneable::CloneableService;
|
||||
@ -46,11 +48,14 @@ pub struct Dispatcher<T, S, B, X, U>
|
||||
where
|
||||
S: Service<Request = Request>,
|
||||
S::Error: Into<Error>,
|
||||
S::Future: Unpin,
|
||||
B: MessageBody,
|
||||
X: Service<Request = Request, Response = Request>,
|
||||
X::Error: Into<Error>,
|
||||
X::Future: Unpin,
|
||||
U: Service<Request = (Request, Framed<T, Codec>), Response = ()>,
|
||||
U::Error: fmt::Display,
|
||||
U::Future: Unpin,
|
||||
{
|
||||
inner: DispatcherState<T, S, B, X, U>,
|
||||
}
|
||||
@ -59,11 +64,14 @@ enum DispatcherState<T, S, B, X, U>
|
||||
where
|
||||
S: Service<Request = Request>,
|
||||
S::Error: Into<Error>,
|
||||
S::Future: Unpin,
|
||||
B: MessageBody,
|
||||
X: Service<Request = Request, Response = Request>,
|
||||
X::Error: Into<Error>,
|
||||
X::Future: Unpin,
|
||||
U: Service<Request = (Request, Framed<T, Codec>), Response = ()>,
|
||||
U::Error: fmt::Display,
|
||||
U::Future: Unpin,
|
||||
{
|
||||
Normal(InnerDispatcher<T, S, B, X, U>),
|
||||
Upgrade(U::Future),
|
||||
@ -74,11 +82,14 @@ struct InnerDispatcher<T, S, B, X, U>
|
||||
where
|
||||
S: Service<Request = Request>,
|
||||
S::Error: Into<Error>,
|
||||
S::Future: Unpin,
|
||||
B: MessageBody,
|
||||
X: Service<Request = Request, Response = Request>,
|
||||
X::Error: Into<Error>,
|
||||
X::Future: Unpin,
|
||||
U: Service<Request = (Request, Framed<T, Codec>), Response = ()>,
|
||||
U::Error: fmt::Display,
|
||||
U::Future: Unpin,
|
||||
{
|
||||
service: CloneableService<S>,
|
||||
expect: CloneableService<X>,
|
||||
@ -170,11 +181,14 @@ where
|
||||
S: Service<Request = Request>,
|
||||
S::Error: Into<Error>,
|
||||
S::Response: Into<Response<B>>,
|
||||
S::Future: Unpin,
|
||||
B: MessageBody,
|
||||
X: Service<Request = Request, Response = Request>,
|
||||
X::Error: Into<Error>,
|
||||
X::Future: Unpin,
|
||||
U: Service<Request = (Request, Framed<T, Codec>), Response = ()>,
|
||||
U::Error: fmt::Display,
|
||||
U::Future: Unpin,
|
||||
{
|
||||
/// Create http/1 dispatcher.
|
||||
pub(crate) fn new(
|
||||
@ -255,20 +269,23 @@ where
|
||||
S: Service<Request = Request>,
|
||||
S::Error: Into<Error>,
|
||||
S::Response: Into<Response<B>>,
|
||||
S::Future: Unpin,
|
||||
B: MessageBody,
|
||||
X: Service<Request = Request, Response = Request>,
|
||||
X::Error: Into<Error>,
|
||||
X::Future: Unpin,
|
||||
U: Service<Request = (Request, Framed<T, Codec>), Response = ()>,
|
||||
U::Error: fmt::Display,
|
||||
U::Future: Unpin,
|
||||
{
|
||||
fn can_read(&self) -> bool {
|
||||
fn can_read(&self, cx: &mut Context) -> bool {
|
||||
if self
|
||||
.flags
|
||||
.intersects(Flags::READ_DISCONNECT | Flags::UPGRADE)
|
||||
{
|
||||
false
|
||||
} else if let Some(ref info) = self.payload {
|
||||
info.need_read() == PayloadStatus::Read
|
||||
info.need_read(cx) == PayloadStatus::Read
|
||||
} else {
|
||||
true
|
||||
}
|
||||
@ -287,7 +304,7 @@ where
|
||||
///
|
||||
/// true - got whouldblock
|
||||
/// false - didnt get whouldblock
|
||||
fn poll_flush(&mut self) -> Result<bool, DispatchError> {
|
||||
fn poll_flush(&mut self, cx: &mut Context) -> Result<bool, DispatchError> {
|
||||
if self.write_buf.is_empty() {
|
||||
return Ok(false);
|
||||
}
|
||||
@ -295,23 +312,23 @@ where
|
||||
let len = self.write_buf.len();
|
||||
let mut written = 0;
|
||||
while written < len {
|
||||
match self.io.write(&self.write_buf[written..]) {
|
||||
Ok(0) => {
|
||||
match Pin::new(&mut self.io).poll_write(cx, &self.write_buf[written..]) {
|
||||
Poll::Ready(Ok(0)) => {
|
||||
return Err(DispatchError::Io(io::Error::new(
|
||||
io::ErrorKind::WriteZero,
|
||||
"",
|
||||
)));
|
||||
}
|
||||
Ok(n) => {
|
||||
Poll::Ready(Ok(n)) => {
|
||||
written += n;
|
||||
}
|
||||
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
|
||||
Poll::Pending => {
|
||||
if written > 0 {
|
||||
let _ = self.write_buf.split_to(written);
|
||||
}
|
||||
return Ok(true);
|
||||
}
|
||||
Err(err) => return Err(DispatchError::Io(err)),
|
||||
Poll::Ready(Err(err)) => return Err(DispatchError::Io(err)),
|
||||
}
|
||||
}
|
||||
if written > 0 {
|
||||
@ -350,12 +367,15 @@ where
|
||||
.extend_from_slice(b"HTTP/1.1 100 Continue\r\n\r\n");
|
||||
}
|
||||
|
||||
fn poll_response(&mut self) -> Result<PollResponse, DispatchError> {
|
||||
fn poll_response(
|
||||
&mut self,
|
||||
cx: &mut Context,
|
||||
) -> Result<PollResponse, DispatchError> {
|
||||
loop {
|
||||
let state = match self.state {
|
||||
State::None => match self.messages.pop_front() {
|
||||
Some(DispatcherMessage::Item(req)) => {
|
||||
Some(self.handle_request(req)?)
|
||||
Some(self.handle_request(req, cx)?)
|
||||
}
|
||||
Some(DispatcherMessage::Error(res)) => {
|
||||
Some(self.send_response(res, ResponseBody::Other(Body::Empty))?)
|
||||
@ -365,54 +385,54 @@ where
|
||||
}
|
||||
None => None,
|
||||
},
|
||||
State::ExpectCall(ref mut fut) => match fut.poll() {
|
||||
Ok(Async::Ready(req)) => {
|
||||
State::ExpectCall(ref mut fut) => match Pin::new(fut).poll(cx) {
|
||||
Poll::Ready(Ok(req)) => {
|
||||
self.send_continue();
|
||||
self.state = State::ServiceCall(self.service.call(req));
|
||||
continue;
|
||||
}
|
||||
Ok(Async::NotReady) => None,
|
||||
Err(e) => {
|
||||
Poll::Ready(Err(e)) => {
|
||||
let res: Response = e.into().into();
|
||||
let (res, body) = res.replace_body(());
|
||||
Some(self.send_response(res, body.into_body())?)
|
||||
}
|
||||
Poll::Pending => None,
|
||||
},
|
||||
State::ServiceCall(ref mut fut) => match fut.poll() {
|
||||
Ok(Async::Ready(res)) => {
|
||||
State::ServiceCall(ref mut fut) => match Pin::new(fut).poll(cx) {
|
||||
Poll::Ready(Ok(res)) => {
|
||||
let (res, body) = res.into().replace_body(());
|
||||
self.state = self.send_response(res, body)?;
|
||||
continue;
|
||||
}
|
||||
Ok(Async::NotReady) => None,
|
||||
Err(e) => {
|
||||
Poll::Ready(Err(e)) => {
|
||||
let res: Response = e.into().into();
|
||||
let (res, body) = res.replace_body(());
|
||||
Some(self.send_response(res, body.into_body())?)
|
||||
}
|
||||
Poll::Pending => None,
|
||||
},
|
||||
State::SendPayload(ref mut stream) => {
|
||||
loop {
|
||||
if self.write_buf.len() < HW_BUFFER_SIZE {
|
||||
match stream
|
||||
.poll_next()
|
||||
.map_err(|_| DispatchError::Unknown)?
|
||||
{
|
||||
Async::Ready(Some(item)) => {
|
||||
match stream.poll_next(cx) {
|
||||
Poll::Ready(Some(Ok(item))) => {
|
||||
self.codec.encode(
|
||||
Message::Chunk(Some(item)),
|
||||
&mut self.write_buf,
|
||||
)?;
|
||||
continue;
|
||||
}
|
||||
Async::Ready(None) => {
|
||||
Poll::Ready(None) => {
|
||||
self.codec.encode(
|
||||
Message::Chunk(None),
|
||||
&mut self.write_buf,
|
||||
)?;
|
||||
self.state = State::None;
|
||||
}
|
||||
Async::NotReady => return Ok(PollResponse::DoNothing),
|
||||
Poll::Ready(Some(Err(_))) => {
|
||||
return Err(DispatchError::Unknown)
|
||||
}
|
||||
Poll::Pending => return Ok(PollResponse::DoNothing),
|
||||
}
|
||||
} else {
|
||||
return Ok(PollResponse::DrainWriteBuf);
|
||||
@ -433,7 +453,7 @@ where
|
||||
// if read-backpressure is enabled and we consumed some data.
|
||||
// we may read more data and retry
|
||||
if self.state.is_call() {
|
||||
if self.poll_request()? {
|
||||
if self.poll_request(cx)? {
|
||||
continue;
|
||||
}
|
||||
} else if !self.messages.is_empty() {
|
||||
@ -446,17 +466,21 @@ where
|
||||
Ok(PollResponse::DoNothing)
|
||||
}
|
||||
|
||||
fn handle_request(&mut self, req: Request) -> Result<State<S, B, X>, DispatchError> {
|
||||
fn handle_request(
|
||||
&mut self,
|
||||
req: Request,
|
||||
cx: &mut Context,
|
||||
) -> Result<State<S, B, X>, DispatchError> {
|
||||
// Handle `EXPECT: 100-Continue` header
|
||||
let req = if req.head().expect() {
|
||||
let mut task = self.expect.call(req);
|
||||
match task.poll() {
|
||||
Ok(Async::Ready(req)) => {
|
||||
match Pin::new(&mut task).poll(cx) {
|
||||
Poll::Ready(Ok(req)) => {
|
||||
self.send_continue();
|
||||
req
|
||||
}
|
||||
Ok(Async::NotReady) => return Ok(State::ExpectCall(task)),
|
||||
Err(e) => {
|
||||
Poll::Pending => return Ok(State::ExpectCall(task)),
|
||||
Poll::Ready(Err(e)) => {
|
||||
let e = e.into();
|
||||
let res: Response = e.into();
|
||||
let (res, body) = res.replace_body(());
|
||||
@ -469,13 +493,13 @@ where
|
||||
|
||||
// Call service
|
||||
let mut task = self.service.call(req);
|
||||
match task.poll() {
|
||||
Ok(Async::Ready(res)) => {
|
||||
match Pin::new(&mut task).poll(cx) {
|
||||
Poll::Ready(Ok(res)) => {
|
||||
let (res, body) = res.into().replace_body(());
|
||||
self.send_response(res, body)
|
||||
}
|
||||
Ok(Async::NotReady) => Ok(State::ServiceCall(task)),
|
||||
Err(e) => {
|
||||
Poll::Pending => Ok(State::ServiceCall(task)),
|
||||
Poll::Ready(Err(e)) => {
|
||||
let res: Response = e.into().into();
|
||||
let (res, body) = res.replace_body(());
|
||||
self.send_response(res, body.into_body())
|
||||
@ -484,9 +508,12 @@ where
|
||||
}
|
||||
|
||||
/// Process one incoming requests
|
||||
pub(self) fn poll_request(&mut self) -> Result<bool, DispatchError> {
|
||||
pub(self) fn poll_request(
|
||||
&mut self,
|
||||
cx: &mut Context,
|
||||
) -> Result<bool, DispatchError> {
|
||||
// limit a mount of non processed requests
|
||||
if self.messages.len() >= MAX_PIPELINED_MESSAGES || !self.can_read() {
|
||||
if self.messages.len() >= MAX_PIPELINED_MESSAGES || !self.can_read(cx) {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
@ -521,7 +548,7 @@ where
|
||||
|
||||
// handle request early
|
||||
if self.state.is_empty() {
|
||||
self.state = self.handle_request(req)?;
|
||||
self.state = self.handle_request(req, cx)?;
|
||||
} else {
|
||||
self.messages.push_back(DispatcherMessage::Item(req));
|
||||
}
|
||||
@ -587,12 +614,12 @@ where
|
||||
}
|
||||
|
||||
/// keep-alive timer
|
||||
fn poll_keepalive(&mut self) -> Result<(), DispatchError> {
|
||||
fn poll_keepalive(&mut self, cx: &mut Context) -> Result<(), DispatchError> {
|
||||
if self.ka_timer.is_none() {
|
||||
// shutdown timeout
|
||||
if self.flags.contains(Flags::SHUTDOWN) {
|
||||
if let Some(interval) = self.codec.config().client_disconnect_timer() {
|
||||
self.ka_timer = Some(Delay::new(interval));
|
||||
self.ka_timer = Some(delay(interval));
|
||||
} else {
|
||||
self.flags.insert(Flags::READ_DISCONNECT);
|
||||
if let Some(mut payload) = self.payload.take() {
|
||||
@ -605,11 +632,8 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
match self.ka_timer.as_mut().unwrap().poll().map_err(|e| {
|
||||
error!("Timer error {:?}", e);
|
||||
DispatchError::Unknown
|
||||
})? {
|
||||
Async::Ready(_) => {
|
||||
match Pin::new(&mut self.ka_timer.as_mut().unwrap()).poll(cx) {
|
||||
Poll::Ready(()) => {
|
||||
// if we get timeout during shutdown, drop connection
|
||||
if self.flags.contains(Flags::SHUTDOWN) {
|
||||
return Err(DispatchError::DisconnectTimeout);
|
||||
@ -624,9 +648,9 @@ where
|
||||
if let Some(deadline) =
|
||||
self.codec.config().client_disconnect_timer()
|
||||
{
|
||||
if let Some(timer) = self.ka_timer.as_mut() {
|
||||
if let Some(mut timer) = self.ka_timer.as_mut() {
|
||||
timer.reset(deadline);
|
||||
let _ = timer.poll();
|
||||
let _ = Pin::new(&mut timer).poll(cx);
|
||||
}
|
||||
} else {
|
||||
// no shutdown timeout, drop socket
|
||||
@ -650,17 +674,17 @@ where
|
||||
} else if let Some(deadline) =
|
||||
self.codec.config().keep_alive_expire()
|
||||
{
|
||||
if let Some(timer) = self.ka_timer.as_mut() {
|
||||
if let Some(mut timer) = self.ka_timer.as_mut() {
|
||||
timer.reset(deadline);
|
||||
let _ = timer.poll();
|
||||
let _ = Pin::new(&mut timer).poll(cx);
|
||||
}
|
||||
}
|
||||
} else if let Some(timer) = self.ka_timer.as_mut() {
|
||||
} else if let Some(mut timer) = self.ka_timer.as_mut() {
|
||||
timer.reset(self.ka_expire);
|
||||
let _ = timer.poll();
|
||||
let _ = Pin::new(&mut timer).poll(cx);
|
||||
}
|
||||
}
|
||||
Async::NotReady => (),
|
||||
Poll::Pending => (),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -673,33 +697,37 @@ where
|
||||
S: Service<Request = Request>,
|
||||
S::Error: Into<Error>,
|
||||
S::Response: Into<Response<B>>,
|
||||
S::Future: Unpin,
|
||||
B: MessageBody,
|
||||
X: Service<Request = Request, Response = Request>,
|
||||
X::Error: Into<Error>,
|
||||
X::Future: Unpin,
|
||||
U: Service<Request = (Request, Framed<T, Codec>), Response = ()>,
|
||||
U::Error: fmt::Display,
|
||||
U::Future: Unpin,
|
||||
{
|
||||
type Item = ();
|
||||
type Error = DispatchError;
|
||||
type Output = Result<(), DispatchError>;
|
||||
|
||||
#[inline]
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
|
||||
match self.inner {
|
||||
DispatcherState::Normal(ref mut inner) => {
|
||||
inner.poll_keepalive()?;
|
||||
inner.poll_keepalive(cx)?;
|
||||
|
||||
if inner.flags.contains(Flags::SHUTDOWN) {
|
||||
if inner.flags.contains(Flags::WRITE_DISCONNECT) {
|
||||
Ok(Async::Ready(()))
|
||||
Poll::Ready(Ok(()))
|
||||
} else {
|
||||
// flush buffer
|
||||
inner.poll_flush()?;
|
||||
inner.poll_flush(cx)?;
|
||||
if !inner.write_buf.is_empty() {
|
||||
Ok(Async::NotReady)
|
||||
Poll::Pending
|
||||
} else {
|
||||
match inner.io.shutdown()? {
|
||||
Async::Ready(_) => Ok(Async::Ready(())),
|
||||
Async::NotReady => Ok(Async::NotReady),
|
||||
match Pin::new(&mut inner.io).poll_shutdown(cx) {
|
||||
Poll::Ready(res) => {
|
||||
Poll::Ready(res.map_err(DispatchError::from))
|
||||
}
|
||||
Poll::Pending => Poll::Pending,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -707,12 +735,12 @@ where
|
||||
// read socket into a buf
|
||||
let should_disconnect =
|
||||
if !inner.flags.contains(Flags::READ_DISCONNECT) {
|
||||
read_available(&mut inner.io, &mut inner.read_buf)?
|
||||
read_available(cx, &mut inner.io, &mut inner.read_buf)?
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
inner.poll_request()?;
|
||||
inner.poll_request(cx)?;
|
||||
if let Some(true) = should_disconnect {
|
||||
inner.flags.insert(Flags::READ_DISCONNECT);
|
||||
if let Some(mut payload) = inner.payload.take() {
|
||||
@ -724,7 +752,7 @@ where
|
||||
if inner.write_buf.remaining_mut() < LW_BUFFER_SIZE {
|
||||
inner.write_buf.reserve(HW_BUFFER_SIZE);
|
||||
}
|
||||
let result = inner.poll_response()?;
|
||||
let result = inner.poll_response(cx)?;
|
||||
let drain = result == PollResponse::DrainWriteBuf;
|
||||
|
||||
// switch to upgrade handler
|
||||
@ -742,7 +770,7 @@ where
|
||||
self.inner = DispatcherState::Upgrade(
|
||||
inner.upgrade.unwrap().call((req, framed)),
|
||||
);
|
||||
return self.poll();
|
||||
return self.poll(cx);
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
@ -751,14 +779,14 @@ where
|
||||
// we didnt get WouldBlock from write operation,
|
||||
// so data get written to kernel completely (OSX)
|
||||
// and we have to write again otherwise response can get stuck
|
||||
if inner.poll_flush()? || !drain {
|
||||
if inner.poll_flush(cx)? || !drain {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// client is gone
|
||||
if inner.flags.contains(Flags::WRITE_DISCONNECT) {
|
||||
return Ok(Async::Ready(()));
|
||||
return Poll::Ready(Ok(()));
|
||||
}
|
||||
|
||||
let is_empty = inner.state.is_empty();
|
||||
@ -771,38 +799,44 @@ where
|
||||
// keep-alive and stream errors
|
||||
if is_empty && inner.write_buf.is_empty() {
|
||||
if let Some(err) = inner.error.take() {
|
||||
Err(err)
|
||||
Poll::Ready(Err(err))
|
||||
}
|
||||
// disconnect if keep-alive is not enabled
|
||||
else if inner.flags.contains(Flags::STARTED)
|
||||
&& !inner.flags.intersects(Flags::KEEPALIVE)
|
||||
{
|
||||
inner.flags.insert(Flags::SHUTDOWN);
|
||||
self.poll()
|
||||
self.poll(cx)
|
||||
}
|
||||
// disconnect if shutdown
|
||||
else if inner.flags.contains(Flags::SHUTDOWN) {
|
||||
self.poll()
|
||||
self.poll(cx)
|
||||
} else {
|
||||
Ok(Async::NotReady)
|
||||
Poll::Pending
|
||||
}
|
||||
} else {
|
||||
Ok(Async::NotReady)
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
DispatcherState::Upgrade(ref mut fut) => fut.poll().map_err(|e| {
|
||||
error!("Upgrade handler error: {}", e);
|
||||
DispatchError::Upgrade
|
||||
}),
|
||||
DispatcherState::Upgrade(ref mut fut) => {
|
||||
Pin::new(fut).poll(cx).map_err(|e| {
|
||||
error!("Upgrade handler error: {}", e);
|
||||
DispatchError::Upgrade
|
||||
})
|
||||
}
|
||||
DispatcherState::None => panic!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn read_available<T>(io: &mut T, buf: &mut BytesMut) -> Result<Option<bool>, io::Error>
|
||||
fn read_available<T>(
|
||||
cx: &mut Context,
|
||||
io: &mut T,
|
||||
buf: &mut BytesMut,
|
||||
) -> Result<Option<bool>, io::Error>
|
||||
where
|
||||
T: io::Read,
|
||||
T: AsyncRead + Unpin,
|
||||
{
|
||||
let mut read_some = false;
|
||||
loop {
|
||||
@ -810,19 +844,18 @@ where
|
||||
buf.reserve(HW_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
let read = unsafe { io.read(buf.bytes_mut()) };
|
||||
match read {
|
||||
Ok(n) => {
|
||||
match read(cx, io, buf) {
|
||||
Poll::Pending => {
|
||||
return if read_some { Ok(Some(false)) } else { Ok(None) };
|
||||
}
|
||||
Poll::Ready(Ok(n)) => {
|
||||
if n == 0 {
|
||||
return Ok(Some(true));
|
||||
} else {
|
||||
read_some = true;
|
||||
unsafe {
|
||||
buf.advance_mut(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
Poll::Ready(Err(e)) => {
|
||||
return if e.kind() == io::ErrorKind::WouldBlock {
|
||||
if read_some {
|
||||
Ok(Some(false))
|
||||
@ -833,12 +866,23 @@ where
|
||||
Ok(Some(true))
|
||||
} else {
|
||||
Err(e)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn read<T>(
|
||||
cx: &mut Context,
|
||||
io: &mut T,
|
||||
buf: &mut BytesMut,
|
||||
) -> Poll<Result<usize, io::Error>>
|
||||
where
|
||||
T: AsyncRead + Unpin,
|
||||
{
|
||||
Pin::new(io).poll_read_buf(cx, buf)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use actix_service::IntoService;
|
||||
|
Reference in New Issue
Block a user