//! A collection of traits abstracting over Listeners and Streams. use std::any::{Any, AnyRefExt}; use std::boxed::BoxAny; use std::fmt; use std::intrinsics::TypeId; use std::io::{IoResult, IoError, ConnectionAborted, InvalidInput, OtherIoError, Stream, Listener, Acceptor}; use std::io::net::ip::{SocketAddr, ToSocketAddr, Port}; use std::io::net::tcp::{TcpStream, TcpListener, TcpAcceptor}; use std::mem::{mod, transmute, transmute_copy}; use std::raw::{mod, TraitObject}; use uany::UncheckedBoxAnyDowncast; use openssl::ssl::{Ssl, SslStream, SslContext, VerifyCallback}; use openssl::ssl::SslVerifyMode::SslVerifyPeer; use openssl::ssl::SslMethod::Sslv23; use openssl::ssl::error::{SslError, StreamError, OpenSslErrors, SslSessionClosed}; use self::HttpStream::{Http, Https}; /// The write-status indicating headers have not been written. #[allow(missing_copy_implementations)] pub struct Fresh; /// The write-status indicating headers have been written. #[allow(missing_copy_implementations)] pub struct Streaming; /// An abstraction to listen for connections on a certain port. pub trait NetworkListener>: Listener { /// Bind to a socket. /// /// Note: This does not start listening for connections. You must call /// `listen()` to do that. fn bind(addr: To) -> IoResult; /// Get the address this Listener ended up listening on. fn socket_name(&mut self) -> IoResult; } /// An abstraction to receive `NetworkStream`s. pub trait NetworkAcceptor: Acceptor + Clone + Send { /// Closes the Acceptor, so no more incoming connections will be handled. fn close(&mut self) -> IoResult<()>; } /// An abstraction over streams that a Server can utilize. pub trait NetworkStream: Stream + Any + StreamClone + Send { /// Get the remote address of the underlying connection. fn peer_name(&mut self) -> IoResult; } #[doc(hidden)] pub trait StreamClone { fn clone_box(&self) -> Box; } impl StreamClone for T { #[inline] fn clone_box(&self) -> Box { box self.clone() } } /// A connector creates a NetworkStream. pub trait NetworkConnector { /// Connect to a remote address. fn connect(&mut self, host: &str, port: Port, scheme: &str) -> IoResult; } impl fmt::Show for Box { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt.pad("Box") } } impl Clone for Box { #[inline] fn clone(&self) -> Box { self.clone_box() } } impl Reader for Box { #[inline] fn read(&mut self, buf: &mut [u8]) -> IoResult { (**self).read(buf) } } impl Writer for Box { #[inline] fn write(&mut self, msg: &[u8]) -> IoResult<()> { (**self).write(msg) } #[inline] fn flush(&mut self) -> IoResult<()> { (**self).flush() } } impl<'a> Reader for &'a mut NetworkStream { #[inline] fn read(&mut self, buf: &mut [u8]) -> IoResult { (**self).read(buf) } } impl<'a> Writer for &'a mut NetworkStream { #[inline] fn write(&mut self, msg: &[u8]) -> IoResult<()> { (**self).write(msg) } #[inline] fn flush(&mut self) -> IoResult<()> { (**self).flush() } } impl UncheckedBoxAnyDowncast for Box { unsafe fn downcast_unchecked(self) -> Box { let to = *mem::transmute::<&Box, &raw::TraitObject>(&self); // Prevent double-free. mem::forget(self); mem::transmute(to.data) } } impl<'a> AnyRefExt<'a> for &'a (NetworkStream + 'a) { #[inline] fn is(self) -> bool { self.get_type_id() == TypeId::of::() } #[inline] fn downcast_ref(self) -> Option<&'a T> { if self.is::() { unsafe { // Get the raw representation of the trait object let to: TraitObject = transmute_copy(&self); // Extract the data pointer Some(transmute(to.data)) } } else { None } } } impl BoxAny for Box { fn downcast(self) -> Result, Box> { if self.is::() { Ok(unsafe { self.downcast_unchecked() }) } else { Err(self) } } } /// A `NetworkListener` for `HttpStream`s. pub struct HttpListener { inner: TcpListener } impl Listener for HttpListener { #[inline] fn listen(self) -> IoResult { Ok(HttpAcceptor { inner: try!(self.inner.listen()) }) } } impl NetworkListener for HttpListener { #[inline] fn bind(addr: To) -> IoResult { Ok(HttpListener { inner: try!(TcpListener::bind(addr)) }) } #[inline] fn socket_name(&mut self) -> IoResult { self.inner.socket_name() } } /// A `NetworkAcceptor` for `HttpStream`s. #[deriving(Clone)] pub struct HttpAcceptor { inner: TcpAcceptor } impl Acceptor for HttpAcceptor { #[inline] fn accept(&mut self) -> IoResult { Ok(Http(try!(self.inner.accept()))) } } impl NetworkAcceptor for HttpAcceptor { #[inline] fn close(&mut self) -> IoResult<()> { self.inner.close_accept() } } /// A wrapper around a TcpStream. #[deriving(Clone)] pub enum HttpStream { /// A stream over the HTTP protocol. Http(TcpStream), /// A stream over the HTTP protocol, protected by SSL. Https(SslStream), } impl Reader for HttpStream { #[inline] fn read(&mut self, buf: &mut [u8]) -> IoResult { match *self { Http(ref mut inner) => inner.read(buf), Https(ref mut inner) => inner.read(buf) } } } impl Writer for HttpStream { #[inline] fn write(&mut self, msg: &[u8]) -> IoResult<()> { match *self { Http(ref mut inner) => inner.write(msg), Https(ref mut inner) => inner.write(msg) } } #[inline] fn flush(&mut self) -> IoResult<()> { match *self { Http(ref mut inner) => inner.flush(), Https(ref mut inner) => inner.flush(), } } } impl NetworkStream for HttpStream { fn peer_name(&mut self) -> IoResult { match *self { Http(ref mut inner) => inner.peer_name(), Https(ref mut inner) => inner.get_mut().peer_name() } } } /// A connector that will produce HttpStreams. #[allow(missing_copy_implementations)] pub struct HttpConnector(pub Option); impl NetworkConnector for HttpConnector { fn connect(&mut self, host: &str, port: Port, scheme: &str) -> IoResult { let addr = (host, port); match scheme { "http" => { debug!("http scheme"); Ok(Http(try!(TcpStream::connect(addr)))) }, "https" => { debug!("https scheme"); let stream = try!(TcpStream::connect(addr)); let mut context = try!(SslContext::new(Sslv23).map_err(lift_ssl_error)); self.0.as_ref().map(|cb| context.set_verify(SslVerifyPeer, Some(*cb))); let ssl = try!(Ssl::new(&context).map_err(lift_ssl_error)); try!(ssl.set_hostname(host).map_err(lift_ssl_error)); let stream = try!(SslStream::new(&context, stream).map_err(lift_ssl_error)); Ok(Https(stream)) }, _ => { Err(IoError { kind: InvalidInput, desc: "Invalid scheme for Http", detail: None }) } } } } fn lift_ssl_error(ssl: SslError) -> IoError { debug!("lift_ssl_error: {}", ssl); match ssl { StreamError(err) => err, SslSessionClosed => IoError { kind: ConnectionAborted, desc: "SSL Connection Closed", detail: None }, // Unfortunately throw this away. No way to support this // detail without a better Error abstraction. OpenSslErrors(errs) => IoError { kind: OtherIoError, desc: "Error in OpenSSL", detail: Some(format!("{}", errs)) } } } #[cfg(test)] mod tests { use std::boxed::BoxAny; use uany::UncheckedBoxAnyDowncast; use mock::MockStream; use super::NetworkStream; #[test] fn test_downcast_box_stream() { let stream = box MockStream::new() as Box; let mock = stream.downcast::().unwrap(); assert_eq!(mock, box MockStream::new()); } #[test] fn test_downcast_unchecked_box_stream() { let stream = box MockStream::new() as Box; let mock = unsafe { stream.downcast_unchecked::() }; assert_eq!(mock, box MockStream::new()); } }