Byron / google-apis-rs

A binding and CLI generator for all Google APIs
http://byron.github.io/google-apis-rs
Other
1.02k stars 136 forks source link

Pubsub implementation not usable in hyper handler #270

Closed bmoyet closed 3 years ago

bmoyet commented 3 years ago

Hello,

I'm quite new to Rust, so I might be reporting something which is not an issue, forgive me if that's the case.

I'm trying to use the library to publish messages on Google Cloud PubSub, when some hyper endpoints are being called. But my project wouldn't build as soon as I'm calling the function I created to do so.

The build gives me the following error :

error[E0277]: `RefCell<Client<hyper_rustls::connector::HttpsConnector<HttpConnector>>>` cannot be shared between threads safely
  --> src/main.rs:27:25
   |
27 |     router.get("/test", Box::new(handler::test_handler));
   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `RefCell<Client<hyper_rustls::connector::HttpsConnector<HttpConnector>>>` cannot be shared between threads safely
   |
   = help: within `google_pubsub1::api::Pubsub<Client<hyper_rustls::connector::HttpsConnector<HttpConnector>>>`, the trait `Sync` is not implemented for `RefCell<Client<hyper_rustls::connector::HttpsConnector<HttpConnector>>>`
...
And a lot more message

From what I understand it is caused by the internal use of RefCell in this project which is not Sync whereas hyper needs it to be Sync.

To reproduce the issue I used the following repo https://github.com/zupzup/rust-minimal-web-service-hyper

My PubSub code is :

pub async fn send() -> Result<(), String> {
    let key = oauth2::read_service_account_key("xxx")
        .await
        .map_err(|err| format!("Cannot read service account credentials : {}", err))?;
    let secret = ServiceAccountAuthenticator::builder(key)
        .build()
        .await
        .map_err(|err| format!("Cannot build authenticator : {}", err))?;

    let hub = Pubsub::new(
        hyper::Client::builder()
            .build(hyper_rustls::HttpsConnector::with_native_roots()),
        secret);

    let msg = PubsubMessage { publish_time: None, message_id: None, attributes: None, data: Some(String::from("SGVsbG8gd29ybGQgIQ==")), ordering_key: None };
    let req = PublishRequest { messages: Some(vec![msg]) };

    let projects = hub.projects();
    println!("Sending new message");
    let result = projects
        .topics_publish(req, format!("projects/{}/topics/{}", "xxx", "xxx").as_str())
        .doit()
        .await;

    match result {
        Err(e) => match e {
            // The Error enum provides details about what exactly happened.
            // You can also just use its `Debug`, `Display` or `Error` traits
            Error::HttpError(_)
            |Error::Io(_)
            |Error::MissingAPIKey
            |Error::MissingToken(_)
            |Error::Cancelled
            |Error::UploadSizeLimitExceeded(_, _)
            |Error::Failure(_)
            |Error::BadRequest(_)
            |Error::FieldClash(_)
            |Error::JsonDecodeError(_, _) => println!("{}", e),
        },
        Ok(res) => println!("Success: {:?}", res),
    }

    return Ok(());
}

And I'm modifying the rust-minimal-web-service-hyper project this way in handler.rs :

pub async fn test_handler(ctx: Context) -> String {
    let res = pubsub::send()
        .await
        .map(|_| "Ok".to_string())
        .unwrap_or("Error".to_string());

    format!("test called, state_thing was: {}. Result : {}", ctx.state.state_thing, res)
}

When calling my method from a main function it works correctly, but as soon as I'm using it inside a hyper handler the build is failing