feat(ssl): redesign SSL usage
BREAKING CHANGE: Server::https was changed to allow any implementation of Ssl. Server in general was also changed. HttpConnector no longer uses SSL; using HttpsConnector instead.
This commit is contained in:
		
							
								
								
									
										417
									
								
								src/net.rs
									
									
									
									
									
								
							
							
						
						
									
										417
									
								
								src/net.rs
									
									
									
									
									
								
							| @@ -4,16 +4,12 @@ use std::fmt; | ||||
| use std::io::{self, ErrorKind, Read, Write}; | ||||
| use std::net::{SocketAddr, ToSocketAddrs, TcpStream, TcpListener, Shutdown}; | ||||
| use std::mem; | ||||
| use std::path::Path; | ||||
| use std::sync::Arc; | ||||
|  | ||||
| use openssl::ssl::{Ssl, SslStream, SslContext, SSL_VERIFY_NONE}; | ||||
| use openssl::ssl::SslMethod::Sslv23; | ||||
| use openssl::ssl::error::StreamError as SslIoError; | ||||
| use openssl::x509::X509FileType; | ||||
| #[cfg(feature = "openssl")] | ||||
| pub use self::openssl::Openssl; | ||||
|  | ||||
| use typeable::Typeable; | ||||
| use {traitobject}; | ||||
| use traitobject; | ||||
|  | ||||
| /// The write-status indicating headers have not been written. | ||||
| pub enum Fresh {} | ||||
| @@ -70,9 +66,6 @@ pub trait NetworkConnector { | ||||
|     type Stream: Into<Box<NetworkStream + Send>>; | ||||
|     /// Connect to a remote address. | ||||
|     fn connect(&self, host: &str, port: u16, scheme: &str) -> ::Result<Self::Stream>; | ||||
|     /// Sets the given `ContextVerifier` to be used when verifying the SSL context | ||||
|     /// on the establishment of a new connection. | ||||
|     fn set_ssl_verifier(&mut self, verifier: ContextVerifier); | ||||
| } | ||||
|  | ||||
| impl<T: NetworkStream + Send> From<T> for Box<NetworkStream + Send> { | ||||
| @@ -143,43 +136,22 @@ impl NetworkStream + Send { | ||||
| } | ||||
|  | ||||
| /// A `NetworkListener` for `HttpStream`s. | ||||
| pub enum HttpListener { | ||||
|     /// Http variant. | ||||
|     Http(TcpListener), | ||||
|     /// Https variant. The two paths point to the certificate and key PEM files, in that order. | ||||
|     Https(TcpListener, Arc<SslContext>) | ||||
| } | ||||
| pub struct HttpListener(TcpListener); | ||||
|  | ||||
| impl Clone for HttpListener { | ||||
|     #[inline] | ||||
|     fn clone(&self) -> HttpListener { | ||||
|         match *self { | ||||
|             HttpListener::Http(ref tcp) => HttpListener::Http(tcp.try_clone().unwrap()), | ||||
|             HttpListener::Https(ref tcp, ref ssl) => HttpListener::Https(tcp.try_clone().unwrap(), ssl.clone()), | ||||
|         } | ||||
|         HttpListener(self.0.try_clone().unwrap()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl HttpListener { | ||||
|  | ||||
|     /// Start listening to an address over HTTP. | ||||
|     pub fn http<To: ToSocketAddrs>(addr: To) -> ::Result<HttpListener> { | ||||
|         Ok(HttpListener::Http(try!(TcpListener::bind(addr)))) | ||||
|     pub fn new<To: ToSocketAddrs>(addr: To) -> ::Result<HttpListener> { | ||||
|         Ok(HttpListener(try!(TcpListener::bind(addr)))) | ||||
|     } | ||||
|  | ||||
|     /// Start listening to an address over HTTPS. | ||||
|     pub fn https<To: ToSocketAddrs>(addr: To, cert: &Path, key: &Path) -> ::Result<HttpListener> { | ||||
|         let mut ssl_context = try!(SslContext::new(Sslv23)); | ||||
|         try!(ssl_context.set_cipher_list("DEFAULT")); | ||||
|         try!(ssl_context.set_certificate_file(cert, X509FileType::PEM)); | ||||
|         try!(ssl_context.set_private_key_file(key, X509FileType::PEM)); | ||||
|         ssl_context.set_verify(SSL_VERIFY_NONE, None); | ||||
|         HttpListener::https_with_context(addr, ssl_context) | ||||
|     } | ||||
|  | ||||
|     /// Start listening to an address of HTTPS using the given SslContext | ||||
|     pub fn https_with_context<To: ToSocketAddrs>(addr: To, ssl_context: SslContext) -> ::Result<HttpListener> { | ||||
|         Ok(HttpListener::Https(try!(TcpListener::bind(addr)), Arc::new(ssl_context))) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl NetworkListener for HttpListener { | ||||
| @@ -187,51 +159,42 @@ impl NetworkListener for HttpListener { | ||||
|  | ||||
|     #[inline] | ||||
|     fn accept(&mut self) -> ::Result<HttpStream> { | ||||
|         match *self { | ||||
|             HttpListener::Http(ref mut tcp) => Ok(HttpStream::Http(CloneTcpStream(try!(tcp.accept()).0))), | ||||
|             HttpListener::Https(ref mut tcp, ref ssl_context) => { | ||||
|                 let stream = CloneTcpStream(try!(tcp.accept()).0); | ||||
|                 match SslStream::new_server(&**ssl_context, stream) { | ||||
|                     Ok(ssl_stream) => Ok(HttpStream::Https(ssl_stream)), | ||||
|                     Err(SslIoError(e)) => { | ||||
|                         Err(io::Error::new(io::ErrorKind::ConnectionAborted, e).into()) | ||||
|                     }, | ||||
|                     Err(e) => Err(e.into()) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         Ok(HttpStream(try!(self.0.accept()).0)) | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     fn local_addr(&mut self) -> io::Result<SocketAddr> { | ||||
|         match *self { | ||||
|             HttpListener::Http(ref mut tcp) => tcp.local_addr(), | ||||
|             HttpListener::Https(ref mut tcp, _) => tcp.local_addr(), | ||||
|         } | ||||
|         self.0.local_addr() | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[doc(hidden)] | ||||
| pub struct CloneTcpStream(TcpStream); | ||||
| /// A wrapper around a TcpStream. | ||||
| pub struct HttpStream(pub TcpStream); | ||||
|  | ||||
| impl Clone for CloneTcpStream{ | ||||
| impl Clone for HttpStream { | ||||
|     #[inline] | ||||
|     fn clone(&self) -> CloneTcpStream { | ||||
|         CloneTcpStream(self.0.try_clone().unwrap()) | ||||
|     fn clone(&self) -> HttpStream { | ||||
|         HttpStream(self.0.try_clone().unwrap()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Read for CloneTcpStream { | ||||
| impl fmt::Debug for HttpStream { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.write_str("HttpStream(_)") | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Read for HttpStream { | ||||
|     #[inline] | ||||
|     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | ||||
|         self.0.read(buf) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Write for CloneTcpStream { | ||||
| impl Write for HttpStream { | ||||
|     #[inline] | ||||
|     fn write(&mut self, buf: &[u8]) -> io::Result<usize> { | ||||
|         self.0.write(buf) | ||||
|     fn write(&mut self, msg: &[u8]) -> io::Result<usize> { | ||||
|         self.0.write(msg) | ||||
|     } | ||||
|     #[inline] | ||||
|     fn flush(&mut self) -> io::Result<()> { | ||||
| @@ -239,85 +202,26 @@ impl Write for CloneTcpStream { | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// A wrapper around a TcpStream. | ||||
| #[derive(Clone)] | ||||
| pub enum HttpStream { | ||||
|     /// A stream over the HTTP protocol. | ||||
|     Http(CloneTcpStream), | ||||
|     /// A stream over the HTTP protocol, protected by SSL. | ||||
|     Https(SslStream<CloneTcpStream>), | ||||
| } | ||||
|  | ||||
| impl fmt::Debug for HttpStream { | ||||
|   fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { | ||||
|     match *self { | ||||
|       HttpStream::Http(_) => write!(fmt, "Http HttpStream"), | ||||
|       HttpStream::Https(_) => write!(fmt, "Https HttpStream"), | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| impl Read for HttpStream { | ||||
|     #[inline] | ||||
|     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | ||||
|         match *self { | ||||
|             HttpStream::Http(ref mut inner) => inner.read(buf), | ||||
|             HttpStream::Https(ref mut inner) => inner.read(buf) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Write for HttpStream { | ||||
|     #[inline] | ||||
|     fn write(&mut self, msg: &[u8]) -> io::Result<usize> { | ||||
|         match *self { | ||||
|             HttpStream::Http(ref mut inner) => inner.write(msg), | ||||
|             HttpStream::Https(ref mut inner) => inner.write(msg) | ||||
|         } | ||||
|     } | ||||
|     #[inline] | ||||
|     fn flush(&mut self) -> io::Result<()> { | ||||
|         match *self { | ||||
|             HttpStream::Http(ref mut inner) => inner.flush(), | ||||
|             HttpStream::Https(ref mut inner) => inner.flush(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl NetworkStream for HttpStream { | ||||
|     #[inline] | ||||
|     fn peer_addr(&mut self) -> io::Result<SocketAddr> { | ||||
|         match *self { | ||||
|             HttpStream::Http(ref mut inner) => inner.0.peer_addr(), | ||||
|             HttpStream::Https(ref mut inner) => inner.get_mut().0.peer_addr() | ||||
|         } | ||||
|             self.0.peer_addr() | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     fn close(&mut self, how: Shutdown) -> io::Result<()> { | ||||
|         #[inline] | ||||
|         fn shutdown(tcp: &mut TcpStream, how: Shutdown) -> io::Result<()> { | ||||
|             match tcp.shutdown(how) { | ||||
|                 Ok(_) => Ok(()), | ||||
|                 // see https://github.com/hyperium/hyper/issues/508 | ||||
|                 Err(ref e) if e.kind() == ErrorKind::NotConnected => Ok(()), | ||||
|                 err => err | ||||
|             } | ||||
|         match self.0.shutdown(how) { | ||||
|             Ok(_) => Ok(()), | ||||
|             // see https://github.com/hyperium/hyper/issues/508 | ||||
|             Err(ref e) if e.kind() == ErrorKind::NotConnected => Ok(()), | ||||
|             err => err | ||||
|         } | ||||
|  | ||||
|         match *self { | ||||
|             HttpStream::Http(ref mut inner) => shutdown(&mut inner.0, how), | ||||
|             HttpStream::Https(ref mut inner) => shutdown(&mut inner.get_mut().0, how) | ||||
|         } | ||||
|  | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// A connector that will produce HttpStreams. | ||||
| 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 + Sync>; | ||||
| #[derive(Debug, Clone, Default)] | ||||
| pub struct HttpConnector; | ||||
|  | ||||
| impl NetworkConnector for HttpConnector { | ||||
|     type Stream = HttpStream; | ||||
| @@ -327,19 +231,7 @@ impl NetworkConnector for HttpConnector { | ||||
|         Ok(try!(match scheme { | ||||
|             "http" => { | ||||
|                 debug!("http scheme"); | ||||
|                 Ok(HttpStream::Http(CloneTcpStream(try!(TcpStream::connect(addr))))) | ||||
|             }, | ||||
|             "https" => { | ||||
|                 debug!("https scheme"); | ||||
|                 let stream = CloneTcpStream(try!(TcpStream::connect(addr))); | ||||
|                 let mut context = try!(SslContext::new(Sslv23)); | ||||
|                 if let Some(ref verifier) = self.0 { | ||||
|                     verifier(&mut context); | ||||
|                 } | ||||
|                 let ssl = try!(Ssl::new(&context)); | ||||
|                 try!(ssl.set_hostname(host)); | ||||
|                 let stream = try!(SslStream::new(&context, stream)); | ||||
|                 Ok(HttpStream::Https(stream)) | ||||
|                 Ok(HttpStream(try!(TcpStream::connect(addr)))) | ||||
|             }, | ||||
|             _ => { | ||||
|                 Err(io::Error::new(io::ErrorKind::InvalidInput, | ||||
| @@ -347,15 +239,241 @@ impl NetworkConnector for HttpConnector { | ||||
|             } | ||||
|         })) | ||||
|     } | ||||
|     fn set_ssl_verifier(&mut self, verifier: ContextVerifier) { | ||||
|         self.0 = Some(verifier); | ||||
| } | ||||
|  | ||||
|  | ||||
| /// An abstraction to allow any SSL implementation to be used with HttpsStreams. | ||||
| pub trait Ssl { | ||||
|     /// The protected stream. | ||||
|     type Stream: NetworkStream + Send + Clone; | ||||
|     /// Wrap a client stream with SSL. | ||||
|     fn wrap_client(&self, stream: HttpStream, host: &str) -> ::Result<Self::Stream>; | ||||
|     /// Wrap a server stream with SSL. | ||||
|     fn wrap_server(&self, stream: HttpStream) -> ::Result<Self::Stream>; | ||||
| } | ||||
|  | ||||
| /// A stream over the HTTP protocol, possibly protected by SSL. | ||||
| #[derive(Debug, Clone)] | ||||
| pub enum HttpsStream<S: NetworkStream> { | ||||
|     /// A plain text stream. | ||||
|     Http(HttpStream), | ||||
|     /// A stream protected by SSL. | ||||
|     Https(S) | ||||
| } | ||||
|  | ||||
| impl<S: NetworkStream> Read for HttpsStream<S> { | ||||
|     #[inline] | ||||
|     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | ||||
|         match *self { | ||||
|             HttpsStream::Http(ref mut s) => s.read(buf), | ||||
|             HttpsStream::Https(ref mut s) => s.read(buf) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<S: NetworkStream> Write for HttpsStream<S> { | ||||
|     #[inline] | ||||
|     fn write(&mut self, msg: &[u8]) -> io::Result<usize> { | ||||
|         match *self { | ||||
|             HttpsStream::Http(ref mut s) => s.write(msg), | ||||
|             HttpsStream::Https(ref mut s) => s.write(msg) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     fn flush(&mut self) -> io::Result<()> { | ||||
|         match *self { | ||||
|             HttpsStream::Http(ref mut s) => s.flush(), | ||||
|             HttpsStream::Https(ref mut s) => s.flush() | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<S: NetworkStream> NetworkStream for HttpsStream<S> { | ||||
|     #[inline] | ||||
|     fn peer_addr(&mut self) -> io::Result<SocketAddr> { | ||||
|         match *self { | ||||
|             HttpsStream::Http(ref mut s) => s.peer_addr(), | ||||
|             HttpsStream::Https(ref mut s) => s.peer_addr() | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     fn close(&mut self, how: Shutdown) -> io::Result<()> { | ||||
|         match *self { | ||||
|             HttpsStream::Http(ref mut s) => s.close(how), | ||||
|             HttpsStream::Https(ref mut s) => s.close(how) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// A Http Listener over SSL. | ||||
| #[derive(Clone)] | ||||
| pub struct HttpsListener<S: Ssl> { | ||||
|     listener: HttpListener, | ||||
|     ssl: S, | ||||
| } | ||||
|  | ||||
| impl<S: Ssl> HttpsListener<S> { | ||||
|  | ||||
|     /// Start listening to an address over HTTPS. | ||||
|     pub fn new<To: ToSocketAddrs>(addr: To, ssl: S) -> ::Result<HttpsListener<S>> { | ||||
|         HttpListener::new(addr).map(|l| HttpsListener { | ||||
|             listener: l, | ||||
|             ssl: ssl | ||||
|         }) | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| impl<S: Ssl + Clone> NetworkListener for HttpsListener<S> { | ||||
|     type Stream = S::Stream; | ||||
|  | ||||
|     #[inline] | ||||
|     fn accept(&mut self) -> ::Result<S::Stream> { | ||||
|         self.listener.accept().and_then(|s| self.ssl.wrap_server(s)) | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     fn local_addr(&mut self) -> io::Result<SocketAddr> { | ||||
|         self.listener.local_addr() | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// A connector that can protect HTTP streams using SSL. | ||||
| #[derive(Debug, Default)] | ||||
| pub struct HttpsConnector<S: Ssl> { | ||||
|     ssl: S | ||||
| } | ||||
|  | ||||
| impl<S: Ssl> HttpsConnector<S> { | ||||
|     /// Create a new connector using the provided SSL implementation. | ||||
|     pub fn new(s: S) -> HttpsConnector<S> { | ||||
|         HttpsConnector { ssl: s } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<S: Ssl> NetworkConnector for HttpsConnector<S> { | ||||
|     type Stream = HttpsStream<S::Stream>; | ||||
|  | ||||
|     fn connect(&self, host: &str, port: u16, scheme: &str) -> ::Result<Self::Stream> { | ||||
|         let addr = &(host, port); | ||||
|         if scheme == "https" { | ||||
|             debug!("https scheme"); | ||||
|             let stream = HttpStream(try!(TcpStream::connect(addr))); | ||||
|             self.ssl.wrap_client(stream, host).map(HttpsStream::Https) | ||||
|         } else { | ||||
|             HttpConnector.connect(host, port, scheme).map(HttpsStream::Http) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| #[cfg(not(feature = "openssl"))] | ||||
| #[doc(hidden)] | ||||
| pub type DefaultConnector = HttpConnector; | ||||
|  | ||||
| #[cfg(feature = "openssl")] | ||||
| #[doc(hidden)] | ||||
| pub type DefaultConnector = HttpsConnector<self::openssl::Openssl>; | ||||
|  | ||||
| #[cfg(feature = "openssl")] | ||||
| mod openssl { | ||||
|     use std::io; | ||||
|     use std::net::{SocketAddr, Shutdown}; | ||||
|     use std::path::Path; | ||||
|     use std::sync::Arc; | ||||
|     use openssl::ssl::{Ssl, SslContext, SslStream, SslMethod, SSL_VERIFY_NONE}; | ||||
|     use openssl::ssl::error::StreamError as SslIoError; | ||||
|     use openssl::ssl::error::SslError; | ||||
|     use openssl::x509::X509FileType; | ||||
|     use super::{NetworkStream, HttpStream}; | ||||
|  | ||||
|  | ||||
|     /// An implementation of `Ssl` for OpenSSL. | ||||
|     /// | ||||
|     /// # Example | ||||
|     /// | ||||
|     /// ```no_run | ||||
|     /// use hyper::Server; | ||||
|     /// use hyper::net::Openssl; | ||||
|     /// | ||||
|     /// let ssl = Openssl::with_cert_and_key("/home/foo/cert", "/home/foo/key").unwrap(); | ||||
|     /// Server::https("0.0.0.0:443", ssl).unwrap(); | ||||
|     /// ``` | ||||
|     /// | ||||
|     /// For complete control, create a `SslContext` with the options you desire | ||||
|     /// and then create `Openssl { context: ctx } | ||||
|     #[derive(Debug, Clone)] | ||||
|     pub struct Openssl { | ||||
|         /// The `SslContext` from openssl crate. | ||||
|         pub context: Arc<SslContext> | ||||
|     } | ||||
|  | ||||
|     impl Default for Openssl { | ||||
|         fn default() -> Openssl { | ||||
|             Openssl { | ||||
|                 context: Arc::new(SslContext::new(SslMethod::Sslv23).unwrap_or_else(|e| { | ||||
|                     // if we cannot create a SslContext, that's because of a | ||||
|                     // serious problem. just crash. | ||||
|                     panic!("{}", e) | ||||
|                 })) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     impl Openssl { | ||||
|         /// Ease creating an `Openssl` with a certificate and key. | ||||
|         pub fn with_cert_and_key<C, K>(cert: C, key: K) -> Result<Openssl, SslError> | ||||
|         where C: AsRef<Path>, K: AsRef<Path> { | ||||
|             let mut ctx = try!(SslContext::new(SslMethod::Sslv23)); | ||||
|             try!(ctx.set_cipher_list("DEFAULT")); | ||||
|             try!(ctx.set_certificate_file(cert.as_ref(), X509FileType::PEM)); | ||||
|             try!(ctx.set_private_key_file(key.as_ref(), X509FileType::PEM)); | ||||
|             ctx.set_verify(SSL_VERIFY_NONE, None); | ||||
|             Ok(Openssl { context: Arc::new(ctx) }) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     impl super::Ssl for Openssl { | ||||
|         type Stream = SslStream<HttpStream>; | ||||
|  | ||||
|         fn wrap_client(&self, stream: HttpStream, host: &str) -> ::Result<Self::Stream> { | ||||
|             //if let Some(ref verifier) = self.verifier { | ||||
|             //    verifier(&mut context); | ||||
|             //} | ||||
|             let ssl = try!(Ssl::new(&self.context)); | ||||
|             try!(ssl.set_hostname(host)); | ||||
|             SslStream::new(&self.context, stream).map_err(From::from) | ||||
|         } | ||||
|  | ||||
|         fn wrap_server(&self, stream: HttpStream) -> ::Result<Self::Stream> { | ||||
|             match SslStream::new_server(&self.context, stream) { | ||||
|                 Ok(ssl_stream) => Ok(ssl_stream), | ||||
|                 Err(SslIoError(e)) => { | ||||
|                     Err(io::Error::new(io::ErrorKind::ConnectionAborted, e).into()) | ||||
|                 }, | ||||
|                 Err(e) => Err(e.into()) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     impl<S: NetworkStream> NetworkStream for SslStream<S> { | ||||
|         #[inline] | ||||
|         fn peer_addr(&mut self) -> io::Result<SocketAddr> { | ||||
|             self.get_mut().peer_addr() | ||||
|         } | ||||
|  | ||||
|         fn close(&mut self, how: Shutdown) -> io::Result<()> { | ||||
|             self.get_mut().close(how) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use mock::MockStream; | ||||
|     use super::{NetworkStream, HttpConnector, NetworkConnector}; | ||||
|     use super::{NetworkStream}; | ||||
|  | ||||
|     #[test] | ||||
|     fn test_downcast_box_stream() { | ||||
| @@ -376,13 +494,4 @@ mod tests { | ||||
|         assert_eq!(mock, Box::new(MockStream::new())); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_http_connector_set_ssl_verifier() { | ||||
|         let mut connector = HttpConnector(None); | ||||
|  | ||||
|         connector.set_ssl_verifier(Box::new(|_| {})); | ||||
|  | ||||
|         assert!(connector.0.is_some()); | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user