Skip to content

Commit

Permalink
Merge branch 'master' into allow-empty-path!
Browse files Browse the repository at this point in the history
  • Loading branch information
utkarshkukreti authored Oct 3, 2020
2 parents 3f694ee + f426ec5 commit 86f3d35
Show file tree
Hide file tree
Showing 18 changed files with 177 additions and 25 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
### v0.2.5 (August 31, 2020)

- **Features**:
- Add `warp_fn`, which can be used to create a `Wrap` from a closure. These in turn are used with `Filter::with()`.
- Add `warp::host` filters to deal with `Host`/`:authority` headers.
- Relax some lifetime bounds on `Server`.
- **Fixes**:
- Fix panic when URI doesn't have a slash (for example, `CONNECT foo.bar`).

### v0.2.4 (July 20, 2020)

- **Features**:
Expand Down
10 changes: 5 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "warp"
version = "0.2.4" # don't forget to update html_root_url
version = "0.2.5" # don't forget to update html_root_url
description = "serve the web at warp speeds"
authors = ["Sean McArthur <[email protected]>"]
license = "MIT"
Expand Down Expand Up @@ -30,16 +30,16 @@ multipart = { version = "0.17", default-features = false, features = ["server"],
scoped-tls = "1.0"
serde = "1.0"
serde_json = "1.0"
serde_urlencoded = "0.6"
serde_urlencoded = "0.7"
tokio = { version = "0.2", features = ["fs", "stream", "sync", "time"] }
tracing = { version = "0.1", default-features = false, features = ["log", "std"] }
tracing-futures = { version = "0.2", default-features = false, features = ["std-future"] }
tower-service = "0.3"
# tls is enabled by default, we don't want that yet
tokio-tungstenite = { version = "0.10", default-features = false, optional = true }
urlencoding = "1.0.0"
tokio-tungstenite = { version = "0.11", default-features = false, optional = true }
percent-encoding = "2.1"
pin-project = "0.4.17"
tokio-rustls = { version = "0.13.1", optional = true }
tokio-rustls = { version = "0.14", optional = true }

[dev-dependencies]
pretty_env_logger = "0.4"
Expand Down
31 changes: 31 additions & 0 deletions examples/wrapping.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#![deny(warnings)]
use warp::Filter;

fn hello_wrapper<F, T>(
filter: F,
) -> impl Filter<Extract = (&'static str,)> + Clone + Send + Sync + 'static
where
F: Filter<Extract = (T,), Error = std::convert::Infallible> + Clone + Send + Sync + 'static,
F::Extract: warp::Reply,
{
warp::any()
.map(|| {
println!("before filter");
})
.untuple_one()
.and(filter)
.map(|_arg| "wrapped hello world")
}

#[tokio::main]
async fn main() {
// Match any request and return hello world!
let routes = warp::any()
.map(|| "hello world")
.boxed()
.recover(|_err| async { Ok("recovered") })
// wrap the filter with hello_wrapper
.with(warp::wrap_fn(hello_wrapper));

warp::serve(routes).run(([127, 0, 0, 1], 3030)).await;
}
1 change: 1 addition & 0 deletions src/filter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ use self::or_else::OrElse;
use self::recover::Recover;
use self::unify::Unify;
use self::untuple_one::UntupleOne;
pub use self::wrap::wrap_fn;
pub(crate) use self::wrap::{Wrap, WrapSealed};

// A crate-private base trait, allowing the actual `filter` method to change
Expand Down
28 changes: 28 additions & 0 deletions src/filter/wrap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,31 @@ where
F: Filter,
{
}

/// Function that receives a filter to be combined with pre and after filters
pub fn wrap_fn<F, T, U>(func: F) -> WrapFn<F>
where
F: Fn(T) -> U,
T: Filter,
U: Filter,
{
WrapFn { func }
}

#[derive(Debug)]
pub struct WrapFn<F> {
func: F,
}

impl<F, T, U> WrapSealed<T> for WrapFn<F>
where
F: Fn(T) -> U,
T: Filter,
U: Filter,
{
type Wrapped = U;

fn wrap(&self, filter: T) -> Self::Wrapped {
(self.func)(filter)
}
}
27 changes: 20 additions & 7 deletions src/filters/cookie.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,20 @@ use super::header;
use crate::filter::{Filter, One};
use crate::reject::Rejection;
use std::convert::Infallible;
use std::str::FromStr;

/// Creates a `Filter` that requires a cookie by name.
///
/// If found, extracts the value of the cookie, otherwise rejects.
pub fn cookie(name: &'static str) -> impl Filter<Extract = One<String>, Error = Rejection> + Copy {
pub fn cookie<T>(name: &'static str) -> impl Filter<Extract = One<T>, Error = Rejection> + Copy
where
T: FromStr + Send + 'static,
{
header::header2().and_then(move |cookie: Cookie| {
let cookie = cookie
.get(name)
.map(String::from)
.ok_or_else(|| crate::reject::missing_cookie(name));
.ok_or_else(|| crate::reject::missing_cookie(name))
.and_then(|s| T::from_str(s).map_err(|_| crate::reject::missing_cookie(name)));
future::ready(cookie)
})
}
Expand All @@ -25,9 +29,18 @@ pub fn cookie(name: &'static str) -> impl Filter<Extract = One<String>, Error =
///
/// If found, extracts the value of the cookie, otherwise continues
/// the request, extracting `None`.
pub fn optional(
pub fn optional<T>(
name: &'static str,
) -> impl Filter<Extract = One<Option<String>>, Error = Infallible> + Copy {
header::optional2()
.map(move |opt: Option<Cookie>| opt.and_then(|cookie| cookie.get(name).map(String::from)))
) -> impl Filter<Extract = One<Option<T>>, Error = Infallible> + Copy
where
T: FromStr + Send + 'static,
{
header::optional2().map(move |opt: Option<Cookie>| {
let cookie = opt.and_then(|cookie| cookie.get(name).map(|x| T::from_str(x)));
match cookie {
Some(Ok(t)) => Some(t),
Some(Err(_)) => None,
None => None,
}
})
}
4 changes: 4 additions & 0 deletions src/filters/cors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ impl Builder {

/// Adds a header to the list of allowed request headers.
///
/// **Note**: These should match the values the browser sends via `Access-Control-Request-Headers`, e.g. `content-type`.
///
/// # Panics
///
/// Panics if the provided argument is not a valid `http::header::HeaderName`.
Expand All @@ -133,6 +135,8 @@ impl Builder {

/// Adds multiple headers to the list of allowed request headers.
///
/// **Note**: These should match the values the browser sends via `Access-Control-Request-Headers`, e.g.`content-type`.
///
/// # Panics
///
/// Panics if any of the headers are not a valid `http::header::HeaderName`.
Expand Down
5 changes: 2 additions & 3 deletions src/filters/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ use headers::{
use http::StatusCode;
use hyper::Body;
use mime_guess;
use percent_encoding::percent_decode_str;
use tokio::fs::File as TkFile;
use tokio::io::AsyncRead;
use urlencoding::decode;

use crate::filter::{Filter, FilterClone, One};
use crate::reject::{self, Rejection};
Expand Down Expand Up @@ -107,11 +107,10 @@ fn path_from_tail(

fn sanitize_path(base: impl AsRef<Path>, tail: &str) -> Result<PathBuf, Rejection> {
let mut buf = PathBuf::from(base.as_ref());
let p = match decode(tail) {
let p = match percent_decode_str(tail).decode_utf8() {
Ok(p) => p,
Err(err) => {
tracing::debug!("dir: failed to decode route={:?}: {:?}", tail, err);
// FromUrlEncodingError doesn't implement StdError
return Err(reject::not_found());
}
};
Expand Down
3 changes: 2 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#![doc(html_root_url = "https://docs.rs/warp/0.2.4")]
#![doc(html_root_url = "https://docs.rs/warp/0.2.5")]
#![deny(missing_docs)]
#![deny(missing_debug_implementations)]
#![cfg_attr(test, deny(warnings))]
Expand Down Expand Up @@ -149,6 +149,7 @@ pub use self::filters::{
trace::trace,
};
// ws() function
pub use self::filter::wrap_fn;
#[cfg(feature = "websocket")]
#[doc(hidden)]
pub use self::filters::ws::ws;
Expand Down
9 changes: 9 additions & 0 deletions src/reject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ fn __reject_custom_compilefail() {}

/// A marker trait to ensure proper types are used for custom rejections.
///
/// Can be converted into Rejection.
///
/// # Example
///
/// ```
Expand Down Expand Up @@ -317,6 +319,13 @@ impl Rejection {
}
}

impl<T: Reject> From<T> for Rejection {
#[inline]
fn from(err: T) -> Rejection {
custom(err)
}
}

impl From<Infallible> for Rejection {
#[inline]
fn from(infallible: Infallible) -> Rejection {
Expand Down
8 changes: 4 additions & 4 deletions src/route.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,14 @@ impl Route {

pub(crate) fn set_unmatched_path(&mut self, index: usize) {
let index = self.segments_index + index;

let path = self.req.uri().path();

if path.len() == index {
if path.is_empty() {
// malformed path
return;
} else if path.len() == index {
self.segments_index = index;
} else {
debug_assert_eq!(path.as_bytes()[index], b'/');

self.segments_index = index + 1;
}
}
Expand Down
5 changes: 5 additions & 0 deletions src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,11 @@ where
self.with_tls(|tls| tls.cert(cert.as_ref()))
}

/// Specify the DER-encoded OCSP response.
pub fn ocsp_resp(self, resp: impl AsRef<[u8]>) -> Self {
self.with_tls(|tls| tls.ocsp_resp(resp.as_ref()))
}

fn with_tls<Func>(self, func: Func) -> Self
where
Func: FnOnce(TlsConfigBuilder) -> TlsConfigBuilder,
Expand Down
7 changes: 6 additions & 1 deletion src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,12 @@ impl WsBuilder {
.header("sec-websocket-key", "dGhlIHNhbXBsZSBub25jZQ==")
.req;

let uri = format!("http://{}{}", addr, req.uri().path())
let query_string = match req.uri().query() {
Some(q) => format!("?{}", q),
None => String::from(""),
};

let uri = format!("http://{}{}{}", addr, req.uri().path(), query_string)
.parse()
.expect("addr + path is valid URI");

Expand Down
10 changes: 9 additions & 1 deletion src/tls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ impl std::error::Error for TlsConfigError {}
pub(crate) struct TlsConfigBuilder {
cert: Box<dyn Read + Send + Sync>,
key: Box<dyn Read + Send + Sync>,
ocsp_resp: Vec<u8>,
}

impl std::fmt::Debug for TlsConfigBuilder {
Expand All @@ -64,6 +65,7 @@ impl TlsConfigBuilder {
TlsConfigBuilder {
key: Box::new(io::empty()),
cert: Box::new(io::empty()),
ocsp_resp: Vec::new(),
}
}

Expand Down Expand Up @@ -97,6 +99,12 @@ impl TlsConfigBuilder {
self
}

/// sets the DER-encoded OCSP response
pub(crate) fn ocsp_resp(mut self, ocsp_resp: &[u8]) -> Self {
self.ocsp_resp = Vec::from(ocsp_resp);
self
}

pub(crate) fn build(mut self) -> Result<ServerConfig, TlsConfigError> {
let mut cert_rdr = BufReader::new(self.cert);
let cert = tokio_rustls::rustls::internal::pemfile::certs(&mut cert_rdr)
Expand Down Expand Up @@ -136,7 +144,7 @@ impl TlsConfigBuilder {

let mut config = ServerConfig::new(NoClientAuth::new());
config
.set_single_cert(cert, key)
.set_single_cert_with_ocsp_and_sct(cert, key, self.ocsp_resp, Vec::new())
.map_err(|err| TlsConfigError::InvalidKey(err))?;
config.set_protocols(&["h2".into(), "http/1.1".into()]);
Ok(config)
Expand Down
6 changes: 3 additions & 3 deletions tests/cookie.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

#[tokio::test]
async fn cookie() {
let foo = warp::cookie("foo");
let foo = warp::cookie::<String>("foo");

let req = warp::test::request().header("cookie", "foo=bar");
assert_eq!(req.filter(&foo).await.unwrap(), "bar");
Expand All @@ -19,7 +19,7 @@ async fn cookie() {

#[tokio::test]
async fn optional() {
let foo = warp::cookie::optional("foo");
let foo = warp::cookie::optional::<String>("foo");

let req = warp::test::request().header("cookie", "foo=bar");
assert_eq!(req.filter(&foo).await.unwrap().unwrap(), "bar");
Expand All @@ -38,7 +38,7 @@ async fn optional() {
async fn missing() {
let _ = pretty_env_logger::try_init();

let cookie = warp::cookie("foo");
let cookie = warp::cookie::<String>("foo");

let res = warp::test::request()
.header("cookie", "not=here")
Expand Down
3 changes: 3 additions & 0 deletions tests/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ async fn dir() {
assert_eq!(res.headers()["accept-ranges"], "bytes");

assert_eq!(res.body(), &*contents);

let malformed_req = warp::test::request().path("todos.rs");
assert_eq!(malformed_req.reply(&file).await.status(), 404);
}

#[tokio::test]
Expand Down
7 changes: 7 additions & 0 deletions tests/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,13 @@ async fn tail() {

let m = tail.and(warp::path::end());
assert!(warp::test::request().path("/foo/bar").matches(&m).await);

let ex = warp::test::request()
.path("localhost")
.filter(&tail)
.await
.unwrap();
assert_eq!(ex.as_str(), "/");
}

#[tokio::test]
Expand Down
Loading

0 comments on commit 86f3d35

Please sign in to comment.