seanmonstar / reqwest

An easy and powerful Rust HTTP Client
https://docs.rs/reqwest
Apache License 2.0
9.76k stars 1.1k forks source link

Streaming JSON serialization for outgoing requests #1487

Open xrl opened 2 years ago

xrl commented 2 years ago

I love the optional json() feature, my HTTP code looks super tidy:

        let response = self
            .sheets
            .reqwest
            .post(url)
            .json(&self.value_range)
            .bearer_auth(self.sheets.oauth2_token)
            .send()
            .await?;

but I imagine I'm going to be streaming lots of data out this request. The code json(&T) looks like it's forcing all the data to be buffered first:

    pub fn json<T: Serialize + ?Sized>(mut self, json: &T) -> RequestBuilder {
        let mut error = None;
        if let Ok(ref mut req) = self.request {
            match serde_json::to_vec(json) {
                Ok(body) => {
                    req.headers_mut()
                        .insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
                    *req.body_mut() = Some(body.into());
                }
                Err(err) => error = Some(crate::error::builder(err)),
            }
        }
        if let Some(err) = error {
            self.request = Err(err);
        }
        self
    }

specifically serde_json::to_vec(json). Could this be converted to use serde_json::to_writer()? Maybe this is a async/blocking code problem? Looks like that's the case in serde's world: https://github.com/serde-rs/json/issues/575.

Just wanted to kick off a conversation here and have something searchable.

seanmonstar commented 2 years ago

Streaming would need to be handled more by the user. First, you need to determine how the remote would receive it: how are the pieces streamed, is it like an array that is never closed? Or is each piece a self-contained JSON object, and they are separated by newlines?

After you've determined that, you could send a request with a channel as the body stream. Then, each time you have a part ready, you can use something like serde_json::to_vec(&item), and then push that on the channel.