Add try_clone to Request and RequestBuilder (#387)
The need to clone a request or builder may arise when repeating a request multiple times. This can be either because: * The Request object is consumed by Client::execute * The request might need to be retried later * A complex request needs to be repeated with slightly different parameters, such as in the Partial-Content scheme which allows seeking through the content of large object over HTTP by performing multiple HTTP GET requests. To make this easier, it would be nice if Request and RequestBuilder were to implement the Clone trait. However, this is not possible because a body might be set that is a stream which can not be cloned. To get around this, I added a try_clone function that fails if the body is not clonable. An alternative solution would be to add a type parameter to Request for the body so a conditional implementation for Clone can be added.
This commit is contained in:
14
src/body.rs
14
src/body.rs
@@ -109,6 +109,11 @@ impl Body {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn try_clone(&self) -> Option<Body> {
|
||||||
|
self.kind.try_clone()
|
||||||
|
.map(|kind| Body { kind })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -117,6 +122,15 @@ enum Kind {
|
|||||||
Bytes(Bytes),
|
Bytes(Bytes),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Kind {
|
||||||
|
fn try_clone(&self) -> Option<Kind> {
|
||||||
|
match self {
|
||||||
|
Kind::Reader(..) => None,
|
||||||
|
Kind::Bytes(v) => Some(Kind::Bytes(v.clone())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<Vec<u8>> for Body {
|
impl From<Vec<u8>> for Body {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(v: Vec<u8>) -> Body {
|
fn from(v: Vec<u8>) -> Body {
|
||||||
|
|||||||
@@ -81,6 +81,26 @@ impl Request {
|
|||||||
&mut self.body
|
&mut self.body
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Attempts to clone the `Request`.
|
||||||
|
///
|
||||||
|
/// None is returned if a body is which can not be cloned. This can be because the body is a
|
||||||
|
/// stream.
|
||||||
|
pub fn try_clone(&self) -> Option<Request> {
|
||||||
|
let body = if let Some(ref body) = self.body.as_ref() {
|
||||||
|
if let Some(body) = body.try_clone() {
|
||||||
|
Some(body)
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let mut req = Request::new(self.method().clone(), self.url().clone());
|
||||||
|
*req.headers_mut() = self.headers().clone();
|
||||||
|
req.body = body;
|
||||||
|
Some(req)
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn into_async(self) -> (async_impl::Request, Option<body::Sender>) {
|
pub(crate) fn into_async(self) -> (async_impl::Request, Option<body::Sender>) {
|
||||||
use header::CONTENT_LENGTH;
|
use header::CONTENT_LENGTH;
|
||||||
|
|
||||||
@@ -484,6 +504,61 @@ impl RequestBuilder {
|
|||||||
self.client.execute(self.request?)
|
self.client.execute(self.request?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Attempts to clone the `RequestBuilder`.
|
||||||
|
///
|
||||||
|
/// None is returned if a body is which can not be cloned. This can be because the body is a
|
||||||
|
/// stream.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// With a static body
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # fn run() -> Result<(), Box<::std::error::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(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Without a body
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # fn run() -> Result<(), Box<::std::error::Error>> {
|
||||||
|
/// let client = reqwest::Client::new();
|
||||||
|
/// let builder = client.get("http://httpbin.org/get");
|
||||||
|
/// let clone = builder.try_clone();
|
||||||
|
/// assert!(clone.is_some());
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// With a non-clonable body
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # fn run() -> Result<(), Box<::std::error::Error>> {
|
||||||
|
/// let client = reqwest::Client::new();
|
||||||
|
/// let builder = client.get("http://httpbin.org/get")
|
||||||
|
/// .body(reqwest::Body::new(std::io::empty()));
|
||||||
|
/// let clone = builder.try_clone();
|
||||||
|
/// assert!(clone.is_none());
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
pub fn try_clone(&self) -> Option<RequestBuilder> {
|
||||||
|
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 {
|
impl fmt::Debug for Request {
|
||||||
|
|||||||
Reference in New Issue
Block a user