use std::{io, net}; use std::rc::Rc; use std::net::SocketAddr; use std::marker::PhantomData; use actix::dev::*; use futures::Stream; use tokio_io::{AsyncRead, AsyncWrite}; use tokio_core::net::{TcpListener, TcpStream}; #[cfg(feature="tls")] use futures::Future; #[cfg(feature="tls")] use native_tls::TlsAcceptor; #[cfg(feature="tls")] use tokio_tls::{TlsStream, TlsAcceptorExt}; #[cfg(feature="alpn")] use futures::Future; #[cfg(feature="alpn")] use openssl::ssl::{SslMethod, SslAcceptorBuilder}; #[cfg(feature="alpn")] use openssl::pkcs12::ParsedPkcs12; #[cfg(feature="alpn")] use tokio_openssl::{SslStream, SslAcceptorExt}; use channel::{HttpChannel, HttpHandler, IntoHttpHandler}; /// An HTTP Server /// /// `T` - async stream, anything that implements `AsyncRead` + `AsyncWrite`. /// /// `A` - peer address /// /// `H` - request handler pub struct HttpServer { h: Rc>, io: PhantomData, addr: PhantomData, } impl Actor for HttpServer { type Context = Context; } impl HttpServer where H: HttpHandler { /// Create new http server with vec of http handlers pub fn new>(handler: U) -> Self where V: IntoHttpHandler { let apps: Vec<_> = handler.into_iter().map(|h| h.into_handler()).collect(); HttpServer {h: Rc::new(apps), io: PhantomData, addr: PhantomData} } } impl HttpServer where T: AsyncRead + AsyncWrite + 'static, A: 'static, H: HttpHandler, { /// Start listening for incomming connections from stream. pub fn serve_incoming(self, stream: S) -> io::Result where Self: ActorAddress, S: Stream + 'static { Ok(HttpServer::create(move |ctx| { ctx.add_stream(stream.map(|(t, _)| IoStream(t, None, false))); self })) } fn bind(&self, addr: S) -> io::Result> { let mut err = None; let mut addrs = Vec::new(); if let Ok(iter) = addr.to_socket_addrs() { for addr in iter { match TcpListener::bind(&addr, Arbiter::handle()) { Ok(tcp) => addrs.push((addr, tcp)), Err(e) => err = Some(e), } } } if addrs.is_empty() { if let Some(e) = err.take() { Err(e) } else { Err(io::Error::new(io::ErrorKind::Other, "Can not bind to address.")) } } else { Ok(addrs) } } } impl HttpServer { /// Start listening for incomming connections. /// /// This methods converts address to list of `SocketAddr` /// then binds to all available addresses. pub fn serve(self, addr: S) -> io::Result where Self: ActorAddress, S: net::ToSocketAddrs, { let addrs = self.bind(addr)?; Ok(HttpServer::create(move |ctx| { for (addr, tcp) in addrs { info!("Starting http server on {}", addr); ctx.add_stream(tcp.incoming().map(|(t, a)| IoStream(t, Some(a), false))); } self })) } } #[cfg(feature="tls")] impl HttpServer, net::SocketAddr, H> { /// Start listening for incomming tls connections. /// /// This methods converts address to list of `SocketAddr` /// then binds to all available addresses. pub fn serve_tls(self, addr: S, pkcs12: ::Pkcs12) -> io::Result where Self: ActorAddress, S: net::ToSocketAddrs, { let addrs = self.bind(addr)?; let acceptor = match TlsAcceptor::builder(pkcs12) { Ok(builder) => { match builder.build() { Ok(acceptor) => Rc::new(acceptor), Err(err) => return Err(io::Error::new(io::ErrorKind::Other, err)) } } Err(err) => return Err(io::Error::new(io::ErrorKind::Other, err)) }; Ok(HttpServer::create(move |ctx| { for (addr, tcp) in addrs { info!("Starting tls http server on {}", addr); let acc = acceptor.clone(); ctx.add_stream(tcp.incoming().and_then(move |(stream, addr)| { TlsAcceptorExt::accept_async(acc.as_ref(), stream) .map(move |t| { IoStream(t, Some(addr), false) }) .map_err(|err| { trace!("Error during handling tls connection: {}", err); io::Error::new(io::ErrorKind::Other, err) }) })); } self })) } } #[cfg(feature="alpn")] impl HttpServer, net::SocketAddr, H> { /// Start listening for incomming tls connections. /// /// This methods converts address to list of `SocketAddr` /// then binds to all available addresses. pub fn serve_tls(self, addr: S, identity: ParsedPkcs12) -> io::Result where Self: ActorAddress, S: net::ToSocketAddrs, { let addrs = self.bind(addr)?; let acceptor = match SslAcceptorBuilder::mozilla_intermediate(SslMethod::tls(), &identity.pkey, &identity.cert, &identity.chain) { Ok(mut builder) => { match builder.builder_mut().set_alpn_protocols(&[b"h2", b"http/1.1"]) { Ok(_) => builder.build(), Err(err) => return Err(io::Error::new(io::ErrorKind::Other, err)), } }, Err(err) => return Err(io::Error::new(io::ErrorKind::Other, err)) }; Ok(HttpServer::create(move |ctx| { for (addr, tcp) in addrs { info!("Starting tls http server on {}", addr); let acc = acceptor.clone(); ctx.add_stream(tcp.incoming().and_then(move |(stream, addr)| { SslAcceptorExt::accept_async(&acc, stream) .map(move |stream| { let http2 = if let Some(p) = stream.get_ref().ssl().selected_alpn_protocol() { p.len() == 2 && &p == b"h2" } else { false }; IoStream(stream, Some(addr), http2) }) .map_err(|err| { trace!("Error during handling tls connection: {}", err); io::Error::new(io::ErrorKind::Other, err) }) })); } self })) } } struct IoStream(T, Option, bool); impl ResponseType for IoStream where T: AsyncRead + AsyncWrite + 'static { type Item = (); type Error = (); } impl StreamHandler, io::Error> for HttpServer where T: AsyncRead + AsyncWrite + 'static, H: HttpHandler + 'static, A: 'static {} impl Handler, io::Error> for HttpServer where T: AsyncRead + AsyncWrite + 'static, H: HttpHandler + 'static, A: 'static, { fn error(&mut self, err: io::Error, _: &mut Context) { debug!("Error handling request: {}", err) } fn handle(&mut self, msg: IoStream, _: &mut Context) -> Response> { Arbiter::handle().spawn( HttpChannel::new(msg.0, msg.1, Rc::clone(&self.h), msg.2)); Self::empty() } }