Skip to content

Commit 6f8de48

Browse files
authored
Merge branch 'master' into local-socketaddr
2 parents a6cf59a + 5ad8a9c commit 6f8de48

24 files changed

+266
-76
lines changed

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
### v0.3.5 (April 28, 2023)
2+
3+
- **Fixes**:
4+
- `multipart` filters now use `multer` dependency, fixing some streaming bugs.
5+
- `Rejection::into_response()` is significantly faster.
6+
17
### v0.3.4 (March 31, 2023)
28

39
- **Fixes**:

Cargo.toml

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "warp"
3-
version = "0.3.4" # don't forget to update html_root_url
3+
version = "0.3.5" # don't forget to update html_root_url
44
description = "serve the web at warp speeds"
55
authors = ["Sean McArthur <[email protected]>"]
66
license = "MIT"
@@ -40,11 +40,11 @@ tower-service = "0.3"
4040
tokio-tungstenite = { version = "0.18", optional = true }
4141
percent-encoding = "2.1"
4242
pin-project = "1.0"
43-
tokio-rustls = { version = "0.23", optional = true }
43+
tokio-rustls = { version = "0.24", optional = true }
4444
rustls-pemfile = "1.0"
4545

4646
[dev-dependencies]
47-
pretty_env_logger = "0.4"
47+
pretty_env_logger = "0.5"
4848
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
4949
tracing-log = "0.1"
5050
serde_derive = "1.0"

examples/multipart.rs

+9-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1+
use bytes::BufMut;
12
use futures_util::TryStreamExt;
23
use warp::multipart::FormData;
3-
use warp::Buf;
44
use warp::Filter;
55

66
#[tokio::main]
@@ -9,13 +9,17 @@ async fn main() {
99
let route = warp::multipart::form().and_then(|form: FormData| async move {
1010
let field_names: Vec<_> = form
1111
.and_then(|mut field| async move {
12-
let contents =
13-
String::from_utf8_lossy(field.data().await.unwrap().unwrap().chunk())
14-
.to_string();
12+
let mut bytes: Vec<u8> = Vec::new();
13+
14+
// field.data() only returns a piece of the content, you should call over it until it replies None
15+
while let Some(content) = field.data().await {
16+
let content = content.unwrap();
17+
bytes.put(content);
18+
}
1519
Ok((
1620
field.name().to_string(),
1721
field.filename().unwrap().to_string(),
18-
contents,
22+
String::from_utf8_lossy(&*bytes).to_string(),
1923
))
2024
})
2125
.try_collect()

examples/stream.rs

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
use bytes::Buf;
2+
use futures_util::{Stream, StreamExt};
3+
use warp::{reply::Response, Filter, Reply};
4+
5+
#[tokio::main]
6+
async fn main() {
7+
// Running curl -T /path/to/a/file 'localhost:3030/' should echo back the content of the file,
8+
// or an HTTP 413 error if the configured size limit is exceeded.
9+
let route = warp::body::content_length_limit(65536)
10+
.and(warp::body::stream())
11+
.then(handler);
12+
warp::serve(route).run(([127, 0, 0, 1], 3030)).await;
13+
}
14+
15+
async fn handler(
16+
mut body: impl Stream<Item = Result<impl Buf, warp::Error>> + Unpin + Send + Sync,
17+
) -> Response {
18+
let mut collected: Vec<u8> = vec![];
19+
while let Some(buf) = body.next().await {
20+
let mut buf = buf.unwrap();
21+
while buf.remaining() > 0 {
22+
let chunk = buf.chunk();
23+
let chunk_len = chunk.len();
24+
collected.extend_from_slice(chunk);
25+
buf.advance(chunk_len);
26+
}
27+
}
28+
println!("Sending {} bytes", collected.len());
29+
collected.into_response()
30+
}

src/filter/boxed.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use futures_util::TryFutureExt;
88
use super::{Filter, FilterBase, Internal, Tuple};
99
use crate::reject::Rejection;
1010

11-
/// A type representing a boxed `Filter` trait object.
11+
/// A type representing a boxed [`Filter`](crate::Filter) trait object.
1212
///
1313
/// The filter inside is a dynamic trait object. The purpose of this type is
1414
/// to ease returning `Filter`s from other functions.

src/filters/any.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ use std::task::{Context, Poll};
66

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

9-
/// A filter that matches any route.
9+
/// A [`Filter`](crate::Filter) that matches any route.
1010
///
1111
/// This can be a useful building block to build new filters from,
12-
/// since [`Filter`](crate::Filter) is otherwise a sealed trait.
12+
/// since [`Filter`] is otherwise a sealed trait.
1313
///
1414
/// # Example
1515
///

src/filters/body.rs

+2
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ pub fn content_length_limit(limit: u64) -> impl Filter<Extract = (), Error = Rej
7070
/// If other filters have already extracted the body, this filter will reject
7171
/// with a `500 Internal Server Error`.
7272
///
73+
/// For example usage, please take a look at [examples/stream.rs](https://github.com/seanmonstar/warp/blob/master/examples/stream.rs).
74+
///
7375
/// # Warning
7476
///
7577
/// This does not have a default size limit, it would be wise to use one to

src/filters/cors.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use crate::reply::Reply;
2020

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

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

59-
/// A wrapping filter constructed via `warp::cors()`.
59+
/// A wrapping [`Filter`](crate::Filter) constructed via `warp::cors()`.
6060
#[derive(Clone, Debug)]
6161
pub struct Cors {
6262
config: Arc<Configured>,

src/filters/fs.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ use crate::reply::{Reply, Response};
3737
/// filters, such as after validating in `POST` request, wanting to return a
3838
/// specific file as the body.
3939
///
40-
/// For serving a directory, see [dir](dir).
40+
/// For serving a directory, see [dir].
4141
///
4242
/// # Example
4343
///

src/filters/log.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use crate::route::Route;
1313

1414
use self::internal::WithLog;
1515

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

53-
/// Create a wrapping filter that receives `warp::log::Info`.
53+
/// Create a wrapping [`Filter`](crate::Filter) that receives `warp::log::Info`.
5454
///
5555
/// # Example
5656
///
@@ -77,7 +77,7 @@ where
7777
Log { func }
7878
}
7979

80-
/// Decorates a [`Filter`](crate::Filter) to log requests and responses.
80+
/// Decorates a [`Filter`] to log requests and responses.
8181
#[derive(Clone, Copy, Debug)]
8282
pub struct Log<F> {
8383
func: F,

src/filters/multipart.rs

+19-13
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Multipart body filters
22
//!
3-
//! Filters that extract a multipart body for a route.
3+
//! [`Filter`](crate::Filter)s that extract a multipart body for a route.
44
55
use std::error::Error as StdError;
66
use std::fmt::{Display, Formatter};
@@ -22,12 +22,12 @@ use crate::reject::{self, Rejection};
2222
// If not otherwise configured, default to 2MB.
2323
const DEFAULT_FORM_DATA_MAX_LENGTH: u64 = 1024 * 1024 * 2;
2424

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

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

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

@@ -59,9 +59,10 @@ pub fn form() -> FormOptions {
5959
impl FormOptions {
6060
/// Set the maximum byte length allowed for this body.
6161
///
62+
/// `max_length(None)` means that maximum byte length is not checked.
6263
/// Defaults to 2MB.
63-
pub fn max_length(mut self, max: u64) -> Self {
64-
self.max_length = max;
64+
pub fn max_length(mut self, max: impl Into<Option<u64>>) -> Self {
65+
self.max_length = max.into();
6566
self
6667
}
6768
}
@@ -83,8 +84,7 @@ impl FilterBase for FormOptions {
8384
future::ready(mime)
8485
});
8586

86-
let filt = super::body::content_length_limit(self.max_length)
87-
.and(boundary)
87+
let filt = boundary
8888
.and(super::body::body())
8989
.map(|boundary: String, body| {
9090
let body = BodyIoError(body);
@@ -93,9 +93,15 @@ impl FilterBase for FormOptions {
9393
}
9494
});
9595

96-
let fut = filt.filter(Internal);
97-
98-
Box::pin(fut)
96+
if let Some(max_length) = self.max_length {
97+
Box::pin(
98+
super::body::content_length_limit(max_length)
99+
.and(filt)
100+
.filter(Internal),
101+
)
102+
} else {
103+
Box::pin(filt.filter(Internal))
104+
}
99105
}
100106
}
101107

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

148154
/// Asynchronously get some of the data for this `Part`.

src/filters/path.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Path Filters
22
//!
3-
//! The filters here work on the "path" of requests.
3+
//! The [`Filter`](crate::Filter)s here work on the "path" of requests.
44
//!
55
//! - [`path`](./fn.path.html) matches a specific segment, like `/foo`.
66
//! - [`param`](./fn.param.html) tries to parse a segment into a type, like `/:u16`.
@@ -137,7 +137,7 @@ use crate::filter::{filter_fn, one, Filter, FilterBase, Internal, One, Tuple};
137137
use crate::reject::{self, Rejection};
138138
use crate::route::{self, Route};
139139

140-
/// Create an exact match path segment `Filter`.
140+
/// Create an exact match path segment [`Filter`](crate::Filter).
141141
///
142142
/// This will try to match exactly to the current request path segment.
143143
///
@@ -189,7 +189,7 @@ where
189189
*/
190190
}
191191

192-
/// A `Filter` matching an exact path segment.
192+
/// A [`Filter`](crate::Filter) matching an exact path segment.
193193
///
194194
/// Constructed from `path()` or `path!()`.
195195
#[allow(missing_debug_implementations)]

src/filters/reply.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,12 @@ use self::sealed::{WithDefaultHeader_, WithHeader_, WithHeaders_};
2828
use crate::filter::{Filter, Map, WrapSealed};
2929
use crate::reply::Reply;
3030

31-
/// Wrap a [`Filter`](crate::Filter) that adds a header to the reply.
31+
/// Wrap a [`Filter`] that adds a header to the reply.
3232
///
3333
/// # Note
3434
///
3535
/// This **only** adds a header if the underlying filter is successful, and
36-
/// returns a [`Reply`](Reply). If the underlying filter was rejected, the
36+
/// returns a [`Reply`] If the underlying filter was rejected, the
3737
/// header is not added.
3838
///
3939
/// # Example
@@ -57,12 +57,12 @@ where
5757
WithHeader { name, value }
5858
}
5959

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

8989
// pub fn headers?
9090

91-
/// Wrap a [`Filter`](crate::Filter) that adds a header to the reply, if they
91+
/// Wrap a [`Filter`] that adds a header to the reply, if they
9292
/// aren't already set.
9393
///
9494
/// # Note
9595
///
9696
/// This **only** adds a header if the underlying filter is successful, and
97-
/// returns a [`Reply`](Reply). If the underlying filter was rejected, the
97+
/// returns a [`Reply`] If the underlying filter was rejected, the
9898
/// header is not added.
9999
///
100100
/// # Example

src/filters/sse.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
//! which specifies the expected behavior of Server Sent Events.
4040
//!
4141
42+
#![allow(rustdoc::invalid_html_tags)]
43+
4244
use serde::Serialize;
4345
use std::borrow::Cow;
4446
use std::error::Error as StdError;
@@ -376,7 +378,7 @@ impl KeepAlive {
376378

377379
/// Wrap an event stream with keep-alive functionality.
378380
///
379-
/// See [`keep_alive`](keep_alive) for more.
381+
/// See [`keep_alive`] for more.
380382
pub fn stream<S>(
381383
self,
382384
event_stream: S,

src/filters/trace.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ pub fn named(name: &'static str) -> Trace<impl Fn(Info<'_>) -> Span + Copy> {
123123
trace(move |_| tracing::debug_span!("context", "{}", name,))
124124
}
125125

126-
/// Decorates a [`Filter`](crate::Filter) to create a [`tracing`] [span] for
126+
/// Decorates a [`Filter`] to create a [`tracing`] [span] for
127127
/// requests and responses.
128128
///
129129
/// [`tracing`]: https://crates.io/crates/tracing

src/filters/ws.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ pub fn ws() -> impl Filter<Extract = One<Ws>, Error = Rejection> + Copy {
6868
)
6969
}
7070

71-
/// Extracted by the [`ws`](ws) filter, and used to finish an upgrade.
71+
/// Extracted by the [`ws`] filter, and used to finish an upgrade.
7272
pub struct Ws {
7373
config: Option<WebSocketConfig>,
7474
key: SecWebsocketKey,

src/lib.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#![doc(html_root_url = "https://docs.rs/warp/0.3.4")]
1+
#![doc(html_root_url = "https://docs.rs/warp/0.3.5")]
22
#![deny(missing_docs)]
33
#![deny(missing_debug_implementations)]
44
#![deny(rust_2018_idioms)]
@@ -81,7 +81,7 @@
8181
//! ## Testing
8282
//!
8383
//! Testing your web services easily is extremely important, and warp provides
84-
//! a [`test`](self::test) module to help send mocked requests through your service.
84+
//! a [`test`](mod@self::test) module to help send mocked requests through your service.
8585
//!
8686
//! [Filter]: trait.Filter.html
8787
//! [reject]: reject/index.html

0 commit comments

Comments
 (0)