From ec3b139273f922faeebbc615e75ce0cbb45db728 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Wed, 1 Nov 2017 16:34:58 -0700 Subject: [PATCH] Allow to start tls server with HttpServer::serve_tls --- .travis.yml | 2 +- CHANGES.md | 4 ++ Cargo.toml | 8 +++- examples/tls/Cargo.toml | 14 ++++++ examples/tls/identity.pfx | Bin 0 -> 4101 bytes examples/tls/src/main.rs | 46 ++++++++++++++++++++ src/lib.rs | 8 ++++ src/server.rs | 89 ++++++++++++++++++++++++++++++-------- 8 files changed, 152 insertions(+), 19 deletions(-) create mode 100644 examples/tls/Cargo.toml create mode 100644 examples/tls/identity.pfx create mode 100644 examples/tls/src/main.rs 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 0000000000000000000000000000000000000000..ac69a0289672f555f18dbcab871c7edde0845c14 GIT binary patch literal 4101 zcmV+g5c=;hf)D`$0Ru3C561=xDuzgg_YDCD0ic2pxCDX^v@n7XurPuJ=LQKXhDe6@ z4FLxRpn?V1FoFft0s#Opf(6Y62`Yw2hW8Bt2LUh~1_~;MNQUitMs z1Sq+50s;sCfPw{}<#X+T_wRMWeQDK0E}M>U7Lb3ZdgD@8y}opCzFWj5KGdMxof%CJz46GvRpEF}U%J9)s~gZ8&DS}z61t(6i>!fc--h~jb$cDvRU zGoF2AL@mg>L@1{6-ratdjmN8il0ks94GzXdOgQGhIK#e$6#Y8Bj{W6eekebHL9B6Z zTAO+ohol+!k`ZfL=eX) z+bf}!yS6F%DdMI)xgHT<8xs%XV2AY@%6XTJdK$bf7ujrMcdB%<6!*-q+i<^%NxPc` z?qlODfVLp)^}^EWKUV1*Yp^e=MkdZbI27Y$Ti2P*A};*|TNmRJNfx&3hRCtMOgx>z z2cwsx8nYwO2VnDSbli{67AFr{FTR0ZD0fKCZ8 z;m8&dlz*hRkm!jzAnif!<6-F^IwrJZcms5(F)3=Q3URTqr|ZQK2Qkz2=E5LYDp
>TH6JOpl_YGLj13;m-9ACQ-$ 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 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> {