Async reqwest call from rpc method

Rust newbie here. I am trying to make a http request from within a json rpc method. Below is my code but it fails to compile.

se jsonrpc_tcp_server::*;
use jsonrpc_tcp_server::jsonrpc_core::*;
use futures::Future;
use reqwest::r#async::Client;

struct Page;

impl RpcMethodSimple for Page {
    type Out = Future<Item = Value, Error = Error>;
    fn call (&self, params: Params) -> Self::Out {
        println!("params = {:?}", params);
        let urls : Vec<String> =  params.parse().unwrap();
        println!("parsing successfull, url = {}", urls[0]);
        Client::new()
            .get(&urls[0])
            .send()
            .and_then(|resp| { resp.text()})
            .map_err(|e| { Error {code: ErrorCode::InternalError,
                message: format!("Failed with error {:?}", e),
                data: None}})
            .map(|data| { Value::String(data)})

    }
}


fn main() {
    let mut io = IoHandler::default();
    io.add_method("get_page",  Page{});
    let server = ServerBuilder::new(io)
        .start(&"0.0.0.0:8080".parse().unwrap())
        .expect("Server must start with no issues");

    server.wait();
}

Error:

error[E0277]: `(dyn futures::future::Future<Item = serde_json::value::Value, Error = jsonrpc_core::types::error::Error> + 'static)` cannot be sent between threads safely
 --> src/main.rs:8:6
  |
8 | impl RpcMethodSimple for Page {
  |      ^^^^^^^^^^^^^^^ `(dyn futures::future::Future<Item = serde_json::value::Value, Error = jsonrpc_core::types::error::Error> + 'static)` cannot be sent between threads safely
  |
  = help: the trait `std::marker::Send` is not implemented for `(dyn futures::future::Future<Item = serde_json::value::Value, Error = jsonrpc_core::types::error::Error> + 'static)`

error[E0277]: the size for values of type `(dyn futures::future::Future<Item = serde_json::value::Value, Error = jsonrpc_core::types::error::Error> + 'static)` cannot be known at compilation time
 --> src/main.rs:8:6
  |
8 | impl RpcMethodSimple for Page {
  |      ^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
  |
  = help: the trait `std::marker::Sized` is not implemented for `(dyn futures::future::Future<Item = serde_json::value::Value, Error = jsonrpc_core::types::error::Error> + 'static)`
  = note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0277`.
error: Could not compile `jsonrpc`.

To learn more, run the command again with --verbose.

I tried returning string, but that does not help either. From what I understand, the rpc method expects future to return Value. Any idea on how to make this work?

Make sure to implement Send + Sized for the object being returned.

Check out: https://docs.rs/jsonrpc-core/8.0.1/jsonrpc_core/trait.RpcMethodSimple.html

It tells you which types need Send. To impl Send, you must do so unsafely per: https://doc.rust-lang.org/nomicon/send-and-sync.html

unsafe impl Send for (dyn futures::future::Future<Item = serde_json::value::Value, Error = jsonrpc_core::types::error::Error> + 'static)

You will have to change the above section after for into the exact object which needs the Send Trait.

And then the compiler also is telling you that Sized is not implemented for (dyn futures::future::Future<Item = serde_json::value::Value, Error = jsonrpc_core::types::error::Error> + 'static) as well. Instead of using the same type of logic for Send as above, you would ensure that the type is bound by Sized.

Notice on the first link on the docs:
type Out: Future<Item = Value, Error = Error> + Send

Yet, in your code you have:
type Out = Future<Item = Value, Error = Error>

Looks like the compiler could figure out the Send and Sized traits. Below code compiles fine. Wouldn't reqwest return similar kind of future?

use jsonrpc_tcp_server::*;
use jsonrpc_tcp_server::jsonrpc_core::*;
use futures::prelude::*;
use reqwest::r#async::Client;

struct Page;
struct PageHelper {
    url: String,
}
impl Future for PageHelper {
    type Item = Value;
    type Error = Error;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        Ok(Async::Ready(Value::String(self.url.clone())))
    }

}

impl RpcMethodSimple for Page {
    type Out = PageHelper;
    fn call (&self, params: Params) -> Self::Out {
        let urls : Vec<String> =  params.parse().unwrap();
        PageHelper{url: urls[0].clone()}
    }
}


fn main() {
        let mut io = IoHandler::default();
        io.add_method("get_page",  Page{});
        let server = ServerBuilder::new(io)
                .start(&"0.0.0.0:8080".parse().unwrap())
                .expect("Server must start with no issues");

        server.wait();
}

Boxing the Future and adding Send marker as suggested by nologik worked. I am not sure how rpc code resolved the boxed future yet.

use jsonrpc_tcp_server::*;
use jsonrpc_tcp_server::jsonrpc_core::*;
use futures::Future;
use reqwest::r#async::Client;

struct Page;

impl RpcMethodSimple for Page {
    type Out = Box<Future<Item = Value, Error = Error> + Send>;
    fn call (&self, params: Params) -> Self::Out {
        println!("params = {:?}", params);
        let urls : Vec<String> =  params.parse().unwrap();
        println!("parsing successfull, url = {}", urls[0]);
        Box::new(Client::new()
            .get(&urls[0])
            .send()
            .and_then(|mut resp| { resp.text()})
            .map_err(|e| { Error {code: ErrorCode::InternalError,
                message: format!("Failed with error {:?}", e),
                data: None}})
            .map(|data| { Value::String(data)}))
    }
}


fn main() {
    let mut io = IoHandler::default();
    io.add_method("get_page",  Page{});
    let server = ServerBuilder::new(io)
        .start(&"0.0.0.0:8080".parse().unwrap())
        .expect("Server must start with no issues");

    server.wait();
}

I've noticed this too in my studies of Rust. Turns out, you can pass-in boxed future or impl Futures just fine, and either will resolve. Why? Well, the underlying asynchronous executor that the jsonrpc program uses accepts either. When you deref a Box<T> like *Box<T>, you have access to the inner T. In this case, if you deref a Box, you get the underlying Future. Plus, a quirk about implementing Deref for a smart pointer like Box: you have access to all the functions contained within T from the perspective of the higher-level Box<T>. As such, the underlying executor calls a function that exists whether from Box<T> or just T.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.