impl AsyncRead for tonic::Streaming - rust

I am trying to take the tonic routeguide tutorial, and turn the client into a rocket server. I am just taking the response and converting from the gRPC to a string.
service RouteGuide {
rpc GetFeature(Point) returns (Feature) {}
rpc ListFeatures(Rectangle) returns (stream Feature) {}
}
This works well enough for GetFeature. For the ListFeatures query, just as Tonic allows the client the stream in the response, I wanted to pass this on to the Rocket client. I see that Rocket supports streaming responses, but I need to implement the AsyncRead trait.
Is there any way to do something like this? Below is a trimmed down version of about what I was doing:
struct FeatureStream {
stream: tonic::Streaming<Feature>,
}
impl AsyncRead for FeatureStream {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<std::io::Result<()>> {
// Write out as utf8 any response messages.
match Pin::new(&mut self.stream.message()).poll(cx) {
Poll::Pending => Poll::Pending,
Poll::Ready(feature) => Poll::Pending,
}
}
}
#[get("/list_features")]
async fn list_features(client: State<'_, RouteGuideClient<Channel>>) -> Stream<FeatureStream> {
let rectangle = Rectangle {
low: Some(Point {
latitude: 400_000_000,
longitude: -750_000_000,
}),
high: Some(Point {
latitude: 420_000_000,
longitude: -730_000_000,
}),
};
let mut client = client.inner().clone();
let stream = client
.list_features(Request::new(rectangle))
.await
.unwrap()
.into_inner();
Stream::from(FeatureStream { stream })
}
#[rocket::launch]
async fn rocket() -> rocket::Rocket {
rocket::ignite()
.manage(
create_route_guide_client("http://[::1]:10000")
.await
.unwrap(),
)
.mount("/", rocket::routes![list_features,])
}
With the error:
error[E0277]: `from_generator::GenFuture<[static generator#Streaming<Feature>::message::{closure#0} for<'r, 's, 't0, 't1, 't2> {ResumeTy, &'r mut Streaming<Feature>, [closure#Streaming<Feature>::message::{closure#0}::{closure#0}], rocket::futures::future::PollFn<[closure#Streaming<Feature>::message::{closure#0}::{closure#0}]>, ()}]>` cannot be unpinned
--> src/web_user.rs:34:15
|
34 | match Pin::new(&mut self.stream.message()).poll(cx) {
| ^^^^^^^^ within `impl std::future::Future`, the trait `Unpin` is not implemented for `from_generator::GenFuture<[static generator#Streaming<Feature>::message::{closure#0} for<'r, 's, 't0, 't1, 't2> {ResumeTy, &'r mut Streaming<Feature>, [closure#Streaming<Feature>::message::{closure#0}::{closure#0}], rocket::futures::future::PollFn<[closure#Streaming<Feature>::message::{closure#0}::{closure#0}]>, ()}]>`
|
::: /home/matan/.cargo/registry/src/github.com-1ecc6299db9ec823/tonic-0.4.0/src/codec/decode.rs:106:40
|
106 | pub async fn message(&mut self) -> Result<Option<T>, Status> {
| ------------------------- within this `impl std::future::Future`
|
= note: required because it appears within the type `impl std::future::Future`
= note: required because it appears within the type `impl std::future::Future`
= note: required by `Pin::<P>::new`

The problem is generated Future from tonic::Streaming<Feature>::message() doesn't implement Unpin since it is an async function. Let's label this type as MessageFuture, you cannot pin &mut MessageFuture pointer safely because the dereferenced type MessageFuture doesn't implement Unpin.
Why it is not safe?
From reference, implementation of Unpin brings:
Types that can be safely moved after being pinned.
It means if T:!Unpin then Pin<&mut T> is not movable, this is important because Futures created by async block has no Unpin implementation since it might hold reference of a member from itself, and if you move the T the pointee of this reference will also be moved, but the reference will still point the same address, to prevent this it should not be movable. Please read "Pinning" section from async-book to visualize the reason.
Note: T:!Unpin means T is the type that has no Unpin implementation.
Solution
message() function is helper to pick next message from the tonic::Streaming<T>. You don't particularly need to call message() to pick next element from the stream, you already have actual stream in your structure.
struct FeatureStream {stream: tonic::Streaming<Feature>}
You can await for the next message for AsyncRead like:
impl AsyncRead for FeatureStream {
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<std::io::Result<()>> {
//it returns Pending for all cases as your code does, you can change it as you want
match self.stream.poll_next_unpin(cx) {
Poll::Ready(Some(Ok(m))) => Poll::Pending,
Poll::Ready(Some(Err(e))) => Poll::Pending,
Poll::Ready(None) => Poll::Pending,
Poll::Pending => Poll::Pending
}
}
}
Please note that tonic::Streaming<T> has implementation of Unpin(reference)

Thank you Omer Erden for answering this. So it came down to implementing AsyncRead based on the futures::Stream trait, which tonic::Streaming implements. Here is the code I actually used.
impl AsyncRead for FeatureStream {
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<std::io::Result<()>> {
use futures::stream::StreamExt;
use std::io::{Error, ErrorKind};
match self.stream.poll_next_unpin(cx) {
Poll::Ready(Some(Ok(m))) => {
buf.put_slice(format!("{:?}\n", m).as_bytes());
Poll::Ready(Ok(()))
}
Poll::Ready(Some(Err(e))) => {
Poll::Ready(Err(Error::new(ErrorKind::Other, format!("{:?}", e))))
}
Poll::Ready(None) => {
// None from a stream means the stream terminated. To indicate
// that from AsyncRead we return Ok and leave buf unchanged.
Poll::Ready(Ok(()))
}
Poll::Pending => Poll::Pending,
}
}
}
In the meantime my workaround was to create both ends of a TcpStream (which implements AsyncRead) and return one end of it while spawning a separate task to actually write out the results.
#[get("/list_features")]
async fn list_features(
client: State<'_, RouteGuideClient<Channel>>,
tasks: State<'_, Mutex<Vec<tokio::task::JoinHandle<()>>>>,
) -> Result<Stream<TcpStream>, Debug<std::io::Error>> {
let mut client = client.inner().clone();
let mut feature_stream = client
.list_features(Request::new(Rectangle {
low: Some(Point {
latitude: 400000000,
longitude: -750000000,
}),
high: Some(Point {
latitude: 420000000,
longitude: -730000000,
}),
}))
.await
.unwrap()
.into_inner();
// Port 0 tells to operating system to choose an unused port.
let tcp_listener = TcpListener::bind(("127.0.0.1", 0)).await?;
let socket_addr = tcp_listener.local_addr().unwrap();
tasks.lock().unwrap().push(tokio::spawn(async move {
let mut tcp_stream = TcpStream::connect(socket_addr).await.unwrap();
while let Some(feature) = feature_stream.message().await.unwrap() {
match tcp_stream
.write_all(format!("{:?}\n", feature).as_bytes())
.await
{
Ok(()) => (),
Err(e) => panic!(e),
}
}
println!("End task");
()
}));
Ok(Stream::from(tcp_listener.accept().await?.0))
}

Related

how to implement trait futures::stream::Stream?

So I'm getting a Response from the reqwest crate and passing it to a HttpResponseBuilder from the actix_web create. However I've tried and failed to understand how to implement the Stream trait from the futures create on a custom struct to act as a middleman and copy the contents down to a file.
I've tried doing this so far, but I'm not sure what to put inside that poll_next function to make it all work.
struct FileCache {
stream: Box<dyn futures::Stream<Item = reqwest::Result<bytes::Bytes>>>,
}
impl FileCache {
fn new(stream: Box<dyn futures::Stream<Item = reqwest::Result<bytes::Bytes>>>) -> Self {
FileCache { stream }
}
}
impl Stream for FileCache {
type Item = reqwest::Result<bytes::Bytes>;
fn poll_next(
self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Option<Self::Item>> {
}
}
This is possible but requires you to understand what pinning is and how to use it safely.
Basically, we just need to proxy to self.stream.poll_next(), but this method accepts Pin<&mut Self> (as you can see in your own implementation). Storing the box as Pin<Box<T>> instead of Box<T> will give us a way to obtain this Pin relatively easily, without requiring unsafe. Making this change is straightforward, since there is a From implementation allowing conversion of Box<T> to Pin<Box<T>> directly:
use std::pin::Pin;
use std::task::{Context, Poll};
use futures::Stream;
struct FileCache {
stream: Pin<Box<dyn Stream<Item = reqwest::Result<bytes::Bytes>>>>,
}
impl FileCache {
fn new(stream: Box<dyn Stream<Item = reqwest::Result<bytes::Bytes>>>) -> FileCache {
FileCache { stream: stream.into() }
}
}
Now we have to figure out how to go from Pin<&mut FileCache> to Pin<&mut dyn Stream<...>>. The correct incantation here is self.get_mut().stream.as_mut():
impl Stream for FileCache {
type Item = reqwest::Result<bytes::Bytes>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
match self.get_mut().stream.as_mut().poll_next(cx) {
Poll::Pending => Poll::Pending,
Poll::Ready(v) => {
// Do what you need to do with v here.
Poll::Ready(v)
}
}
}
}
The catch is that poll_next isn't async and so you can't asynchronously wait for whatever you're doing with v. bytes::Bytes is atomically-refcounted, though, so you could clone the inner bytes::Bytes value and spawn a separate task on your executor, which is probably what you want to do anyway so that whoever is waiting for FileCache doesn't have to wait for that task to complete before using the data. So you'd do something like:
Poll::Ready(v) => {
if let Some(Ok(ref bytes)) = &v {
let bytes = bytes.clone();
spawn_new_task(async move {
// Do something with bytes
});
}
Poll::Ready(v)
}
Where spawn_new_task() is the function your executor provides, e.g. tokio::spawn().
Now that we can see what we're doing here, we can simplify this down and eliminate the match by pushing Poll::Ready into our pattern, and unconditionally returning whatever the inner poll_next() call did:
impl Stream for FileCache {
type Item = reqwest::Result<bytes::Bytes>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let r = self.get_mut().stream.as_mut().poll_next(cx);
if let Poll::Ready(Some(Ok(ref bytes))) = &r {
let bytes = bytes.clone();
spawn_new_task(async move {
// Do something with bytes
});
}
r
}
}

Rust: how to build a hashmap of functions and mutate it at runtime

I'm trying to parse a bunch of network packets captured in the network and perform some analysis. But I'm having a hard time dealing with procotols with dynamic port mechanism...
Some backgrounds on what "dynamic port" is: For some network protocols like FTP, 2 separate TCP channels are used for communication(command channel and data channel). Command channel uses a well-known port (like 21 for FTP), but the port that data channel uses is "dynamic": it's randomly selected by the server and wrapped in the server response for PASV command.
Typical communication procedure:
client establishes command channel( connects to well-known server port X)
client asks for the port of the data channel
server sends back a port Y
client establishes data channel (connects to server port Y)
client sends command on command channel(port X), server responses data on data channel(port Y)
When I'm parsing TCP packets, I use src and dst port to decide which parser function to use. To handle this situation, I think I need to keep a HashMap<u16, Parser>, mapping port to parser functions. But I just cannot make it work(mostly confusing lifetime issues). I think the main problem is: the parser functions will need to update the Hashmap that contains it. But I don't know how to solve this...
Here's what I came up with right now(still does not compile), could anyone offer some better ideas? Am I doing this the wrong way? Thanks!
use bytes::{BufMut, BytesMut};
use nom::number::complete::{be_u16, be_u32};
use nom::IResult;
use nom::bytes::complete::take;
use std::collections::HashMap;
#[derive(Debug)]
struct Command<'a> {
command: &'a [u8],
port: u16,
}
#[derive(Debug)]
struct Data<'a> {
path: &'a [u8],
}
fn parse_command<'a>(input: &'a [u8]) -> IResult<&'a [u8], Command<'a>> {
let (input, command_len) = be_u32(input)?;
let (input, command) = take(command_len as usize)(input)?;
let (input, port) = be_u16(input)?;
Ok((input, Command { command, port }))
}
fn parse_data<'a>(input: &'a [u8]) -> IResult<&'a [u8], Data<'a>> {
let (input, len) = be_u32(input)?;
let (input, path) = take(len as usize)(input)?;
Ok((input, Data { path }))
}
fn make_command_bytes(command: &[u8], data_port: u16) -> BytesMut {
let mut buf = BytesMut::with_capacity(1024);
buf.put_u32(command.len() as u32);
buf.put(&command[..]);
buf.put_u16(data_port);
buf
}
fn make_data_bytes(path: &[u8]) -> BytesMut {
let mut buf = BytesMut::with_capacity(1024);
buf.put_u32(path.len() as u32);
buf.put(&path[..]);
buf
}
#[derive(Debug)]
enum Payload<'a> {
Command(Command<'a>),
Data(Data<'a>),
}
trait Parser<'a> {
type Output;
fn parse(&mut self, input: &'a [u8]) -> IResult<&'a [u8], Self::Output>;
}
struct CommandParser<'a, 'b> {
map: &'b mut HashMap<u16, Box<dyn Parser<'a, Output = Payload<'a>>>>,
}
struct DataParser<'a, 'b> {
map: &'b mut HashMap<u16, Box<dyn Parser<'a, Output = Payload<'a>>>>,
}
impl<'a, 'b> Parser<'a> for CommandParser<'a, 'b> {
type Output = Payload<'a>;
fn parse(&mut self, input: &'a [u8]) -> IResult<&'a [u8], Self::Output> {
let (input, command) = parse_command(input)?;
self.map
.insert(command.port, Box::new(DataParser { map: self.map }))
.unwrap();
Ok((input, Payload::Command(command)))
}
}
impl<'a, 'b> Parser<'a> for DataParser<'a, 'b> {
type Output = Payload<'a>;
fn parse(&mut self, input: &'a [u8]) -> IResult<&'a [u8], Self::Output> {
let (input, data) = parse_data(input)?;
Ok((input, Payload::Data(data)))
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let data_port = 1234;
let command = b"test";
let data = make_command_bytes(&command[..], data_port);
let mut parser_map = HashMap::<u16, Box<dyn Parser<Output = Payload>>>::new();
parser_map.insert(21, Box::new(CommandParser { map: &parser_map }));
let parser = parser_map.get(&21).unwrap();
match parser.parse(&data) {
Ok((input, result)) => {
println!("result: {:?}, remain: {:?}", result, input);
}
Err(e) => {
println!("{:?}", e);
}
}
Ok(())
}
the compiler says:
--> src/main.rs:91:57
|
91 | parser_map.insert(21, Box::new(CommandParser { map: &parser_map }));
| ^^^^^^^^^^^ types differ in mutability
|
= note: expected mutable reference `&mut HashMap<u16, Box<(dyn Parser<'_, Output = Payload<'_>> + 'static)>>`
found reference `&HashMap<u16, Box<dyn Parser<'_, Output = Payload<'_>>>>`
where does + 'static come from?
As Jmb stated in the comments, you cant store the reference in each struct.
So I would keep the reference just in the main function and mutate the map from there:
match parser.parse(&data) {
// divide `Payload::Data` and `Payload::Command`
Ok((input, Payload::Data(data))) => {
println!("data: {:?}, remain: {:?}", data, input);
}
// destructure `Command` into `command` and `port`
Ok((input, Payload::Command(Command { command, port }))) => {
println!("command: {:?} for {}, remain: {:?}", command, port, input);
parser_map.insert(port, Box::new(DataParser {})).unwrap();
}
Err(e) => {
println!("{:?}", e);
}
}
Now you can remove the map from both CommandParser and DataParser and remove the unused parameters from the struct and impls:
struct CommandParser {}
struct DataParser {}
impl<'a> Parser<'a> for CommandParser {
type Output = Payload<'a>;
fn parse(&mut self, input: &'a [u8]) -> IResult<&'a [u8], Self::Output> {
let (input, command) = parse_command(input)?;
Ok((input, Payload::Command(command)))
}
}
impl<'a> Parser<'a> for DataParser {
type Output = Payload<'a>;
fn parse(&mut self, input: &'a [u8]) -> IResult<&'a [u8], Self::Output> {
let (input, data) = parse_data(input)?;
Ok((input, Payload::Data(data)))
}
}
Now there is only an Error about mutability:
error[E0596]: cannot borrow `**parser` as mutable, as it is behind a `&` reference
--> src/main.rs:118:11
|
117 | let parser = parser_map.get(&21).unwrap();
| ------ help: consider changing this to be a mutable reference: `&mut Box<dyn Parser<'_, Output = Payload<'_>>>`
118 | match parser.parse(&data) {
| ^^^^^^^^^^^^^^^^^^^ `parser` is a `&` reference, so the data it refers to cannot be borrowed as mutable
This can be fixed by changing let parser = parser_map.get(&21).unwrap(); to
let parser = parser_map.get_mut(&21).unwrap();
Note that the unwrap on parser_map.insert(port, Box::new(DataParser {})).unwrap(); panics because 1234 was not a port in the map before.
Here is the full code
I personally would remove trait Parser and both structs Commandparser and DataParser and store just both functions in the parser_map.

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.

Creating a stream of values while calling async fns?

I can't figure out how to provide a Stream where I await async functions to get the data needed for the values of the stream.
I've tried to implement the the Stream trait directly, but I run into issues because I'd like to use async things like awaiting, the compiler does not want me to call async functions.
I assume that I'm missing some background on what the goal of Stream is and I'm just attacking this incorrectly and perhaps I shouldn't be looking at Stream at all, but I don't know where else to turn. I've seen the other functions in the stream module that could be useful, but I'm unsure how I could store any state and use these functions.
As a slightly simplified version of my actual goal, I want to provide a stream of 64-byte Vecs from an AsyncRead object (i.e. TCP stream), but also store a little state inside whatever logic ends up producing values for the stream, in this example, a counter.
pub struct Receiver<T>
where
T: AsyncRead + Unpin,
{
readme: T,
num: u64,
}
// ..code for a simple `new() -> Self` function..
impl<T> Stream for Receiver<T>
where
T: AsyncRead + Unpin,
{
type Item = Result<Vec<u8>, io::Error>;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let mut buf: [u8; 64] = [0; 64];
match self.readme.read_exact(&mut buf).await {
Ok(()) => {
self.num += 1;
Poll::Ready(Some(Ok(buf.to_vec())))
}
Err(e) => Poll::Ready(Some(Err(e))),
}
}
}
This fails to build, saying
error[E0728]: `await` is only allowed inside `async` functions and blocks
I'm using rustc 1.36.0-nightly (d35181ad8 2019-05-20) and my Cargo.toml looks like this:
[dependencies]
futures-preview = { version = "0.3.0-alpha.16", features = ["compat", "io-compat"] }
pin-utils = "0.1.0-alpha.4"
Answer copy/pasted from the reddit post by user Matthias247:
It's unfortunately not possible at the moment - Streams have to be implemented by hand and can not utilize async fn. Whether it's possible to change this in the future is unclear.
You can work around it by defining a different Stream trait which makes use of Futures like:
trait Stream<T> {
type NextFuture: Future<Output=T>;
fn next(&mut self) -> Self::NextFuture;
}
This article and this futures-rs issue have more information around it.
You can do it with gen-stream crate:
#![feature(generators, generator_trait, gen_future)]
use {
futures::prelude::*,
gen_stream::{gen_await, GenTryStream},
pin_utils::unsafe_pinned,
std::{
io,
marker::PhantomData,
pin::Pin,
sync::{
atomic::{AtomicU64, Ordering},
Arc,
},
task::{Context, Poll},
},
};
pub type Inner = Pin<Box<dyn Stream<Item = Result<Vec<u8>, io::Error>> + Send>>;
pub struct Receiver<T> {
inner: Inner,
pub num: Arc<AtomicU64>,
_marker: PhantomData<T>,
}
impl<T> Receiver<T> {
unsafe_pinned!(inner: Inner);
}
impl<T> From<T> for Receiver<T>
where
T: AsyncRead + Unpin + Send + 'static,
{
fn from(mut readme: T) -> Self {
let num = Arc::new(AtomicU64::new(0));
Self {
inner: Box::pin(GenTryStream::from({
let num = num.clone();
static move || loop {
let mut buf: [u8; 64] = [0; 64];
match gen_await!(readme.read_exact(&mut buf)) {
Ok(()) => {
num.fetch_add(1, Ordering::Relaxed);
yield Poll::Ready(buf.to_vec())
}
Err(e) => return Err(e),
}
}
})),
num,
_marker: PhantomData,
}
}
}
impl<T> Stream for Receiver<T>
where
T: AsyncRead + Unpin,
{
type Item = Result<Vec<u8>, io::Error>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
self.inner().poll_next(cx)
}
}

Resources