diff --git a/src/async_impl/body.rs b/src/async_impl/body.rs
index 0ff7fec..dbe2809 100644
--- a/src/async_impl/body.rs
+++ b/src/async_impl/body.rs
@@ -125,6 +125,13 @@ impl Body {
(reuse, self)
}
+ pub(crate) fn try_clone(&self) -> Option
{
+ match self.inner {
+ Inner::Reusable(ref chunk) => Some(Body::reusable(chunk.clone())),
+ Inner::Streaming { .. } => None,
+ }
+ }
+
pub(crate) fn into_stream(self) -> ImplStream {
ImplStream(self)
}
diff --git a/src/async_impl/request.rs b/src/async_impl/request.rs
index dc3cbd6..f4fe960 100644
--- a/src/async_impl/request.rs
+++ b/src/async_impl/request.rs
@@ -89,6 +89,20 @@ impl Request {
&mut self.body
}
+ /// Attempt to clone the request.
+ ///
+ /// `None` is returned if the request can not be cloned, i.e. if the body is a stream.
+ pub fn try_clone(&self) -> Option {
+ let body = match self.body.as_ref() {
+ Some(ref body) => Some(body.try_clone()?),
+ None => None,
+ };
+ let mut req = Request::new(self.method().clone(), self.url().clone());
+ *req.headers_mut() = self.headers().clone();
+ req.body = body;
+ Some(req)
+ }
+
pub(super) fn pieces(self) -> (Method, Url, HeaderMap, Option) {
(self.method, self.url, self.headers, self.body)
}
@@ -323,6 +337,36 @@ impl RequestBuilder {
Err(err) => Pending::new_err(err),
}
}
+
+ /// Attempt to clone the RequestBuilder.
+ ///
+ /// `None` is returned if the RequestBuilder can not be cloned,
+ /// i.e. if the request body is a stream.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use reqwest::Error;
+ /// #
+ /// # fn run() -> Result<(), Error> {
+ /// let client = reqwest::Client::new();
+ /// let builder = client.post("http://httpbin.org/post")
+ /// .body("from a &str!");
+ /// let clone = builder.try_clone();
+ /// assert!(clone.is_some());
+ /// # Ok(())
+ /// # }
+ /// ```
+ pub fn try_clone(&self) -> Option {
+ self.request
+ .as_ref()
+ .ok()
+ .and_then(|req| req.try_clone())
+ .map(|req| RequestBuilder {
+ client: self.client.clone(),
+ request: Ok(req),
+ })
+ }
}
impl fmt::Debug for Request {
@@ -380,6 +424,7 @@ pub(crate) fn replace_headers(dst: &mut HeaderMap, src: HeaderMap) {
#[cfg(test)]
mod tests {
use super::Client;
+ use crate::Method;
use serde::Serialize;
use std::collections::BTreeMap;
@@ -488,6 +533,52 @@ mod tests {
assert_eq!(req.url().as_str(), "https://google.com/");
}
+ #[test]
+ fn try_clone_reusable() {
+ let client = Client::new();
+ let builder = client.post("http://httpbin.org/post")
+ .header("foo", "bar")
+ .body("from a &str!");
+ let req = builder
+ .try_clone()
+ .expect("clone successful")
+ .build()
+ .expect("request is valid");
+ assert_eq!(req.url().as_str(), "http://httpbin.org/post");
+ assert_eq!(req.method(), Method::POST);
+ assert_eq!(req.headers()["foo"], "bar");
+ }
+
+ #[test]
+ fn try_clone_no_body() {
+ let client = Client::new();
+ let builder = client.get("http://httpbin.org/get");
+ let req = builder
+ .try_clone()
+ .expect("clone successful")
+ .build()
+ .expect("request is valid");
+ assert_eq!(req.url().as_str(), "http://httpbin.org/get");
+ assert_eq!(req.method(), Method::GET);
+ assert!(req.body().is_none());
+ }
+
+ #[test]
+ #[cfg(feature = "unstable-stream")]
+ fn try_clone_stream() {
+ let chunks: Vec> = vec![
+ Ok("hello"),
+ Ok(" "),
+ Ok("world"),
+ ];
+ let stream = futures_util::stream::iter(chunks);
+ let client = Client::new();
+ let builder = client.get("http://httpbin.org/get")
+ .body(super::Body::wrap_stream(stream));
+ let clone = builder.try_clone();
+ assert!(clone.is_none());
+ }
+
/*
use {body, Method};
use super::Client;