diff --git a/.travis.yml b/.travis.yml index 329511c98..275c9e0f4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,7 +33,7 @@ before_script: - export PATH=$PATH:~/.cargo/bin script: - - cargo test --no-default-features + - USE_SKEPTIC=1 cargo test --no-default-features - | if [[ "$TRAVIS_RUST_VERSION" == "nightly" && $CLIPPY ]]; then cargo clippy diff --git a/CHANGES.md b/CHANGES.md index fa2d2c95c..2c3ca287d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,10 @@ # Changes +## 0.2.1 (2017-11-xx) + +* Allow to start tls server with `HttpServer::serve_tls` + ## 0.2.0 (2017-10-30) * Do not use `http::Uri` as it can not parse some valid paths diff --git a/Cargo.toml b/Cargo.toml index 42fd1d369..94c2f609b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,9 @@ path = "src/lib.rs" [features] default = [] -# http/2 +# tls +tls = ["native-tls", "tokio-tls"] + # http2 = ["h2"] [dependencies] @@ -49,6 +51,10 @@ tokio-io = "0.1" tokio-core = "0.1" # h2 = { git = 'https://github.com/carllerche/h2', optional = true } +# tls +native-tls = { version="0.1", optional = true } +tokio-tls = { version="0.1", optional = true } + [dependencies.actix] version = ">=0.3.1" #path = "../actix" diff --git a/examples/tls/Cargo.toml b/examples/tls/Cargo.toml new file mode 100644 index 000000000..bb983dc49 --- /dev/null +++ b/examples/tls/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "ssl-example" +version = "0.1.0" +authors = ["Nikolay Kim "] + +[[bin]] +name = "server" +path = "src/main.rs" + +[dependencies] +env_logger = "0.4" + +actix = "0.3.1" +actix-web = { path = "../../", features=["tls"] } diff --git a/examples/tls/identity.pfx b/examples/tls/identity.pfx new file mode 100644 index 000000000..ac69a0289 Binary files /dev/null and b/examples/tls/identity.pfx differ diff --git a/examples/tls/src/main.rs b/examples/tls/src/main.rs new file mode 100644 index 000000000..9a2c12258 --- /dev/null +++ b/examples/tls/src/main.rs @@ -0,0 +1,46 @@ +#![allow(unused_variables)] +extern crate actix; +extern crate actix_web; +extern crate env_logger; + +//use tokio_tls; +use std::fs::File; +use std::io::Read; +// use native_tls::{TlsAcceptor, TlsStream}; + +use actix_web::*; + +/// somple handle +fn index(req: &mut HttpRequest, _payload: Payload, state: &()) -> HttpResponse { + println!("{:?}", req); + httpcodes::HTTPOk.with_body("Welcome!") +} + +fn main() { + ::std::env::set_var("RUST_LOG", "actix_web=info"); + let _ = env_logger::init(); + let sys = actix::System::new("ws-example"); + + let mut file = File::open("identity.pfx").unwrap(); + let mut pkcs12 = vec![]; + file.read_to_end(&mut pkcs12).unwrap(); + let pkcs12 = Pkcs12::from_der(&pkcs12, "12345").unwrap(); + + HttpServer::new( + Application::default("/") + // enable logger + .middleware(Logger::new(None)) + // register simple handler, handle all methods + .handler("/index.html", index) + // with path parameters + .resource("/", |r| r.handler(Method::GET, |req, _, _| { + Ok(httpcodes::HTTPFound + .builder() + .header("LOCATION", "/index.html") + .body(Body::Empty)?) + }))) + .serve_tls::<_, ()>("127.0.0.1:8080", pkcs12).unwrap(); + + println!("Started http server: 127.0.0.1:8080"); + let _ = sys.run(); +} diff --git a/src/lib.rs b/src/lib.rs index af37f5be1..f064676a7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,6 +21,11 @@ extern crate url; extern crate percent_encoding; extern crate actix; +#[cfg(feature="tls")] +extern crate native_tls; +#[cfg(feature="tls")] +extern crate tokio_tls; + mod application; mod body; mod context; @@ -64,3 +69,6 @@ pub use http::{Method, StatusCode, Version}; pub use cookie::{Cookie, CookieBuilder}; pub use cookie::{ParseError as CookieParseError}; pub use http_range::{HttpRange, HttpRangeParseError}; + +#[cfg(feature="tls")] +pub use native_tls::Pkcs12; diff --git a/src/server.rs b/src/server.rs index 713cca903..149d5bb40 100644 --- a/src/server.rs +++ b/src/server.rs @@ -11,6 +11,11 @@ use tokio_core::reactor::Timeout; use tokio_core::net::{TcpListener, TcpStream}; use tokio_io::{AsyncRead, AsyncWrite}; +#[cfg(feature="tls")] +use native_tls::TlsAcceptor; +#[cfg(feature="tls")] +use tokio_tls::{TlsStream, TlsAcceptorExt}; + use task::Task; use reader::{Reader, ReaderError}; use payload::Payload; @@ -69,17 +74,9 @@ impl HttpServer self })) } -} -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, + fn bind(&self, addr: S) + -> io::Result> { let mut err = None; let mut addrs = Vec::new(); @@ -98,17 +95,71 @@ impl HttpServer { Err(io::Error::new(io::ErrorKind::Other, "Can not bind to address.")) } } else { - 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, a))); - } - self - })) + 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, a))); + } + 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, addr)) + .map_err(|err| io::Error::new(io::ErrorKind::Other, err)) + })); + } + self + })) + } +} + struct IoStream(T, A); impl ResponseType for IoStream @@ -129,6 +180,10 @@ impl Handler, io::Error> for HttpServer A: 'static, H: HttpHandler + 'static, { + fn error(&mut self, err: io::Error, _: &mut Context) { + trace!("Error handling request: {}", err) + } + fn handle(&mut self, msg: IoStream, _: &mut Context) -> Response> {