Timeout doesn't time out in AsyncRead - rust

I'm trying to implement an async read wrapper that will add read timeout functionality. The objective is that the API is plain AsyncRead. In other words, I don't want to add io.read(buf).timeout(t) everywehere in the code. Instead, the read instance itself should return the appropriate io::ErrorKind::TimedOut after the given timeout expires.
I can't poll the delay to Ready though. It's always Pending. I've tried with async-std, futures, smol-timeout - the same result. While the timeout does trigger when awaited, it just doesn't when polled. I know timeouts aren't easy. Something needs to wake it up. What am I doing wrong? How to pull this through?
use async_std::{
future::Future,
io,
pin::Pin,
task::{sleep, Context, Poll},
};
use std::time::Duration;
pub struct PrudentIo<IO> {
expired: Option<Pin<Box<dyn Future<Output = ()> + Sync + Send>>>,
timeout: Duration,
io: IO,
}
impl<IO> PrudentIo<IO> {
pub fn new(timeout: Duration, io: IO) -> Self {
PrudentIo {
expired: None,
timeout,
io,
}
}
}
fn delay(t: Duration) -> Option<Pin<Box<dyn Future<Output = ()> + Sync + Send + 'static>>> {
if t.is_zero() {
return None;
}
Some(Box::pin(sleep(t)))
}
impl<IO: io::Read + Unpin> io::Read for PrudentIo<IO> {
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<io::Result<usize>> {
if let Some(ref mut expired) = self.expired {
match expired.as_mut().poll(cx) {
Poll::Ready(_) => {
println!("expired ready");
// too much time passed since last read/write
return Poll::Ready(Err(io::ErrorKind::TimedOut.into()));
}
Poll::Pending => {
println!("expired pending");
// in good time
}
}
}
let res = Pin::new(&mut self.io).poll_read(cx, buf);
println!("read {:?}", res);
match res {
Poll::Pending => {
if self.expired.is_none() {
// No data, start checking for a timeout
self.expired = delay(self.timeout);
}
}
Poll::Ready(_) => self.expired = None,
}
res
}
}
impl<IO: io::Write + Unpin> io::Write for PrudentIo<IO> {
fn poll_write(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
Pin::new(&mut self.io).poll_write(cx, buf)
}
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Pin::new(&mut self.io).poll_flush(cx)
}
fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Pin::new(&mut self.io).poll_close(cx)
}
}
#[cfg(test)]
mod io_tests {
use super::*;
use async_std::io::ReadExt;
use async_std::prelude::FutureExt;
use async_std::{
io::{copy, Cursor},
net::TcpStream,
};
use std::time::Duration;
#[async_std::test]
async fn fail_read_after_timeout() -> io::Result<()> {
let mut output = b"______".to_vec();
let io = PendIo;
let mut io = PrudentIo::new(Duration::from_millis(5), io);
let mut io = Pin::new(&mut io);
insta::assert_debug_snapshot!(io.read(&mut output[..]).timeout(Duration::from_secs(1)).await,#"Ok(io::Err(timeou))");
Ok(())
}
#[async_std::test]
async fn timeout_expires() {
let later = delay(Duration::from_millis(1)).expect("some").await;
insta::assert_debug_snapshot!(later,#r"()");
}
/// Mock IO always pending
struct PendIo;
impl io::Read for PendIo {
fn poll_read(
self: Pin<&mut Self>,
_cx: &mut Context<'_>,
_buf: &mut [u8],
) -> Poll<futures_io::Result<usize>> {
Poll::Pending
}
}
impl io::Write for PendIo {
fn poll_write(
self: Pin<&mut Self>,
_cx: &mut Context<'_>,
_buf: &[u8],
) -> Poll<futures_io::Result<usize>> {
Poll::Pending
}
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<futures_io::Result<()>> {
Poll::Pending
}
fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<futures_io::Result<()>> {
Poll::Pending
}
}
}

Async timeouts work as follows:
You create the timeout future.
The runtime calls poll into the timeout, it checks whether the timeout has expired.
If it is expired, it returns Ready and done.
If it is not expired, it somehow registers a callback for when the right time has passed it calls cx.waker().wake(), or similar.
When the time has passed, the callback from #4 is invoked, that calls wake() in the proper waker, which instructs the runtime to call poll again.
This time poll will return Ready. Done!
The problem with your code is that you create the delay from inside the poll() implementation: self.expired = delay(self.timeout);. But then you return Pending without polling the timeout even once. This way, there is no callback registered anywhere that would call the Waker. No waker, no timeout.
I see several solutions:
A. Do not initialize PrudentIo::expired to None but create the timeout directly in the constructor. That way the timeout will always be polled before the io at least once, and it will be woken. But you will create a timeout always, even if it is not actually needed.
B. When creating the timeout do a recursive poll:
Poll::Pending => {
if self.expired.is_none() {
// No data, start checking for a timeout
self.expired = delay(self.timeout);
return self.poll_read(cx, buf);
}
This will call the io twice, unnecesarily, so it may not be optimal.
C. Add a call to poll after creating the timeout:
Poll::Pending => {
if self.expired.is_none() {
// No data, start checking for a timeout
self.expired = delay(self.timeout);
self.expired.as_mut().unwrap().as_mut().poll(cx);
}
Maybe you should match the output of poll in case it returns Ready, but hey, it's a new timeout, it's probably pending yet, and it seems to work nicely.

// This is another solution. I think it is better.
impl<IO: io::AsyncRead + Unpin> io::AsyncRead for PrudentIo<IO> {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<io::Result<usize>> {
let this = self.get_mut();
let io = Pin::new(&mut this.io);
if let Poll::Ready(res) = io.poll_read(cx, buf) {
return Poll::Ready(res);
}
loop {
if let Some(expired) = this.expired.as_mut() {
ready!(expired.poll(cx));
this.expired.take();
return Poll::Ready(Err(io::ErrorKind::TimedOut.into()));
}
let timeout = Timer::after(this.timeout);
this.expired = Some(timeout);
}
}
}

// 1. smol used, not async_std.
// 2. IO should be 'static.
// 3. when timeout, read_poll return Poll::Ready::Err(io::ErrorKind::Timeout)
use {
smol::{future::FutureExt, io, ready, Timer},
std::{
future::Future,
pin::Pin,
task::{Context, Poll},
time::Duration,
},
};
// --
pub struct PrudentIo<IO> {
expired: Option<Pin<Box<dyn Future<Output = io::Result<usize>>>>>,
timeout: Duration,
io: IO,
}
impl<IO> PrudentIo<IO> {
pub fn new(timeout: Duration, io: IO) -> Self {
PrudentIo {
expired: None,
timeout,
io,
}
}
}
impl<IO: io::AsyncRead + Unpin + 'static> io::AsyncRead for PrudentIo<IO> {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<io::Result<usize>> {
let this = self.get_mut();
loop {
if let Some(expired) = this.expired.as_mut() {
let res = ready!(expired.poll(cx))?;
this.expired.take();
return Ok(res).into();
}
let timeout = this.timeout.clone();
let (io, read_buf) = unsafe {
// Safety: ONLY used in poll_read method.
(&mut *(&mut this.io as *mut IO), &mut *(buf as *mut [u8]))
};
let fut = async move {
let timeout_fut = async {
Timer::after(timeout).await;
io::Result::<usize>::Err(io::ErrorKind::TimedOut.into())
};
let read_fut = io::AsyncReadExt::read(io, read_buf);
let res = read_fut.or(timeout_fut).await;
res
}
.boxed_local();
this.expired = Some(fut);
}
}
}
impl<IO: io::AsyncWrite + Unpin> io::AsyncWrite for PrudentIo<IO> {
fn poll_write(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
Pin::new(&mut self.io).poll_write(cx, buf)
}
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Pin::new(&mut self.io).poll_flush(cx)
}
fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Pin::new(&mut self.io).poll_close(cx)
}
}

Related

Implement Future trait based on future available inside the struct

I'm trying to create a DelayedValue future that resolves to a value after a certain time period has elapsed. To do this I simply wanted to wrap the Sleep future from tokio crate. But I get errors relating to Pin and no matter what I do I can't seem to call the poll method on the underlying Sleep member.
For reference here is a full program which fails to compile but should illustrate what I want:
use futures::task::{Context, Poll};
use futures::Future;
use std::pin::Pin;
use tokio::time::{sleep, Sleep, Duration};
struct DelayedValue<T> {
value: T,
sleep: Sleep,
}
impl<T> Future for DelayedValue<T> {
type Output = T;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
match &mut self.sleep.poll(cx) {
Poll::Ready(()) => Poll::Ready(self.value),
x => x,
}
}
}
#[tokio::main]
async fn main() {
let dv = DelayedValue {
value: 10_u8,
sleep: sleep(Duration::from_millis(5000)),
};
println!("waiting for delayed value");
let v = dv.await;
println!("delayed value: {}", v);
}
There is also a playground link: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=d573d8dcbbef5c99314d98cacc3d6c92
The easiest way is to use pin-project or pin-project-lite:
pin_project_lite::pin_project! {
struct DelayedValue<T> {
value: Option<T>,
#[pin]
sleep: Sleep,
}
}
impl<T> Future for DelayedValue<T> {
type Output = T;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project();
match this.sleep.poll(cx) {
Poll::Ready(()) => Poll::Ready(this.value.take().unwrap()),
Poll::Pending => Poll::Pending,
}
}
}
For reference, since I only needed this for this struct I opted to not use pin-project. Instead I implemented it myself for the field I needed:
#[derive(Debug)]
pub struct DelayedValue<T: Copy> {
value: T,
sleep: Sleep,
}
impl<T: Copy> DelayedValue<T> {
pub fn new(value: T, sleep: Sleep) -> DelayedValue<T> {
DelayedValue {value, sleep}
}
}
impl<T: Copy> Future for DelayedValue<T> {
type Output = T;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let x = self.value;
let s = unsafe { self.map_unchecked_mut(|s| &mut s.sleep) };
match &mut s.poll(cx) {
Poll::Ready(()) => Poll::Ready(x),
Poll::Pending => Poll::Pending,
}
}
}
The easiest is probably to just map the result of a Sleep future instead of implementing a full new struct:
use futures::FutureExt;
use tokio::time::{ Duration, sleep };
#[tokio::main]
async fn main() {
let dv = sleep (Duration::from_millis (5000)).map (|_| { 10_u8 });
println!("waiting for delayed value");
let v = dv.await;
println!("delayed value: {}", v);
}
Playground

How to sleep in Future::poll()?

I want to sleep 1 second in poll(), but it gets stuck in Pending.
From what I understand, I pass it &mut cx and it should wake up the current task after 1 second.
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use std::time::{Duration};
struct MyDelay {}
impl Future for MyDelay {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
println!("poll");
let sleep = tokio::time::sleep(Duration::from_secs(1));
tokio::pin!(sleep);
sleep.poll(cx)
}
}
#[tokio::main]
async fn main() {
let delay = MyDelay{};
let a = delay.await;
dbg!(a);
}
Play url: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=ed6c09df100f1140ddc85b2ae600ab93
Each time you poll you create a new Sleep future, and it starts from scratch.
Instead, you should store the Sleep inside your future. It is easy with pin-project-lite:
pin_project_lite::pin_project! {
struct MyDelay {
#[pin]
sleep: tokio::time::Sleep,
}
}
impl MyDelay {
fn new() -> Self {
Self {
sleep: tokio::time::sleep(Duration::from_secs(1)),
}
}
}
impl Future for MyDelay {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
println!("poll");
let this = self.project();
this.sleep.poll(cx)
}
}
Playground.
Note that the number of times this will be polled is not guaranteed, and may not be constant.

Why does my Future implementation get stuck at the start? [duplicate]

This question already has answers here:
How to implement a Future or Stream that polls an async fn?
(2 answers)
Closed 5 months ago.
Although the code compiles, I don't understand why when using await directly it gets stuck in the first request. Why do I need to use the method execute_requests instead of calling it on the Future implementation?
// ...
async fn send(url: &str) {
println!("Sending to URL {}", url);
// Simulate the sending process
sleep(Duration::from_millis(500)).await;
}
type Request = Pin<Box<dyn Future<Output = ()>>>;
struct Proxy;
impl Proxy {
async fn execute_requests(&self) {
let request_1 = async {
send("url 1").await;
};
let request_2 = async {
send("url 2").await;
};
let mut requests: Vec<Request> = vec![];
requests.push(Box::pin(request_2));
requests.push(Box::pin(request_1));
while let Some(request) = requests.pop() {
request.await;
}
}
}
impl Future for Proxy {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut queue_process = Box::pin(self.execute_requests());
queue_process.as_mut().poll(cx)
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let proxy = Proxy;
// Executes both requests
// Ok(proxy.execute_requests().await)
// FIXME: Timeouts on the first request
Ok(proxy.await)
}
Rust Playground
execute_requests is a simplification: it needs access to self to get the requests and other things.
Each time you poll, you create a new execute_requests() future and poll it once. It will never advance to the next poll - next time your poll() is called, a new future will be created and will be polled once, and so on.
Instead, you should store the execute_requests() future within Proxy, and poll the same future:
struct Proxy(Pin<Box<dyn Future<Output = ()>>>);
impl Proxy {
fn new() -> Self {
Self(Box::pin(Self::execute_requests()))
}
async fn execute_requests() {
let request_1 = async {
send("url 1").await;
};
let request_2 = async {
send("url 2").await;
};
let mut requests: Vec<Request> = vec![];
requests.push(Box::pin(request_2));
requests.push(Box::pin(request_1));
while let Some(request) = requests.pop() {
request.await;
}
}
}
impl Future for Proxy {
type Output = ();
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
self.0.as_mut().poll(cx)
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let proxy = Proxy::new();
// Executes both requests
// Ok(proxy.execute_requests().await)
// FIXME: Timeouts on the first request
Ok(proxy.await)
}
Playground.

try_lock on futures::lock::Mutex outside of async?

I'm trying to implement Async read for a struct that has a futures::lock::Mutex:
pub struct SmolSocket<'a> {
stack: Arc<futures::lock::Mutex<SmolStackWithDevice<'a>>>,
}
impl<'a> AsyncRead for SmolSocket<'a> {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut tokio::io::ReadBuf<'_>
) -> Poll<std::io::Result<()>> {
block_on(self.stack).read(...)
}
}
The problem is that, since poll_read is not async, I cannot call await. But I also don't want to, as it'd block. I could call try_lock to try and if not, I'd register a Waker to be called by SmolSocket in the future.
Since I cannot do that either because it's not async, is there a version of block_on that does the same as try_lock for futures::lock::Mutex outside of async?
You probably mean to poll the MutexLockFuture instead, this can for example be done with the core::task::ready! macro, which desugars as following:
let num = match fut.poll(cx) {
Poll::Ready(t) => t,
Poll::Pending => return Poll::Pending,
};
To poll a future, you also need to pin it (ensure it doesn't get moved). This can be done on the stack with tokio::pin!, or Pin::new if the type is already Unpin (MutexLockFuture is), or by moving onto the heap with Box::pin.
Below is a runnable example.
⚠️ KEEP READING TO SEE WHY YOU DON'T WANT TO DO THIS!
#![feature(ready_macro)]
use core::{
future::Future,
pin::Pin,
task::{ready, Context, Poll},
};
use std::sync::Arc;
use tokio::io::{AsyncRead, AsyncReadExt};
pub struct SmolStackWithDevice<'a> {
counter: usize,
data: &'a [u8],
}
impl<'a> AsyncRead for SmolStackWithDevice<'a> {
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut tokio::io::ReadBuf<'_>,
) -> Poll<std::io::Result<()>> {
if self.counter % 2 == 0 {
self.counter += 1;
cx.waker().wake_by_ref();
println!("read nothing");
return Poll::Pending;
}
buf.put_slice(&[self.data[self.counter / 2]]);
self.counter += 1;
println!("read something");
Poll::Ready(Ok(()))
}
}
pub struct SmolSocket<'a> {
stack: Arc<futures::lock::Mutex<SmolStackWithDevice<'a>>>,
}
impl<'a> AsyncRead for SmolSocket<'a> {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut tokio::io::ReadBuf<'_>,
) -> Poll<std::io::Result<()>> {
let mut lock_fut = self.stack.lock();
let pinned_lock_fut = Pin::new(&mut lock_fut);
let mut guard = ready!(pinned_lock_fut.poll(cx));
println!("acquired lock");
let pinned_inner = Pin::new(&mut *guard);
pinned_inner.poll_read(cx, buf)
}
}
#[tokio::main(flavor = "current_thread")]
async fn main() {
let data = b"HORSE";
let mut buf = [0; 5];
let mut s = SmolSocket {
stack: Arc::new(
SmolStackWithDevice {
counter: 0,
data: &data[..],
}
.into(),
),
};
s.read_exact(&mut buf).await.unwrap();
println!("{}", String::from_utf8_lossy(&buf));
}
Look at it go! (in Rust Playground)
⚠️ KEEP READING TO SEE WHY YOU DON'T WANT TO DO THIS!
So, what is the problem?
Well, as you can see from the output, whenever we succeed in acquiring the lock, but the underlying source is not ready to read, or only gives us a small read, we drop the lock, and on the next poll we will have to acquire it again.
This is a good point to remember that async flavors of Mutex are only recommended over std or parking_lot when it is expected that the Guard from a successful locking will be held across an await, or explicitly stored in a Future data structure.
We are not doing that here, we are only ever exercising the fast path equivalent to Mutex::try_lock, because whenever the lock is not immediately available, we drop the MutexLockFuture instead of waiting to be waked to poll it again.
However, storing the lock in the data structure would make it easy to accidentally deadlock. So a good design might be creating an awkward-to-store(borrowing) AsyncRead adapter that wraps the lock:
pub struct SmolSocket<'a> {
stack: Arc<futures::lock::Mutex<SmolStackWithDevice<'a>>>,
}
impl<'a> SmolSocket<'a> {
fn read(&'a self) -> Reader<'a> {
Reader::Locking(self.stack.lock())
}
}
pub enum Reader<'a> {
Locking(futures::lock::MutexLockFuture<'a, SmolStackWithDevice<'a>>),
Locked(futures::lock::MutexGuard<'a, SmolStackWithDevice<'a>>),
}
impl<'a> AsyncRead for Reader<'a> {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut tokio::io::ReadBuf<'_>,
) -> Poll<std::io::Result<()>> {
let this = self.get_mut();
match this {
Reader::Locking(f) => {
*this = Reader::Locked(ready!(Pin::new(f).poll(cx)));
println!("acquired lock");
Pin::new(this).poll_read(cx, buf)
}
Reader::Locked(l) => Pin::new(&mut **l).poll_read(cx, buf),
}
}
}
#[tokio::main(flavor = "current_thread")]
async fn main() {
let data = b"HORSE";
let mut buf = [0; 5];
let s = SmolSocket {
stack: Arc::new(
SmolStackWithDevice {
counter: 0,
data: &data[..],
}
.into(),
),
};
s.read().read_exact(&mut buf).await.unwrap();
println!("{}", String::from_utf8_lossy(&buf));
}
Look at it go! (executable Playground link)
This works out, because both the LockFuture and our SmolStackWithDevice are Unpin (non-self-referential) and so we don't have to guarantee we aren't moving them.
In a general case, for example if your SmolStackWithDevice is not Unpin, you'd have to project the Pin like this:
unsafe {
let this = self.get_unchecked_mut();
match this {
Reader::Locking(f) => {
*this = Reader::Locked(ready!(Pin::new_unchecked(f).poll(cx)));
println!("acquired lock");
Pin::new_unchecked(this).poll_read(cx, buf)
}
Reader::Locked(l) => Pin::new_unchecked(&mut **l).poll_read(cx, buf),
}
}
Not sure how to encapsulate the unsafety, pin_project isn't enough here, as we also need to dereference the guard.
But this only acquires the lock once, and drops it when the Reader is dropped, so, great success.
You can also see that it doesn't deadlock if you do
let mut r1 = s.read();
let mut r2 = s.read();
r1.read_exact(&mut buf[..3]).await.unwrap();
drop(r1);
r2.read_exact(&mut buf[3..]).await.unwrap();
println!("{}", String::from_utf8_lossy(&buf));
This is only possible because we deferred locking until polling.

Using a custom transporter for Rust's hyper http crate

ps: the answer below helped but it's not the answer I need, I have a new problem and I edited the question
I'm trying to make a custom transporter for the hyper http crate, so I can transport http packets in my own way.
Hyper's http client can be passed a custom https://docs.rs/hyper/0.14.2/hyper/client/connect/trait.Connect.html here:
pub fn build<C, B>(&self, connector: C) -> Client<C, B> where C: Connect + Clone, B: HttpBody + Send, B::Data: Send,
If we look at
impl<S, T> Connect for S where
S: Service<Uri, Response = T> + Send + 'static,
S::Error: Into<Box<dyn StdError + Send + Sync>>,
S::Future: Unpin + Send,
T: AsyncRead + AsyncWrite + Connection + Unpin + Send + 'static,
the type T, which is the type of the Response, must implement AsyncRead + AsyncWrite, so I've chosen type Response = Cursor<Vec<u8>>.
Here's my custom transporter with a Response of type std::io::Cursor wrapped in CustomResponse so I can implement AsyncWrite and AsyncRead to it:
use hyper::service::Service;
use core::task::{Context, Poll};
use core::future::Future;
use std::pin::Pin;
use std::io::Cursor;
use hyper::client::connect::{Connection, Connected};
use tokio::io::{AsyncRead, AsyncWrite};
#[derive(Clone)]
pub struct CustomTransporter;
unsafe impl Send for CustomTransporter {}
impl CustomTransporter {
pub fn new() -> CustomTransporter {
CustomTransporter{}
}
}
impl Connection for CustomTransporter {
fn connected(&self) -> Connected {
Connected::new()
}
}
pub struct CustomResponse {
//w: Cursor<Vec<u8>>,
v: Vec<u8>,
i: i32
}
unsafe impl Send for CustomResponse {
}
impl Connection for CustomResponse {
fn connected(&self) -> Connected {
println!("connected");
Connected::new()
}
}
impl AsyncRead for CustomResponse {
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut tokio::io::ReadBuf<'_>
) -> Poll<std::io::Result<()>> {
self.i+=1;
if self.i >=3 {
println!("poll_read for buf size {}", buf.capacity());
buf.put_slice(self.v.as_slice());
println!("did poll_read");
Poll::Ready(Ok(()))
} else {
println!("poll read pending, i={}", self.i);
Poll::Pending
}
}
}
impl AsyncWrite for CustomResponse {
fn poll_write(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8]
) -> Poll<Result<usize, std::io::Error>>{
//let v = vec!();
println!("poll_write____");
let s = match std::str::from_utf8(buf) {
Ok(v) => v,
Err(e) => panic!("Invalid UTF-8 sequence: {}", e),
};
println!("result: {}, size: {}, i: {}", s, s.len(), self.i);
if self.i>=0{
//r
Poll::Ready(Ok(s.len()))
}else{
println!("poll_write pending");
Poll::Pending
}
}
fn poll_flush(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>
) -> Poll<Result<(), std::io::Error>> {
println!("poll_flush");
if self.i>=0{
println!("DID poll_flush");
Poll::Ready(Ok(()))
}else{
println!("poll_flush pending");
Poll::Pending
}
}
fn poll_shutdown(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>
) -> Poll<Result<(), std::io::Error>>
{
println!("poll_shutdown");
Poll::Ready(Ok(()))
}
}
impl Service<hyper::Uri> for CustomTransporter {
type Response = CustomResponse;
type Error = hyper::http::Error;
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
println!("poll_ready");
Poll::Ready(Ok(()))
//Poll::Pending
}
fn call(&mut self, req: hyper::Uri) -> Self::Future {
println!("call");
// create the body
let body: Vec<u8> = "HTTP/1.1 200 OK\nDate: Mon, 27 Jul 2009 12:28:53 GMT\nServer: Apache/2.2.14 (Win32)\nLast-Modified: Wed, 22 Jul 2009 19:15:56 GMT\nContent-Length: 88\nContent-Type: text/html\nConnection: Closed<html><body><h1>Hello, World!</h1></body></html>".as_bytes()
.to_owned();
// Create the HTTP response
let resp = CustomResponse{
//w: Cursor::new(body),
v: body,
i: 0
};
// create a response in a future.
let fut = async move{
Ok(resp)
};
println!("gonna return from call");
// Return the response as an immediate future
Box::pin(fut)
}
}
Then I use it like this:
let connector = CustomTransporter::new();
let client: Client<CustomTransporter, hyper::Body> = Client::builder().build(connector);
let mut res = client.get(url).await.unwrap();
However, it gets stuck and hyper never reads my response, but it writes the GET to it.
Here's a complete project for testing: https://github.com/lzunsec/rust_hyper_custom_transporter/blob/39cd036fc929057d975a71969ccbe97312543061/src/custom_req.rs
RUn like this:
cargo run http://google.com
I cannot simply implement Send to Future, and I cannot change Future by a wrapper. What should I do here?
It looks like the problem is your Service::Future is missing the Send constraint. The future being returned in call is already Send so it will work with the simple change:
impl Service<hyper::Uri> for CustomTransporter {
type Response = CustomResponse;
type Error = hyper::http::Error;
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
// ^^^^
...
Your code has a few other errors: un-inferred vec!(), self: Pin<...> missing mut, CustomResponse should be pub...
You can specify the B of client by using inference:
let client: Client<CustomTransporter, hyper::Body> = Client::builder().build(connector);
Or by using the turbofish operator on build:
let client = Client::builder().build::<CustomTransporter, hyper::Body>(connector);
I don't know enough about creating custom hyper transports to know if its functional, but these fixes make it compile. Hopefully it helps you make progress.

Resources