Skip to content

Commit

Permalink
make Client: Send + Sync, RequestBuilder: Send, Response: Send
Browse files Browse the repository at this point in the history
  • Loading branch information
seanmonstar committed Dec 2, 2016
1 parent 552f471 commit d18a53b
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 17 deletions.
4 changes: 2 additions & 2 deletions src/body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ impl Body {
///
/// A `Body` constructed from a set of bytes, like `String` or `Vec<u8>`,
/// are stored differently and can be reused.
pub fn new<R: Read + 'static>(reader: R) -> Body {
pub fn new<R: Read + Send + 'static>(reader: R) -> Body {
Body {
reader: Kind::Reader(Box::new(reader), None),
}
Expand Down Expand Up @@ -53,7 +53,7 @@ pub fn read_to_string(mut body: Body) -> ::std::io::Result<String> {
}

enum Kind {
Reader(Box<Read>, Option<u64>),
Reader(Box<Read + Send>, Option<u64>),
Bytes(Vec<u8>),
}

Expand Down
68 changes: 53 additions & 15 deletions src/client.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use std::fmt;
use std::io::{self, Read};
use std::sync::Arc;

use hyper::client::IntoUrl;
use hyper::header::{Headers, ContentType, Location, Referer, UserAgent};
Expand All @@ -22,9 +24,8 @@ static DEFAULT_USER_AGENT: &'static str = concat!(env!("CARGO_PKG_NAME"), "/", e
///
/// The `Client` holds a connection pool internally, so it is advised that
/// you create one and reuse it.
#[derive(Debug)]
pub struct Client {
inner: ::hyper::Client,
inner: ClientRef, //::hyper::Client,
}

impl Client {
Expand All @@ -33,7 +34,9 @@ impl Client {
let mut client = try!(new_hyper_client());
client.set_redirect_policy(::hyper::client::RedirectPolicy::FollowNone);
Ok(Client {
inner: client
inner: ClientRef {
hyper: Arc::new(client),
}
})
}

Expand All @@ -59,7 +62,7 @@ impl Client {
pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder {
let url = url.into_url();
RequestBuilder {
client: self,
client: self.inner.clone(),
method: method,
url: url,
_version: HttpVersion::Http11,
Expand All @@ -70,6 +73,17 @@ impl Client {
}
}

impl fmt::Debug for Client {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.pad("Client")
}
}

#[derive(Clone)]
struct ClientRef {
hyper: Arc<::hyper::Client>,
}

fn new_hyper_client() -> ::Result<::hyper::Client> {
use tls::TlsClient;
Ok(::hyper::Client::with_connector(
Expand All @@ -82,9 +96,8 @@ fn new_hyper_client() -> ::Result<::hyper::Client> {


/// A builder to construct the properties of a `Request`.
#[derive(Debug)]
pub struct RequestBuilder<'a> {
client: &'a Client,
pub struct RequestBuilder {
client: ClientRef,

method: Method,
url: Result<Url, ::UrlError>,
Expand All @@ -94,7 +107,7 @@ pub struct RequestBuilder<'a> {
body: Option<::Result<Body>>,
}

impl<'a> RequestBuilder<'a> {
impl RequestBuilder {
/// Add a `Header` to this Request.
///
/// ```no_run
Expand All @@ -105,20 +118,20 @@ impl<'a> RequestBuilder<'a> {
/// .header(UserAgent("foo".to_string()))
/// .send();
/// ```
pub fn header<H: ::header::Header + ::header::HeaderFormat>(mut self, header: H) -> RequestBuilder<'a> {
pub fn header<H: ::header::Header + ::header::HeaderFormat>(mut self, header: H) -> RequestBuilder {
self.headers.set(header);
self
}
/// Add a set of Headers to the existing ones on this Request.
///
/// The headers will be merged in to any already set.
pub fn headers(mut self, headers: ::header::Headers) -> RequestBuilder<'a> {
pub fn headers(mut self, headers: ::header::Headers) -> RequestBuilder {
self.headers.extend(headers.iter());
self
}

/// Set the request body.
pub fn body<T: Into<Body>>(mut self, body: T) -> RequestBuilder<'a> {
pub fn body<T: Into<Body>>(mut self, body: T) -> RequestBuilder {
self.body = Some(Ok(body.into()));
self
}
Expand All @@ -139,7 +152,7 @@ impl<'a> RequestBuilder<'a> {
/// .form(&params)
/// .send();
/// ```
pub fn form<T: Serialize>(mut self, form: &T) -> RequestBuilder<'a> {
pub fn form<T: Serialize>(mut self, form: &T) -> RequestBuilder {
let body = serde_urlencoded::to_string(form).map_err(::Error::from);
self.headers.set(ContentType::form_url_encoded());
self.body = Some(body.map(|b| b.into()));
Expand All @@ -161,7 +174,7 @@ impl<'a> RequestBuilder<'a> {
/// .json(&map)
/// .send();
/// ```
pub fn json<T: Serialize>(mut self, json: &T) -> RequestBuilder<'a> {
pub fn json<T: Serialize>(mut self, json: &T) -> RequestBuilder {
let body = serde_json::to_vec(json).expect("serde to_vec cannot fail");
self.headers.set(ContentType::json());
self.body = Some(Ok(body.into()));
Expand All @@ -188,7 +201,7 @@ impl<'a> RequestBuilder<'a> {
loop {
let res = {
debug!("request {:?} \"{}\"", method, url);
let mut req = client.inner.request(method.clone(), url.clone())
let mut req = client.hyper.request(method.clone(), url.clone())
.headers(headers.clone());

if let Some(ref mut b) = body {
Expand Down Expand Up @@ -265,40 +278,65 @@ impl<'a> RequestBuilder<'a> {
}
}

impl fmt::Debug for RequestBuilder {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("RequestBuilder")
.field("method", &self.method)
.field("url", &self.url)
.field("headers", &self.headers)
.finish()
}
}

/// A Response to a submitted `Request`.
#[derive(Debug)]
pub struct Response {
inner: ::hyper::client::Response,
}

impl Response {
/// Get the `StatusCode`.
#[inline]
pub fn status(&self) -> &StatusCode {
&self.inner.status
}

/// Get the `Headers`.
#[inline]
pub fn headers(&self) -> &Headers {
&self.inner.headers
}

/// Get the `HttpVersion`.
#[inline]
pub fn version(&self) -> &HttpVersion {
&self.inner.version
}

/// Try and deserialize the response body as JSON.
#[inline]
pub fn json<T: Deserialize>(&mut self) -> ::Result<T> {
serde_json::from_reader(self).map_err(::Error::from)
}
}

/// Read the body of the Response.
impl Read for Response {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.inner.read(buf)
}
}

impl fmt::Debug for Response {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Response")
.field("status", self.status())
.field("headers", self.headers())
.field("version", self.version())
.finish()
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
11 changes: 11 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,14 @@ pub fn get<T: IntoUrl>(url: T) -> ::Result<Response> {
let client = try!(Client::new());
client.get(url).send()
}

fn _assert_impls() {
fn assert_send<T: Send>() {}
fn assert_sync<T: Sync>() {}

assert_send::<Client>();
assert_sync::<Client>();

assert_send::<RequestBuilder>();
assert_send::<Response>();
}

0 comments on commit d18a53b

Please sign in to comment.