Skip to content

Commit 64e47b4

Browse files
committed
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.
1 parent d7fa961 commit 64e47b4

File tree

7 files changed

+51
-22
lines changed

7 files changed

+51
-22
lines changed

examples/client.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ fn main() {
2020
}
2121
};
2222

23-
let mut client = Client::new();
23+
let client = Client::new();
2424

2525
let mut res = client.get(&*url)
2626
.header(Connection::close())

examples/client_http2.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ fn main() {
2121
}
2222
};
2323

24-
let mut client = Client::with_protocol(h2::new_protocol());
24+
let client = Client::with_protocol(h2::new_protocol());
2525

2626
// `Connection: Close` is not a valid header for HTTP/2, but the client handles it gracefully.
2727
let mut res = client.get(&*url)

src/client/mod.rs

+35-11
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
//!
1010
//! ```no_run
1111
//! # use hyper::Client;
12-
//! let mut client = Client::new();
12+
//! let client = Client::new();
1313
//!
1414
//! let res = client.get("http://example.domain").send().unwrap();
1515
//! assert_eq!(res.status, hyper::Ok);
@@ -23,14 +23,38 @@
2323
//!
2424
//! ```no_run
2525
//! # use hyper::Client;
26-
//! let mut client = Client::new();
26+
//! let client = Client::new();
2727
//!
2828
//! let res = client.post("http://example.domain")
2929
//! .body("foo=bar")
3030
//! .send()
3131
//! .unwrap();
3232
//! assert_eq!(res.status, hyper::Ok);
3333
//! ```
34+
//!
35+
//! # Sync
36+
//!
37+
//! The `Client` implements `Sync`, so you can share it among multiple threads
38+
//! and make multiple requests simultaneously.
39+
//!
40+
//! ```no_run
41+
//! # use hyper::Client;
42+
//! use std::sync::Arc;
43+
//! use std::thread;
44+
//!
45+
//! // Note: an Arc is used here because `thread::spawn` creates threads that
46+
//! // can outlive the main thread, so we must use reference counting to keep
47+
//! // the Client alive long enough. Scoped threads could skip the Arc.
48+
//! let client = Arc::new(Client::new());
49+
//! let clone1 = client.clone();
50+
//! let clone2 = client.clone();
51+
//! thread::spawn(move || {
52+
//! clone1.get("http://example.domain").send().unwrap();
53+
//! });
54+
//! thread::spawn(move || {
55+
//! clone2.post("http://example.domain/post").body("foo=bar").send().unwrap();
56+
//! });
57+
//! ```
3458
use std::default::Default;
3559
use std::io::{self, copy, Read};
3660
use std::iter::Extend;
@@ -61,7 +85,7 @@ use http::h1::Http11Protocol;
6185
///
6286
/// Clients can handle things such as: redirect policy, connection pooling.
6387
pub struct Client {
64-
protocol: Box<Protocol + Send>,
88+
protocol: Box<Protocol + Send + Sync>,
6589
redirect_policy: RedirectPolicy,
6690
}
6791

@@ -79,12 +103,12 @@ impl Client {
79103

80104
/// Create a new client with a specific connector.
81105
pub fn with_connector<C, S>(connector: C) -> Client
82-
where C: NetworkConnector<Stream=S> + Send + 'static, S: NetworkStream + Send {
106+
where C: NetworkConnector<Stream=S> + Send + Sync + 'static, S: NetworkStream + Send {
83107
Client::with_protocol(Http11Protocol::with_connector(connector))
84108
}
85109

86110
/// Create a new client with a specific `Protocol`.
87-
pub fn with_protocol<P: Protocol + Send + 'static>(protocol: P) -> Client {
111+
pub fn with_protocol<P: Protocol + Send + Sync + 'static>(protocol: P) -> Client {
88112
Client {
89113
protocol: Box::new(protocol),
90114
redirect_policy: Default::default()
@@ -102,33 +126,33 @@ impl Client {
102126
}
103127

104128
/// Build a Get request.
105-
pub fn get<U: IntoUrl>(&mut self, url: U) -> RequestBuilder<U> {
129+
pub fn get<U: IntoUrl>(&self, url: U) -> RequestBuilder<U> {
106130
self.request(Method::Get, url)
107131
}
108132

109133
/// Build a Head request.
110-
pub fn head<U: IntoUrl>(&mut self, url: U) -> RequestBuilder<U> {
134+
pub fn head<U: IntoUrl>(&self, url: U) -> RequestBuilder<U> {
111135
self.request(Method::Head, url)
112136
}
113137

114138
/// Build a Post request.
115-
pub fn post<U: IntoUrl>(&mut self, url: U) -> RequestBuilder<U> {
139+
pub fn post<U: IntoUrl>(&self, url: U) -> RequestBuilder<U> {
116140
self.request(Method::Post, url)
117141
}
118142

119143
/// Build a Put request.
120-
pub fn put<U: IntoUrl>(&mut self, url: U) -> RequestBuilder<U> {
144+
pub fn put<U: IntoUrl>(&self, url: U) -> RequestBuilder<U> {
121145
self.request(Method::Put, url)
122146
}
123147

124148
/// Build a Delete request.
125-
pub fn delete<U: IntoUrl>(&mut self, url: U) -> RequestBuilder<U> {
149+
pub fn delete<U: IntoUrl>(&self, url: U) -> RequestBuilder<U> {
126150
self.request(Method::Delete, url)
127151
}
128152

129153

130154
/// Build a new request using this Client.
131-
pub fn request<U: IntoUrl>(&mut self, method: Method, url: U) -> RequestBuilder<U> {
155+
pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder<U> {
132156
RequestBuilder {
133157
client: self,
134158
method: method,

src/http/h1.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -275,17 +275,17 @@ impl Http11Protocol {
275275
/// Creates a new `Http11Protocol` instance that will use the given `NetworkConnector` for
276276
/// establishing HTTP connections.
277277
pub fn with_connector<C, S>(c: C) -> Http11Protocol
278-
where C: NetworkConnector<Stream=S> + Send + 'static,
278+
where C: NetworkConnector<Stream=S> + Send + Sync + 'static,
279279
S: NetworkStream + Send {
280280
Http11Protocol {
281281
connector: Connector(Box::new(ConnAdapter(c))),
282282
}
283283
}
284284
}
285285

286-
struct ConnAdapter<C: NetworkConnector + Send>(C);
286+
struct ConnAdapter<C: NetworkConnector + Send + Sync>(C);
287287

288-
impl<C: NetworkConnector<Stream=S> + Send, S: NetworkStream + Send> NetworkConnector for ConnAdapter<C> {
288+
impl<C: NetworkConnector<Stream=S> + Send + Sync, S: NetworkStream + Send> NetworkConnector for ConnAdapter<C> {
289289
type Stream = Box<NetworkStream + Send>;
290290
#[inline]
291291
fn connect(&self, host: &str, port: u16, scheme: &str)
@@ -298,7 +298,7 @@ impl<C: NetworkConnector<Stream=S> + Send, S: NetworkStream + Send> NetworkConne
298298
}
299299
}
300300

301-
struct Connector(Box<NetworkConnector<Stream=Box<NetworkStream + Send>> + Send>);
301+
struct Connector(Box<NetworkConnector<Stream=Box<NetworkStream + Send>> + Send + Sync>);
302302

303303
impl NetworkConnector for Connector {
304304
type Stream = Box<NetworkStream + Send>;

src/lib.rs

+5
Original file line numberDiff line numberDiff line change
@@ -198,3 +198,8 @@ fn _assert_send<T: Send>() {
198198
_assert_send::<client::Request<net::Fresh>>();
199199
_assert_send::<client::Response>();
200200
}
201+
202+
#[allow(unconditional_recursion)]
203+
fn _assert_sync<T: Sync>() {
204+
_assert_sync::<Client>();
205+
}

src/mock.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -144,12 +144,12 @@ impl NetworkConnector for MockConnector {
144144
///
145145
/// Otherwise, it behaves the same as `MockConnector`.
146146
pub struct ChannelMockConnector {
147-
calls: Sender<String>,
147+
calls: Mutex<Sender<String>>,
148148
}
149149

150150
impl ChannelMockConnector {
151151
pub fn new(calls: Sender<String>) -> ChannelMockConnector {
152-
ChannelMockConnector { calls: calls }
152+
ChannelMockConnector { calls: Mutex::new(calls) }
153153
}
154154
}
155155

@@ -158,13 +158,13 @@ impl NetworkConnector for ChannelMockConnector {
158158
#[inline]
159159
fn connect(&self, _host: &str, _port: u16, _scheme: &str)
160160
-> ::Result<MockStream> {
161-
self.calls.send("connect".into()).unwrap();
161+
self.calls.lock().unwrap().send("connect".into()).unwrap();
162162
Ok(MockStream::new())
163163
}
164164

165165
#[inline]
166166
fn set_ssl_verifier(&mut self, _verifier: ContextVerifier) {
167-
self.calls.send("set_ssl_verifier".into()).unwrap();
167+
self.calls.lock().unwrap().send("set_ssl_verifier".into()).unwrap();
168168
}
169169
}
170170

src/net.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ impl NetworkStream for HttpStream {
317317
pub struct HttpConnector(pub Option<ContextVerifier>);
318318

319319
/// A method that can set verification methods on an SSL context
320-
pub type ContextVerifier = Box<Fn(&mut SslContext) -> () + Send>;
320+
pub type ContextVerifier = Box<Fn(&mut SslContext) -> () + Send + Sync>;
321321

322322
impl NetworkConnector for HttpConnector {
323323
type Stream = HttpStream;

0 commit comments

Comments
 (0)