From a8258643f991843afb1a5e86429a1f7f59da52a2 Mon Sep 17 00:00:00 2001 From: LoganDark Date: Mon, 16 Oct 2023 15:44:09 -0700 Subject: [PATCH 1/2] add `.chunk()` associated function to `blocking::Response` --- src/blocking/response.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/blocking/response.rs b/src/blocking/response.rs index 8d0e9b9cf..04bc7cc02 100644 --- a/src/blocking/response.rs +++ b/src/blocking/response.rs @@ -263,6 +263,29 @@ impl Response { }) } + /// Stream a chunk of the response body. + /// + /// When the response body has been exhausted, this will return `None`. + /// + /// # Example + /// + /// ``` + /// # fn run() -> Result<(), Box> { + /// let mut res = reqwest::blocking::get("https://hyper.rs")?; + /// + /// while let Some(chunk) = res.chunk()? { + /// println!("Chunk: {:?}", chunk); + /// } + /// # Ok(()) + /// # } + /// ``` + pub fn chunk(&mut self) -> crate::Result> { + wait::timeout(self.inner.chunk(), self.timeout).map_err(|e| match e { + wait::Waited::TimedOut(e) => crate::error::decode(e), + wait::Waited::Inner(e) => e, + }) + } + /// Get the response text. /// /// This method decodes the response body with BOM sniffing From ead4ac76a03fc000fe2af625b05605056b1ba59a Mon Sep 17 00:00:00 2001 From: LoganDark Date: Mon, 16 Oct 2023 15:44:59 -0700 Subject: [PATCH 2/2] add `.json_chunk()` method to `Response` and `blocking::Response` `error::decode` method is private, so it's impossible to implement this outside of the crate. Used for https://github.com/huggingface/text-generation-inference --- src/async_impl/response.rs | 50 ++++++++++++++++++++++++++++++++++++++ src/blocking/response.rs | 49 +++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) diff --git a/src/async_impl/response.rs b/src/async_impl/response.rs index fc5a5d464..7fb3d28c0 100644 --- a/src/async_impl/response.rs +++ b/src/async_impl/response.rs @@ -240,6 +240,56 @@ impl Response { serde_json::from_slice(&full).map_err(crate::error::decode) } + /// Try to deserialize a single chunk of the request body as JSON. + /// + /// When the response body has been exhausted, this will return `Ok(None)`. + /// + /// # Optional + /// + /// This requires the optional `json` feature enabled. + /// + /// # Example + /// + /// ```rust + /// # extern crate reqwest; + /// # extern crate serde; + /// # + /// # use reqwest::Error; + /// # use serde::Deserialize; + /// # + /// // This `derive` requires the `serde` dependency. + /// #[derive(Deserialize)] + /// struct Ip { + /// origin: String, + /// } + /// + /// # async fn run() -> Result<(), Error> { + /// let mut res = reqwest::get("http://httpbin.org/ip").await?; + /// + /// while let Some(chunk) = res.json_chunk::().await? { + /// println!("Chunk: {:?}", chunk); + /// } + /// # Ok(()) + /// # } + /// ``` + /// + /// # Errors + /// + /// This method fails whenever the response chunk is not in JSON format + /// or it cannot be properly deserialized to target type `T`. For more + /// details please see [`serde_json::from_reader`]. + /// + /// [`serde_json::from_reader`]: https://docs.serde.rs/serde_json/fn.from_reader.html + #[cfg(feature = "json")] + #[cfg_attr(docsrs, doc(cfg(feature = "json")))] + pub async fn json_chunk(&mut self) -> crate::Result> { + if let Some(full) = self.chunk().await? { + Ok(Some(serde_json::from_slice(&full).map_err(crate::error::decode)?)) + } else { + Ok(None) + } + } + /// Get the full response body as `Bytes`. /// /// # Example diff --git a/src/blocking/response.rs b/src/blocking/response.rs index 04bc7cc02..a0ddb2c16 100644 --- a/src/blocking/response.rs +++ b/src/blocking/response.rs @@ -244,6 +244,55 @@ impl Response { }) } + /// Try to deserialize a single chunk of the request body as JSON. + /// + /// When the response body has been exhausted, this will return `Ok(None)`. + /// + /// # Optional + /// + /// This requires the optional `json` feature enabled. + /// + /// # Example + /// + /// ```rust + /// # extern crate reqwest; + /// # extern crate serde; + /// # + /// # use reqwest::Error; + /// # use serde::Deserialize; + /// # + /// // This `derive` requires the `serde` dependency. + /// #[derive(Deserialize)] + /// struct Ip { + /// origin: String, + /// } + /// + /// # fn run() -> Result<(), Error> { + /// let mut res = reqwest::blocking::get("http://httpbin.org/ip")?; + /// + /// while let Some(chunk) = res.json_chunk::()? { + /// println!("Chunk: {:?}", chunk); + /// } + /// # Ok(()) + /// # } + /// ``` + /// + /// # Errors + /// + /// This method fails whenever the response chunk is not in JSON format + /// or it cannot be properly deserialized to target type `T`. For more + /// details please see [`serde_json::from_reader`]. + /// + /// [`serde_json::from_reader`]: https://docs.serde.rs/serde_json/fn.from_reader.html + #[cfg(feature = "json")] + #[cfg_attr(docsrs, doc(cfg(feature = "json")))] + pub fn json_chunk(&mut self) -> crate::Result> { + wait::timeout(self.inner.json_chunk(), self.timeout).map_err(|e| match e { + wait::Waited::TimedOut(e) => crate::error::decode(e), + wait::Waited::Inner(e) => e, + }) + } + /// Get the full response body as `Bytes`. /// /// # Example