Skip to content

Commit

Permalink
Merge branch 'master' into local-socketaddr
Browse files Browse the repository at this point in the history
  • Loading branch information
daviessm committed Sep 3, 2023
2 parents a6cf59a + 5ad8a9c commit 6f8de48
Show file tree
Hide file tree
Showing 24 changed files with 266 additions and 76 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
### v0.3.5 (April 28, 2023)

- **Fixes**:
- `multipart` filters now use `multer` dependency, fixing some streaming bugs.
- `Rejection::into_response()` is significantly faster.

### v0.3.4 (March 31, 2023)

- **Fixes**:
Expand Down
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "warp"
version = "0.3.4" # don't forget to update html_root_url
version = "0.3.5" # don't forget to update html_root_url
description = "serve the web at warp speeds"
authors = ["Sean McArthur <sean@seanmonstar.com>"]
license = "MIT"
Expand Down Expand Up @@ -40,11 +40,11 @@ tower-service = "0.3"
tokio-tungstenite = { version = "0.18", optional = true }
percent-encoding = "2.1"
pin-project = "1.0"
tokio-rustls = { version = "0.23", optional = true }
tokio-rustls = { version = "0.24", optional = true }
rustls-pemfile = "1.0"

[dev-dependencies]
pretty_env_logger = "0.4"
pretty_env_logger = "0.5"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
tracing-log = "0.1"
serde_derive = "1.0"
Expand Down
14 changes: 9 additions & 5 deletions examples/multipart.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use bytes::BufMut;
use futures_util::TryStreamExt;
use warp::multipart::FormData;
use warp::Buf;
use warp::Filter;

#[tokio::main]
Expand All @@ -9,13 +9,17 @@ async fn main() {
let route = warp::multipart::form().and_then(|form: FormData| async move {
let field_names: Vec<_> = form
.and_then(|mut field| async move {
let contents =
String::from_utf8_lossy(field.data().await.unwrap().unwrap().chunk())
.to_string();
let mut bytes: Vec<u8> = Vec::new();

// field.data() only returns a piece of the content, you should call over it until it replies None
while let Some(content) = field.data().await {
let content = content.unwrap();
bytes.put(content);
}
Ok((
field.name().to_string(),
field.filename().unwrap().to_string(),
contents,
String::from_utf8_lossy(&*bytes).to_string(),
))
})
.try_collect()
Expand Down
30 changes: 30 additions & 0 deletions examples/stream.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use bytes::Buf;
use futures_util::{Stream, StreamExt};
use warp::{reply::Response, Filter, Reply};

#[tokio::main]
async fn main() {
// Running curl -T /path/to/a/file 'localhost:3030/' should echo back the content of the file,
// or an HTTP 413 error if the configured size limit is exceeded.
let route = warp::body::content_length_limit(65536)
.and(warp::body::stream())
.then(handler);
warp::serve(route).run(([127, 0, 0, 1], 3030)).await;
}

async fn handler(
mut body: impl Stream<Item = Result<impl Buf, warp::Error>> + Unpin + Send + Sync,
) -> Response {
let mut collected: Vec<u8> = vec![];
while let Some(buf) = body.next().await {
let mut buf = buf.unwrap();
while buf.remaining() > 0 {
let chunk = buf.chunk();
let chunk_len = chunk.len();
collected.extend_from_slice(chunk);
buf.advance(chunk_len);
}
}
println!("Sending {} bytes", collected.len());
collected.into_response()
}
2 changes: 1 addition & 1 deletion src/filter/boxed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use futures_util::TryFutureExt;
use super::{Filter, FilterBase, Internal, Tuple};
use crate::reject::Rejection;

/// A type representing a boxed `Filter` trait object.
/// A type representing a boxed [`Filter`](crate::Filter) trait object.
///
/// The filter inside is a dynamic trait object. The purpose of this type is
/// to ease returning `Filter`s from other functions.
Expand Down
4 changes: 2 additions & 2 deletions src/filters/any.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ use std::task::{Context, Poll};

use crate::filter::{Filter, FilterBase, Internal};

/// A filter that matches any route.
/// A [`Filter`](crate::Filter) that matches any route.
///
/// This can be a useful building block to build new filters from,
/// since [`Filter`](crate::Filter) is otherwise a sealed trait.
/// since [`Filter`] is otherwise a sealed trait.
///
/// # Example
///
Expand Down
2 changes: 2 additions & 0 deletions src/filters/body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ pub fn content_length_limit(limit: u64) -> impl Filter<Extract = (), Error = Rej
/// If other filters have already extracted the body, this filter will reject
/// with a `500 Internal Server Error`.
///
/// For example usage, please take a look at [examples/stream.rs](https://github.com/seanmonstar/warp/blob/master/examples/stream.rs).
///
/// # Warning
///
/// This does not have a default size limit, it would be wise to use one to
Expand Down
4 changes: 2 additions & 2 deletions src/filters/cors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use crate::reply::Reply;

use self::internal::{CorsFilter, IntoOrigin, Seconds};

/// Create a wrapping filter that exposes [CORS][] behavior for a wrapped
/// Create a wrapping [`Filter`](crate::Filter) that exposes [CORS][] behavior for a wrapped
/// filter.
///
/// [CORS]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
Expand Down Expand Up @@ -56,7 +56,7 @@ pub fn cors() -> Builder {
}
}

/// A wrapping filter constructed via `warp::cors()`.
/// A wrapping [`Filter`](crate::Filter) constructed via `warp::cors()`.
#[derive(Clone, Debug)]
pub struct Cors {
config: Arc<Configured>,
Expand Down
2 changes: 1 addition & 1 deletion src/filters/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ use crate::reply::{Reply, Response};
/// filters, such as after validating in `POST` request, wanting to return a
/// specific file as the body.
///
/// For serving a directory, see [dir](dir).
/// For serving a directory, see [dir].
///
/// # Example
///
Expand Down
6 changes: 3 additions & 3 deletions src/filters/log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use crate::route::Route;

use self::internal::WithLog;

/// Create a wrapping filter with the specified `name` as the `target`.
/// Create a wrapping [`Filter`](crate::Filter) with the specified `name` as the `target`.
///
/// This uses the default access logging format, and log records produced
/// will have their `target` set to `name`.
Expand Down Expand Up @@ -50,7 +50,7 @@ pub fn log(name: &'static str) -> Log<impl Fn(Info<'_>) + Copy> {
Log { func }
}

/// Create a wrapping filter that receives `warp::log::Info`.
/// Create a wrapping [`Filter`](crate::Filter) that receives `warp::log::Info`.
///
/// # Example
///
Expand All @@ -77,7 +77,7 @@ where
Log { func }
}

/// Decorates a [`Filter`](crate::Filter) to log requests and responses.
/// Decorates a [`Filter`] to log requests and responses.
#[derive(Clone, Copy, Debug)]
pub struct Log<F> {
func: F,
Expand Down
32 changes: 19 additions & 13 deletions src/filters/multipart.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Multipart body filters
//!
//! Filters that extract a multipart body for a route.
//! [`Filter`](crate::Filter)s that extract a multipart body for a route.

use std::error::Error as StdError;
use std::fmt::{Display, Formatter};
Expand All @@ -22,12 +22,12 @@ use crate::reject::{self, Rejection};
// If not otherwise configured, default to 2MB.
const DEFAULT_FORM_DATA_MAX_LENGTH: u64 = 1024 * 1024 * 2;

/// A `Filter` to extract a `multipart/form-data` body from a request.
/// A [`Filter`](crate::Filter) to extract a `multipart/form-data` body from a request.
///
/// Create with the `warp::multipart::form()` function.
#[derive(Debug, Clone)]
pub struct FormOptions {
max_length: u64,
max_length: Option<u64>,
}

/// A `Stream` of multipart/form-data `Part`s.
Expand All @@ -44,13 +44,13 @@ pub struct Part {
part: PartInner<'static>,
}

/// Create a `Filter` to extract a `multipart/form-data` body from a request.
/// Create a [`Filter`](crate::Filter) to extract a `multipart/form-data` body from a request.
///
/// The extracted `FormData` type is a `Stream` of `Part`s, and each `Part`
/// in turn is a `Stream` of bytes.
pub fn form() -> FormOptions {
FormOptions {
max_length: DEFAULT_FORM_DATA_MAX_LENGTH,
max_length: Some(DEFAULT_FORM_DATA_MAX_LENGTH),
}
}

Expand All @@ -59,9 +59,10 @@ pub fn form() -> FormOptions {
impl FormOptions {
/// Set the maximum byte length allowed for this body.
///
/// `max_length(None)` means that maximum byte length is not checked.
/// Defaults to 2MB.
pub fn max_length(mut self, max: u64) -> Self {
self.max_length = max;
pub fn max_length(mut self, max: impl Into<Option<u64>>) -> Self {
self.max_length = max.into();
self
}
}
Expand All @@ -83,8 +84,7 @@ impl FilterBase for FormOptions {
future::ready(mime)
});

let filt = super::body::content_length_limit(self.max_length)
.and(boundary)
let filt = boundary
.and(super::body::body())
.map(|boundary: String, body| {
let body = BodyIoError(body);
Expand All @@ -93,9 +93,15 @@ impl FilterBase for FormOptions {
}
});

let fut = filt.filter(Internal);

Box::pin(fut)
if let Some(max_length) = self.max_length {
Box::pin(
super::body::content_length_limit(max_length)
.and(filt)
.filter(Internal),
)
} else {
Box::pin(filt.filter(Internal))
}
}
}

Expand Down Expand Up @@ -142,7 +148,7 @@ impl Part {
/// Get the content-type of this part, if present.
pub fn content_type(&self) -> Option<&str> {
let content_type = self.part.content_type();
content_type.map(|t| t.type_().as_str())
content_type.map(|t| t.as_ref())
}

/// Asynchronously get some of the data for this `Part`.
Expand Down
6 changes: 3 additions & 3 deletions src/filters/path.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Path Filters
//!
//! The filters here work on the "path" of requests.
//! The [`Filter`](crate::Filter)s here work on the "path" of requests.
//!
//! - [`path`](./fn.path.html) matches a specific segment, like `/foo`.
//! - [`param`](./fn.param.html) tries to parse a segment into a type, like `/:u16`.
Expand Down Expand Up @@ -137,7 +137,7 @@ use crate::filter::{filter_fn, one, Filter, FilterBase, Internal, One, Tuple};
use crate::reject::{self, Rejection};
use crate::route::{self, Route};

/// Create an exact match path segment `Filter`.
/// Create an exact match path segment [`Filter`](crate::Filter).
///
/// This will try to match exactly to the current request path segment.
///
Expand Down Expand Up @@ -189,7 +189,7 @@ where
*/
}

/// A `Filter` matching an exact path segment.
/// A [`Filter`](crate::Filter) matching an exact path segment.
///
/// Constructed from `path()` or `path!()`.
#[allow(missing_debug_implementations)]
Expand Down
12 changes: 6 additions & 6 deletions src/filters/reply.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ use self::sealed::{WithDefaultHeader_, WithHeader_, WithHeaders_};
use crate::filter::{Filter, Map, WrapSealed};
use crate::reply::Reply;

/// Wrap a [`Filter`](crate::Filter) that adds a header to the reply.
/// Wrap a [`Filter`] that adds a header to the reply.
///
/// # Note
///
/// This **only** adds a header if the underlying filter is successful, and
/// returns a [`Reply`](Reply). If the underlying filter was rejected, the
/// returns a [`Reply`] If the underlying filter was rejected, the
/// header is not added.
///
/// # Example
Expand All @@ -57,12 +57,12 @@ where
WithHeader { name, value }
}

/// Wrap a [`Filter`](crate::Filter) that adds multiple headers to the reply.
/// Wrap a [`Filter`] that adds multiple headers to the reply.
///
/// # Note
///
/// This **only** adds a header if the underlying filter is successful, and
/// returns a [`Reply`](Reply). If the underlying filter was rejected, the
/// returns a [`Reply`] If the underlying filter was rejected, the
/// header is not added.
///
/// # Example
Expand All @@ -88,13 +88,13 @@ pub fn headers(headers: HeaderMap) -> WithHeaders {

// pub fn headers?

/// Wrap a [`Filter`](crate::Filter) that adds a header to the reply, if they
/// Wrap a [`Filter`] that adds a header to the reply, if they
/// aren't already set.
///
/// # Note
///
/// This **only** adds a header if the underlying filter is successful, and
/// returns a [`Reply`](Reply). If the underlying filter was rejected, the
/// returns a [`Reply`] If the underlying filter was rejected, the
/// header is not added.
///
/// # Example
Expand Down
4 changes: 3 additions & 1 deletion src/filters/sse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
//! which specifies the expected behavior of Server Sent Events.
//!

#![allow(rustdoc::invalid_html_tags)]

use serde::Serialize;
use std::borrow::Cow;
use std::error::Error as StdError;
Expand Down Expand Up @@ -376,7 +378,7 @@ impl KeepAlive {

/// Wrap an event stream with keep-alive functionality.
///
/// See [`keep_alive`](keep_alive) for more.
/// See [`keep_alive`] for more.
pub fn stream<S>(
self,
event_stream: S,
Expand Down
2 changes: 1 addition & 1 deletion src/filters/trace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ pub fn named(name: &'static str) -> Trace<impl Fn(Info<'_>) -> Span + Copy> {
trace(move |_| tracing::debug_span!("context", "{}", name,))
}

/// Decorates a [`Filter`](crate::Filter) to create a [`tracing`] [span] for
/// Decorates a [`Filter`] to create a [`tracing`] [span] for
/// requests and responses.
///
/// [`tracing`]: https://crates.io/crates/tracing
Expand Down
2 changes: 1 addition & 1 deletion src/filters/ws.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ pub fn ws() -> impl Filter<Extract = One<Ws>, Error = Rejection> + Copy {
)
}

/// Extracted by the [`ws`](ws) filter, and used to finish an upgrade.
/// Extracted by the [`ws`] filter, and used to finish an upgrade.
pub struct Ws {
config: Option<WebSocketConfig>,
key: SecWebsocketKey,
Expand Down
4 changes: 2 additions & 2 deletions 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.3.4")]
#![doc(html_root_url = "https://docs.rs/warp/0.3.5")]
#![deny(missing_docs)]
#![deny(missing_debug_implementations)]
#![deny(rust_2018_idioms)]
Expand Down Expand Up @@ -81,7 +81,7 @@
//! ## Testing
//!
//! Testing your web services easily is extremely important, and warp provides
//! a [`test`](self::test) module to help send mocked requests through your service.
//! a [`test`](mod@self::test) module to help send mocked requests through your service.
//!
//! [Filter]: trait.Filter.html
//! [reject]: reject/index.html
Expand Down

0 comments on commit 6f8de48

Please sign in to comment.