Skip to content

Commit

Permalink
Add optional TLS support
Browse files Browse the repository at this point in the history
  • Loading branch information
seanmonstar committed Dec 12, 2018
1 parent 086fc41 commit 1a3ad0c
Show file tree
Hide file tree
Showing 8 changed files with 337 additions and 9 deletions.
4 changes: 4 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ matrix:
- rust: 1.28.0
script: cargo build

# `tls` feature
- rust: stable
script: cargo test --features tls

script:
- cargo test
- 'if [ "$TRAVIS_RUST_VERSION" == "nightly" ]; then cargo test --benches; fi'
Expand Down
10 changes: 8 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ futures = "0.1"
#headers = { path = "../headers" }
headers-ext = "0.0.3"
http = "0.1"
hyper = "0.12.10"
hyper = "0.12.18"
log = "0.4"
mime = "0.3"
mime_guess = "2.0.0-alpha.6"
Expand All @@ -26,17 +26,23 @@ serde_json = "1.0"
serde_urlencoded = "0.5.3"
tokio = "0.1.11"
tokio-io = "0.1"
rustls = { version = "0.14", optional = true }
tokio-threadpool = "0.1.7"
# tls is enabled by default, we don't want that yet
tungstenite = { default-features = false, version = "0.6" }
urlencoding = "1.0.0"


[dev-dependencies]
pretty_env_logger = "0.2"
serde_derive = "1.0"
handlebars = "1.0.0"

[features]
tls = ["rustls"]

[package.metadata.docs.rs]
features = ["tls"]

[profile.release]
codegen-units = 1
incremental = false
Expand Down
23 changes: 23 additions & 0 deletions examples/tls.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#![deny(warnings)]
extern crate warp;

// Don't copy this `cfg`, it's only needed because this file is within
// the warp repository.
#[cfg(feature = "tls")]
fn main() {
use warp::Filter;

// Match any request and return hello world!
let routes = warp::any()
.map(|| "Hello, World!");

warp::serve(routes)
.tls("examples/tls/cert.pem", "examples/tls/key.rsa")
.run(([127, 0, 0, 1], 3030));
}


#[cfg(not(feature = "tls"))]
fn main() {
eprintln!("Requires the `tls` feature.");
}
24 changes: 24 additions & 0 deletions examples/tls/cert.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
-----BEGIN CERTIFICATE-----
MIIEADCCAmigAwIBAgICAcgwDQYJKoZIhvcNAQELBQAwLDEqMCgGA1UEAwwhcG9u
eXRvd24gUlNBIGxldmVsIDIgaW50ZXJtZWRpYXRlMB4XDTE2MDgxMzE2MDcwNFoX
DTIyMDIwMzE2MDcwNFowGTEXMBUGA1UEAwwOdGVzdHNlcnZlci5jb20wggEiMA0G
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCpVhh1/FNP2qvWenbZSghari/UThwe
dynfnHG7gc3JmygkEdErWBO/CHzHgsx7biVE5b8sZYNEDKFojyoPHGWK2bQM/FTy
niJCgNCLdn6hUqqxLAml3cxGW77hAWu94THDGB1qFe+eFiAUnDmob8gNZtAzT6Ky
b/JGJdrEU0wj+Rd7wUb4kpLInNH/Jc+oz2ii2AjNbGOZXnRz7h7Kv3sO9vABByYe
LcCj3qnhejHMqVhbAT1MD6zQ2+YKBjE52MsQKU/xhUpu9KkUyLh0cxkh3zrFiKh4
Vuvtc+n7aeOv2jJmOl1dr0XLlSHBlmoKqH6dCTSbddQLmlK7dms8vE01AgMBAAGj
gb4wgbswDAYDVR0TAQH/BAIwADALBgNVHQ8EBAMCBsAwHQYDVR0OBBYEFMeUzGYV
bXwJNQVbY1+A8YXYZY8pMEIGA1UdIwQ7MDmAFJvEsUi7+D8vp8xcWvnEdVBGkpoW
oR6kHDAaMRgwFgYDVQQDDA9wb255dG93biBSU0EgQ0GCAXswOwYDVR0RBDQwMoIO
dGVzdHNlcnZlci5jb22CFXNlY29uZC50ZXN0c2VydmVyLmNvbYIJbG9jYWxob3N0
MA0GCSqGSIb3DQEBCwUAA4IBgQBsk5ivAaRAcNgjc7LEiWXFkMg703AqDDNx7kB1
RDgLalLvrjOfOp2jsDfST7N1tKLBSQ9bMw9X4Jve+j7XXRUthcwuoYTeeo+Cy0/T
1Q78ctoX74E2nB958zwmtRykGrgE/6JAJDwGcgpY9kBPycGxTlCN926uGxHsDwVs
98cL6ZXptMLTR6T2XP36dAJZuOICSqmCSbFR8knc/gjUO36rXTxhwci8iDbmEVaf
BHpgBXGU5+SQ+QM++v6bHGf4LNQC5NZ4e4xvGax8ioYu/BRsB/T3Lx+RlItz4zdU
XuxCNcm3nhQV2ZHquRdbSdoyIxV5kJXel4wCmOhWIq7A2OBKdu5fQzIAzzLi65EN
RPAKsKB4h7hGgvciZQ7dsMrlGw0DLdJ6UrFyiR5Io7dXYT/+JP91lP5xsl6Lhg9O
FgALt7GSYRm2cZdgi9pO9rRr83Br1VjQT1vHz6yoZMXSqc4A2zcN2a2ZVq//rHvc
FZygs8miAhWPzqnpmgTj1cPiU1M=
-----END CERTIFICATE-----
27 changes: 27 additions & 0 deletions examples/tls/key.rsa
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAqVYYdfxTT9qr1np22UoIWq4v1E4cHncp35xxu4HNyZsoJBHR
K1gTvwh8x4LMe24lROW/LGWDRAyhaI8qDxxlitm0DPxU8p4iQoDQi3Z+oVKqsSwJ
pd3MRlu+4QFrveExwxgdahXvnhYgFJw5qG/IDWbQM0+ism/yRiXaxFNMI/kXe8FG
+JKSyJzR/yXPqM9ootgIzWxjmV50c+4eyr97DvbwAQcmHi3Ao96p4XoxzKlYWwE9
TA+s0NvmCgYxOdjLEClP8YVKbvSpFMi4dHMZId86xYioeFbr7XPp+2njr9oyZjpd
Xa9Fy5UhwZZqCqh+nQk0m3XUC5pSu3ZrPLxNNQIDAQABAoIBAFKtZJgGsK6md4vq
kyiYSufrcBLaaEQ/rkQtYCJKyC0NAlZKFLRy9oEpJbNLm4cQSkYPXn3Qunx5Jj2k
2MYz+SgIDy7f7KHgr52Ew020dzNQ52JFvBgt6NTZaqL1TKOS1fcJSSNIvouTBerK
NCSXHzfb4P+MfEVe/w1c4ilE+kH9SzdEo2jK/sRbzHIY8TX0JbmQ4SCLLayr22YG
usIxtIYcWt3MMP/G2luRnYzzBCje5MXdpAhlHLi4TB6x4h5PmBKYc57uOVNngKLd
YyrQKcszW4Nx5v0a4HG3A5EtUXNCco1+5asXOg2lYphQYVh2R+1wgu5WiDjDVu+6
EYgjFSkCgYEA0NBk6FDoxE/4L/4iJ4zIhu9BptN8Je/uS5c6wRejNC/VqQyw7SHb
hRFNrXPvq5Y+2bI/DxtdzZLKAMXOMjDjj0XEgfOIn2aveOo3uE7zf1i+njxwQhPu
uSYA9AlBZiKGr2PCYSDPnViHOspVJjxRuAgyWM1Qf+CTC0D95aj0oz8CgYEAz5n4
Cb3/WfUHxMJLljJ7PlVmlQpF5Hk3AOR9+vtqTtdxRjuxW6DH2uAHBDdC3OgppUN4
CFj55kzc2HUuiHtmPtx8mK6G+otT7Lww+nLSFL4PvZ6CYxqcio5MPnoYd+pCxrXY
JFo2W7e4FkBOxb5PF5So5plg+d0z/QiA7aFP1osCgYEAtgi1rwC5qkm8prn4tFm6
hkcVCIXc+IWNS0Bu693bXKdGr7RsmIynff1zpf4ntYGpEMaeymClCY0ppDrMYlzU
RBYiFNdlBvDRj6s/H+FTzHRk2DT/99rAhY9nzVY0OQFoQIXK8jlURGrkmI/CYy66
XqBmo5t4zcHM7kaeEBOWEKkCgYAYnO6VaRtPNQfYwhhoFFAcUc+5t+AVeHGW/4AY
M5qlAlIBu64JaQSI5KqwS0T4H+ZgG6Gti68FKPO+DhaYQ9kZdtam23pRVhd7J8y+
xMI3h1kiaBqZWVxZ6QkNFzizbui/2mtn0/JB6YQ/zxwHwcpqx0tHG8Qtm5ZAV7PB
eLCYhQKBgQDALJxU/6hMTdytEU5CLOBSMby45YD/RrfQrl2gl/vA0etPrto4RkVq
UrkDO/9W4mZORClN3knxEFSTlYi8YOboxdlynpFfhcs82wFChs+Ydp1eEsVHAqtu
T+uzn0sroycBiBfVB949LExnzGDFUkhG0i2c2InarQYLTsIyHCIDEA==
-----END RSA PRIVATE KEY-----
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,9 @@ extern crate serde;
extern crate serde_json;
extern crate serde_urlencoded;
extern crate tokio;
#[cfg_attr(feature = "tls", macro_use)]
extern crate tokio_io;
#[cfg(feature = "tls")] extern crate rustls;
extern crate tokio_threadpool;
extern crate tungstenite;
extern crate urlencoding;
Expand All @@ -112,6 +114,7 @@ pub mod reply;
mod route;
mod server;
pub mod test;
#[cfg(feature = "tls")] mod tls;

pub use self::error::Error;
pub use self::filter::{Filter};
Expand Down
141 changes: 134 additions & 7 deletions src/server.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use std::net::SocketAddr;
use std::sync::Arc;
#[cfg(feature = "tls")] use std::path::Path;

use futures::{Async, Future, Poll, Stream};
use hyper::{rt, Server as HyperServer};
use hyper::server::conn::AddrIncoming;
use hyper::service::{service_fn};
use tokio_io::{AsyncRead, AsyncWrite};

Expand All @@ -29,11 +31,20 @@ pub struct Server<S> {
service: S,
}

/// A Warp Server ready to filter requests over TLS.
///
/// *This type requires the `"tls"` feature.*
#[cfg(feature = "tls")]
pub struct TlsServer<S> {
server: Server<S>,
tls: ::rustls::ServerConfig,
}

// Getting all various generic bounds to make this a re-usable method is
// very complicated, so instead this is just a macro.
macro_rules! into_service {
($this:ident) => ({
let inner = Arc::new($this.service.into_warp_service());
($into:expr) => ({
let inner = Arc::new($into.into_warp_service());
move || {
let inner = inner.clone();
service_fn(move |req| {
Expand All @@ -44,17 +55,47 @@ macro_rules! into_service {
}
});
}

macro_rules! addr_incoming {
($addr:expr) => ({
let addr = $addr.into();
let mut incoming = AddrIncoming::bind(&addr)
.unwrap_or_else(|e| {
panic!("error binding to {}: {}", addr, e);
});
incoming.set_nodelay(true);
let addr = incoming.local_addr();
(addr, incoming)
});
}

macro_rules! bind_inner {
($this:ident, $addr:expr) => ({
let service = into_service!($this);
let srv = HyperServer::bind(&$addr.into())
let service = into_service!($this.service);
let (addr, incoming) = addr_incoming!($addr);
let srv = HyperServer::builder(incoming)
.http1_pipeline_flush($this.pipeline)
.serve(service);
let addr = srv.local_addr();
(addr, srv)
});

(tls: $this:ident, $addr:expr) => ({
let service = into_service!($this.server.service);
let (addr, incoming) = addr_incoming!($addr);
let tls = Arc::new($this.tls);
let incoming = incoming.map(move |sock| {
let session = ::rustls::ServerSession::new(&tls);
::tls::TlsStream::new(sock, session)
});
let srv = HyperServer::builder(incoming)
.http1_pipeline_flush($this.server.pipeline)
.serve(service);
(addr, srv)
});
}

// ===== impl Server =====

impl<S> Server<S>
where
S: IntoWarpService + 'static,
Expand All @@ -65,7 +106,7 @@ where
pub fn run(self, addr: impl Into<SocketAddr> + 'static) {
let (addr, fut) = self.bind_ephemeral(addr);

info!("warp drive engaged: listening on {}", addr);
info!("warp drive engaged: listening on http://{}", addr);

rt::run(fut);
}
Expand Down Expand Up @@ -160,7 +201,7 @@ where
I::Item: AsyncRead + AsyncWrite + Send + 'static,
I::Error: Into<Box<::std::error::Error + Send + Sync>>,
{
let service = into_service!(self);
let service = into_service!(self.service);
HyperServer::builder(incoming)
.http1_pipeline_flush(self.pipeline)
.serve(service)
Expand All @@ -175,8 +216,94 @@ where
self.pipeline = true;
self
}

/// Configure a server to use TLS with the supplied certificate and key files.
///
/// *This function requires the `"tls"` feature.*
#[cfg(feature = "tls")]
pub fn tls(self, cert: impl AsRef<Path>, key: impl AsRef<Path>) -> TlsServer<S> {
let tls = ::tls::configure(cert.as_ref(), key.as_ref());

TlsServer {
server: self,
tls,
}
}
}

// ===== impl TlsServer =====

#[cfg(feature = "tls")]
impl<S> TlsServer<S>
where
S: IntoWarpService + 'static,
<<S::Service as WarpService>::Reply as Future>::Item: Reply + Send,
<<S::Service as WarpService>::Reply as Future>::Error: Reject + Send,
{
/// Run this `TlsServer` forever on the current thread.
///
/// *This function requires the `"tls"` feature.*
pub fn run(self, addr: impl Into<SocketAddr> + 'static) {
let (addr, fut) = self.bind_ephemeral(addr);

info!("warp drive engaged: listening on https://{}", addr);

rt::run(fut);
}

/// Bind to a socket address, returning a `Future` that can be
/// executed on any runtime.
///
/// *This function requires the `"tls"` feature.*
pub fn bind(self, addr: impl Into<SocketAddr> + 'static) -> impl Future<Item=(), Error=()> + 'static {
let (_, fut) = self.bind_ephemeral(addr);
fut
}

/// Bind to a possibly ephemeral socket address.
///
/// Returns the bound address and a `Future` that can be executed on
/// any runtime.
///
/// *This function requires the `"tls"` feature.*
pub fn bind_ephemeral(self, addr: impl Into<SocketAddr> + 'static) -> (SocketAddr, impl Future<Item=(), Error=()> + 'static) {
let (addr, srv) = bind_inner!(tls: self, addr);
(addr, srv.map_err(|e| error!("server error: {}", e)))
}

/// Create a server with graceful shutdown signal.
///
/// When the signal completes, the server will start the graceful shutdown
/// process.
///
/// *This function requires the `"tls"` feature.*
pub fn bind_with_graceful_shutdown(
self,
addr: impl Into<SocketAddr> + 'static,
signal: impl Future<Item=()> + Send + 'static,
) -> (SocketAddr, impl Future<Item=(), Error=()> + 'static) {
let (addr, srv) = bind_inner!(tls: self, addr);
let fut = srv
.with_graceful_shutdown(signal)
.map_err(|e| error!("server error: {}", e));
(addr, fut)
}
}

#[cfg(feature = "tls")]
impl<S> ::std::fmt::Debug for TlsServer<S>
where
S: ::std::fmt::Debug,
{
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
f.debug_struct("TlsServer")
.field("server", &self.server)
.finish()
}
}

// ===== impl WarpService =====

pub trait IntoWarpService {
type Service: WarpService + Send + Sync + 'static;
fn into_warp_service(self) -> Self::Service;
Expand Down
Loading

0 comments on commit 1a3ad0c

Please sign in to comment.