feat(client): impl Sync for Client
Connector::connect already used &self, and so would require synchronization to be handled per connector anyway. Adding Sync to the Client allows users to setup config for a Client once, such as using a single connection Pool, and then making requests across multiple threads. Closes #254 BREAKING CHANGE: Connectors and Protocols passed to the `Client` must now also have a `Sync` bounds, but this shouldn't break default usage.
This commit is contained in:
@@ -20,7 +20,7 @@ fn main() {
|
||||
}
|
||||
};
|
||||
|
||||
let mut client = Client::new();
|
||||
let client = Client::new();
|
||||
|
||||
let mut res = client.get(&*url)
|
||||
.header(Connection::close())
|
||||
|
||||
@@ -21,7 +21,7 @@ fn main() {
|
||||
}
|
||||
};
|
||||
|
||||
let mut client = Client::with_protocol(h2::new_protocol());
|
||||
let client = Client::with_protocol(h2::new_protocol());
|
||||
|
||||
// `Connection: Close` is not a valid header for HTTP/2, but the client handles it gracefully.
|
||||
let mut res = client.get(&*url)
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
//!
|
||||
//! ```no_run
|
||||
//! # use hyper::Client;
|
||||
//! let mut client = Client::new();
|
||||
//! let client = Client::new();
|
||||
//!
|
||||
//! let res = client.get("http://example.domain").send().unwrap();
|
||||
//! assert_eq!(res.status, hyper::Ok);
|
||||
@@ -23,7 +23,7 @@
|
||||
//!
|
||||
//! ```no_run
|
||||
//! # use hyper::Client;
|
||||
//! let mut client = Client::new();
|
||||
//! let client = Client::new();
|
||||
//!
|
||||
//! let res = client.post("http://example.domain")
|
||||
//! .body("foo=bar")
|
||||
@@ -31,6 +31,30 @@
|
||||
//! .unwrap();
|
||||
//! assert_eq!(res.status, hyper::Ok);
|
||||
//! ```
|
||||
//!
|
||||
//! # Sync
|
||||
//!
|
||||
//! The `Client` implements `Sync`, so you can share it among multiple threads
|
||||
//! and make multiple requests simultaneously.
|
||||
//!
|
||||
//! ```no_run
|
||||
//! # use hyper::Client;
|
||||
//! use std::sync::Arc;
|
||||
//! use std::thread;
|
||||
//!
|
||||
//! // Note: an Arc is used here because `thread::spawn` creates threads that
|
||||
//! // can outlive the main thread, so we must use reference counting to keep
|
||||
//! // the Client alive long enough. Scoped threads could skip the Arc.
|
||||
//! let client = Arc::new(Client::new());
|
||||
//! let clone1 = client.clone();
|
||||
//! let clone2 = client.clone();
|
||||
//! thread::spawn(move || {
|
||||
//! clone1.get("http://example.domain").send().unwrap();
|
||||
//! });
|
||||
//! thread::spawn(move || {
|
||||
//! clone2.post("http://example.domain/post").body("foo=bar").send().unwrap();
|
||||
//! });
|
||||
//! ```
|
||||
use std::default::Default;
|
||||
use std::io::{self, copy, Read};
|
||||
use std::iter::Extend;
|
||||
@@ -61,7 +85,7 @@ use http::h1::Http11Protocol;
|
||||
///
|
||||
/// Clients can handle things such as: redirect policy, connection pooling.
|
||||
pub struct Client {
|
||||
protocol: Box<Protocol + Send>,
|
||||
protocol: Box<Protocol + Send + Sync>,
|
||||
redirect_policy: RedirectPolicy,
|
||||
}
|
||||
|
||||
@@ -79,12 +103,12 @@ impl Client {
|
||||
|
||||
/// Create a new client with a specific connector.
|
||||
pub fn with_connector<C, S>(connector: C) -> Client
|
||||
where C: NetworkConnector<Stream=S> + Send + 'static, S: NetworkStream + Send {
|
||||
where C: NetworkConnector<Stream=S> + Send + Sync + 'static, S: NetworkStream + Send {
|
||||
Client::with_protocol(Http11Protocol::with_connector(connector))
|
||||
}
|
||||
|
||||
/// Create a new client with a specific `Protocol`.
|
||||
pub fn with_protocol<P: Protocol + Send + 'static>(protocol: P) -> Client {
|
||||
pub fn with_protocol<P: Protocol + Send + Sync + 'static>(protocol: P) -> Client {
|
||||
Client {
|
||||
protocol: Box::new(protocol),
|
||||
redirect_policy: Default::default()
|
||||
@@ -102,33 +126,33 @@ impl Client {
|
||||
}
|
||||
|
||||
/// Build a Get request.
|
||||
pub fn get<U: IntoUrl>(&mut self, url: U) -> RequestBuilder<U> {
|
||||
pub fn get<U: IntoUrl>(&self, url: U) -> RequestBuilder<U> {
|
||||
self.request(Method::Get, url)
|
||||
}
|
||||
|
||||
/// Build a Head request.
|
||||
pub fn head<U: IntoUrl>(&mut self, url: U) -> RequestBuilder<U> {
|
||||
pub fn head<U: IntoUrl>(&self, url: U) -> RequestBuilder<U> {
|
||||
self.request(Method::Head, url)
|
||||
}
|
||||
|
||||
/// Build a Post request.
|
||||
pub fn post<U: IntoUrl>(&mut self, url: U) -> RequestBuilder<U> {
|
||||
pub fn post<U: IntoUrl>(&self, url: U) -> RequestBuilder<U> {
|
||||
self.request(Method::Post, url)
|
||||
}
|
||||
|
||||
/// Build a Put request.
|
||||
pub fn put<U: IntoUrl>(&mut self, url: U) -> RequestBuilder<U> {
|
||||
pub fn put<U: IntoUrl>(&self, url: U) -> RequestBuilder<U> {
|
||||
self.request(Method::Put, url)
|
||||
}
|
||||
|
||||
/// Build a Delete request.
|
||||
pub fn delete<U: IntoUrl>(&mut self, url: U) -> RequestBuilder<U> {
|
||||
pub fn delete<U: IntoUrl>(&self, url: U) -> RequestBuilder<U> {
|
||||
self.request(Method::Delete, url)
|
||||
}
|
||||
|
||||
|
||||
/// Build a new request using this Client.
|
||||
pub fn request<U: IntoUrl>(&mut self, method: Method, url: U) -> RequestBuilder<U> {
|
||||
pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder<U> {
|
||||
RequestBuilder {
|
||||
client: self,
|
||||
method: method,
|
||||
|
||||
@@ -275,7 +275,7 @@ impl Http11Protocol {
|
||||
/// Creates a new `Http11Protocol` instance that will use the given `NetworkConnector` for
|
||||
/// establishing HTTP connections.
|
||||
pub fn with_connector<C, S>(c: C) -> Http11Protocol
|
||||
where C: NetworkConnector<Stream=S> + Send + 'static,
|
||||
where C: NetworkConnector<Stream=S> + Send + Sync + 'static,
|
||||
S: NetworkStream + Send {
|
||||
Http11Protocol {
|
||||
connector: Connector(Box::new(ConnAdapter(c))),
|
||||
@@ -283,9 +283,9 @@ impl Http11Protocol {
|
||||
}
|
||||
}
|
||||
|
||||
struct ConnAdapter<C: NetworkConnector + Send>(C);
|
||||
struct ConnAdapter<C: NetworkConnector + Send + Sync>(C);
|
||||
|
||||
impl<C: NetworkConnector<Stream=S> + Send, S: NetworkStream + Send> NetworkConnector for ConnAdapter<C> {
|
||||
impl<C: NetworkConnector<Stream=S> + Send + Sync, S: NetworkStream + Send> NetworkConnector for ConnAdapter<C> {
|
||||
type Stream = Box<NetworkStream + Send>;
|
||||
#[inline]
|
||||
fn connect(&self, host: &str, port: u16, scheme: &str)
|
||||
@@ -298,7 +298,7 @@ impl<C: NetworkConnector<Stream=S> + Send, S: NetworkStream + Send> NetworkConne
|
||||
}
|
||||
}
|
||||
|
||||
struct Connector(Box<NetworkConnector<Stream=Box<NetworkStream + Send>> + Send>);
|
||||
struct Connector(Box<NetworkConnector<Stream=Box<NetworkStream + Send>> + Send + Sync>);
|
||||
|
||||
impl NetworkConnector for Connector {
|
||||
type Stream = Box<NetworkStream + Send>;
|
||||
|
||||
@@ -198,3 +198,8 @@ fn _assert_send<T: Send>() {
|
||||
_assert_send::<client::Request<net::Fresh>>();
|
||||
_assert_send::<client::Response>();
|
||||
}
|
||||
|
||||
#[allow(unconditional_recursion)]
|
||||
fn _assert_sync<T: Sync>() {
|
||||
_assert_sync::<Client>();
|
||||
}
|
||||
|
||||
@@ -144,12 +144,12 @@ impl NetworkConnector for MockConnector {
|
||||
///
|
||||
/// Otherwise, it behaves the same as `MockConnector`.
|
||||
pub struct ChannelMockConnector {
|
||||
calls: Sender<String>,
|
||||
calls: Mutex<Sender<String>>,
|
||||
}
|
||||
|
||||
impl ChannelMockConnector {
|
||||
pub fn new(calls: Sender<String>) -> ChannelMockConnector {
|
||||
ChannelMockConnector { calls: calls }
|
||||
ChannelMockConnector { calls: Mutex::new(calls) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,13 +158,13 @@ impl NetworkConnector for ChannelMockConnector {
|
||||
#[inline]
|
||||
fn connect(&self, _host: &str, _port: u16, _scheme: &str)
|
||||
-> ::Result<MockStream> {
|
||||
self.calls.send("connect".into()).unwrap();
|
||||
self.calls.lock().unwrap().send("connect".into()).unwrap();
|
||||
Ok(MockStream::new())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_ssl_verifier(&mut self, _verifier: ContextVerifier) {
|
||||
self.calls.send("set_ssl_verifier".into()).unwrap();
|
||||
self.calls.lock().unwrap().send("set_ssl_verifier".into()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -317,7 +317,7 @@ impl NetworkStream for HttpStream {
|
||||
pub struct HttpConnector(pub Option<ContextVerifier>);
|
||||
|
||||
/// A method that can set verification methods on an SSL context
|
||||
pub type ContextVerifier = Box<Fn(&mut SslContext) -> () + Send>;
|
||||
pub type ContextVerifier = Box<Fn(&mut SslContext) -> () + Send + Sync>;
|
||||
|
||||
impl NetworkConnector for HttpConnector {
|
||||
type Stream = HttpStream;
|
||||
|
||||
Reference in New Issue
Block a user