hyper_native_tls/
lib.rs

1//! SSL support for Hyper via the native-tls crate.
2//!
3//! # Usage
4//!
5//! On the client side:
6//!
7//! ```
8//! extern crate hyper;
9//! extern crate hyper_native_tls;
10//!
11//! use hyper::Client;
12//! use hyper::net::HttpsConnector;
13//! use hyper_native_tls::NativeTlsClient;
14//! use std::io::Read;
15//!
16//! fn main() {
17//!     let ssl = NativeTlsClient::new().unwrap();
18//!     let connector = HttpsConnector::new(ssl);
19//!     let client = Client::with_connector(connector);
20//!
21//!     let mut resp = client.get("https://google.com").send().unwrap();
22//!     let mut body = vec![];
23//!     resp.read_to_end(&mut body).unwrap();
24//!     println!("{}", String::from_utf8_lossy(&body));
25//! }
26//! ```
27//!
28//! Or on the server side:
29//!
30//! ```no_run
31//! extern crate hyper;
32//! extern crate hyper_native_tls;
33//!
34//! use hyper::Server;
35//! use hyper_native_tls::NativeTlsServer;
36//!
37//! fn main() {
38//!     let ssl = NativeTlsServer::new("identity.p12", "mypass").unwrap();
39//!     let server = Server::https("0.0.0.0:8443", ssl).unwrap();
40//! }
41//! ```
42#![warn(missing_docs)]
43#![doc(html_root_url="https://docs.rs/hyper-native-tls/0.3")]
44extern crate antidote;
45extern crate hyper;
46pub extern crate native_tls;
47
48use antidote::Mutex;
49use hyper::net::{SslClient, SslServer, NetworkStream};
50use native_tls::{TlsAcceptor, TlsConnector, Identity};
51use std::net::SocketAddr;
52use std::time::Duration;
53use std::error::Error;
54use std::io::{self, Read};
55use std::fs::File;
56use std::ops::{Deref, DerefMut};
57use std::sync::Arc;
58use std::fmt;
59use std::path::Path;
60
61pub use native_tls::Certificate;
62
63/// A Hyper stream using native_tls.
64#[derive(Debug, Clone)]
65pub struct TlsStream<S>(Arc<Mutex<native_tls::TlsStream<S>>>);
66
67impl<S> TlsStream<S>
68    where S: io::Read + io::Write
69{
70    /// Returns a guard around a locked TLS stream.
71    pub fn lock(&self) -> StreamGuard<S> {
72        StreamGuard(self.0.lock())
73    }
74}
75
76impl<S> io::Read for TlsStream<S>
77    where S: io::Read + io::Write
78{
79    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
80        self.0.lock().read(buf)
81    }
82}
83
84impl<S> io::Write for TlsStream<S>
85    where S: io::Read + io::Write
86{
87    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
88        self.0.lock().write(buf)
89    }
90
91    fn flush(&mut self) -> io::Result<()> {
92        self.0.lock().flush()
93    }
94}
95
96impl<S> NetworkStream for TlsStream<S>
97    where S: NetworkStream
98{
99    fn peer_addr(&mut self) -> io::Result<SocketAddr> {
100        self.0.lock().get_mut().peer_addr()
101    }
102
103    fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
104        self.0.lock().get_mut().set_read_timeout(dur)
105    }
106
107    fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
108        self.0.lock().get_mut().set_write_timeout(dur)
109    }
110}
111
112/// A guard around a locked inner `TlsStream`.
113pub struct StreamGuard<'a, T: io::Read + io::Write + 'a>(antidote::MutexGuard<'a, native_tls::TlsStream<T>>);
114
115impl<'a, T> Deref for StreamGuard<'a, T>
116    where T: io::Read + io::Write + 'a
117{
118    type Target = native_tls::TlsStream<T>;
119
120    fn deref(&self) -> &Self::Target {
121        &self.0
122    }
123}
124
125impl<'a, T> DerefMut for StreamGuard<'a, T>
126    where T: io::Read + io::Write + 'a
127{
128    fn deref_mut(&mut self) -> &mut Self::Target {
129        &mut self.0
130    }
131}
132
133/// An `SslClient` implementation using native-tls.
134pub struct NativeTlsClient {
135    connector: TlsConnector,
136}
137
138impl NativeTlsClient {
139    /// Returns a `NativeTlsClient` with a default configuration.
140    ///
141    /// To customize the configuration, build a `TlsConnector` and then use
142    /// `NativeTlsClient`'s `From` implementation.
143    pub fn new() -> native_tls::Result<NativeTlsClient> {
144        TlsConnector::builder().build()
145            .map(NativeTlsClient::from)
146    }
147}
148
149impl From<TlsConnector> for NativeTlsClient {
150    fn from(t: TlsConnector) -> NativeTlsClient {
151        NativeTlsClient {
152            connector: t,
153        }
154    }
155}
156
157impl<T> SslClient<T> for NativeTlsClient
158    where T: NetworkStream + Send + Clone + fmt::Debug + Sync
159{
160    type Stream = TlsStream<T>;
161
162    fn wrap_client(&self, stream: T, host: &str) -> hyper::Result<TlsStream<T>> {
163        let stream = self.connector.connect(host, stream);
164        match stream {
165            Ok(s) => Ok(TlsStream(Arc::new(Mutex::new(s)))),
166            Err(e) => Err(hyper::Error::Ssl(Box::new(e))),
167        }
168    }
169}
170
171/// An `SslServer` implementation using native-tls.
172#[derive(Clone)]
173pub struct NativeTlsServer(Arc<TlsAcceptor>);
174
175impl NativeTlsServer {
176    /// Returns a `NativeTlsServer` with a default configuration.
177    ///
178    /// To customize the configuration, build a `TlsAcceptor` and then use
179    /// `NativeTlsServer`'s `From` implementation.
180    pub fn new<P>(identity: P, password: &str) -> Result<NativeTlsServer, ServerError>
181        where P: AsRef<Path>
182    {
183        let mut buf = vec![];
184        try!(File::open(identity)
185                 .and_then(|mut f| f.read_to_end(&mut buf))
186                 .map_err(ServerError::Io));
187        let identity = try!(Identity::from_pkcs12(&buf, password).map_err(ServerError::Tls));
188
189        let acceptor = try!(TlsAcceptor::builder(identity)
190                                .build()
191                                .map_err(ServerError::Tls));
192        Ok(acceptor.into())
193    }
194}
195
196impl From<TlsAcceptor> for NativeTlsServer {
197    fn from(t: TlsAcceptor) -> NativeTlsServer {
198        NativeTlsServer(Arc::new(t))
199    }
200}
201
202impl<T> SslServer<T> for NativeTlsServer
203    where T: NetworkStream + Send + Clone + fmt::Debug + Sync
204{
205    type Stream = TlsStream<T>;
206
207    fn wrap_server(&self, stream: T) -> hyper::Result<TlsStream<T>> {
208        match self.0.accept(stream) {
209            Ok(s) => Ok(TlsStream(Arc::new(Mutex::new(s)))),
210            Err(e) => Err(hyper::Error::Ssl(Box::new(e))),
211        }
212    }
213}
214
215/// An error creating a `NativeTlsServer`.
216#[derive(Debug)]
217pub enum ServerError {
218    /// An error reading the identity file.
219    Io(io::Error),
220    /// An error initializing the acceptor.
221    Tls(native_tls::Error),
222}
223
224impl fmt::Display for ServerError {
225    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
226        try!(fmt.write_str(self.description()));
227        match *self {
228            ServerError::Io(ref e) => write!(fmt, ": {}", e),
229            ServerError::Tls(ref e) => write!(fmt, ": {}", e),
230        }
231    }
232}
233
234impl Error for ServerError {
235    fn description(&self) -> &str {
236        match *self {
237            ServerError::Io(_) => "error reading identity",
238            ServerError::Tls(_) => "error initializing acceptor",
239        }
240    }
241
242    fn cause(&self) -> Option<&Error> {
243        match *self {
244            ServerError::Io(ref e) => Some(e),
245            ServerError::Tls(ref e) => Some(e),
246        }
247    }
248}
249
250#[cfg(test)]
251mod test {
252    use hyper::{Client, Server};
253    use hyper::server::{Request, Response, Fresh};
254    use hyper::net::HttpsConnector;
255    use std::fs::File;
256    use std::io::Read;
257    use std::mem;
258
259    use super::*;
260
261    #[test]
262    fn client() {
263        let ssl = NativeTlsClient::new().unwrap();
264        let connector = HttpsConnector::new(ssl);
265        let client = Client::with_connector(connector);
266
267        let mut resp = client.get("https://google.com").send().unwrap();
268        assert!(resp.status.is_success());
269        let mut body = vec![];
270        resp.read_to_end(&mut body).unwrap();
271    }
272
273    #[test]
274    fn server() {
275        let ssl = NativeTlsServer::new("test/identity.p12", "mypass").unwrap();
276        let server = Server::https("127.0.0.1:0", ssl).unwrap();
277
278        let listening = server
279            .handle(|_: Request, resp: Response<Fresh>| resp.send(b"hello").unwrap())
280            .unwrap();
281        let port = listening.socket.port();
282        mem::forget(listening);
283
284
285        let mut buf = Vec::new();
286        let _ = File::open("test/root-ca.der")
287            .unwrap()
288            .read_to_end(&mut buf)
289            .unwrap();
290        let cert = Certificate::from_der(&buf).unwrap();
291
292        let mut tls_connector_builder = TlsConnector::builder();
293        tls_connector_builder.add_root_certificate(cert);
294        let tls_connector = tls_connector_builder.build().unwrap();
295
296        let native_tls_client = NativeTlsClient::from(tls_connector);
297        let connector = HttpsConnector::new(native_tls_client);
298        let client = Client::with_connector(connector);
299
300        let mut resp = client
301            .get(&format!("https://localhost:{}", port))
302            .send()
303            .unwrap();
304        let mut body = vec![];
305        resp.read_to_end(&mut body).unwrap();
306        assert_eq!(body, b"hello");
307    }
308}