Skip to content

Commit 53beb0b

Browse files
committed
url decode path segments with warp::path::param()
1 parent 99bd129 commit 53beb0b

File tree

4 files changed

+116
-1
lines changed

4 files changed

+116
-1
lines changed

src/filters/path.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ use std::str::FromStr;
131131

132132
use futures_util::future;
133133
use http::uri::PathAndQuery;
134+
use percent_encoding::percent_decode_str;
134135

135136
use self::internal::Opaque;
136137
use crate::filter::{filter_fn, one, Filter, FilterBase, Internal, One, Tuple};
@@ -266,11 +267,12 @@ pub fn end() -> impl Filter<Extract = (), Error = Rejection> + Copy {
266267
pub fn param<T: FromStr + Send + 'static>(
267268
) -> impl Filter<Extract = One<T>, Error = Rejection> + Copy {
268269
filter_segment(|seg| {
270+
let seg = percent_decode_str(seg).decode_utf8().map_err(|_| reject::invalid_encoded_url())?;
269271
tracing::trace!("param?: {:?}", seg);
270272
if seg.is_empty() {
271273
return Err(reject::not_found());
272274
}
273-
T::from_str(seg).map(one).map_err(|_| reject::not_found())
275+
T::from_str(seg.as_ref()).map(one).map_err(|_| reject::not_found())
274276
})
275277
}
276278

src/reject.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,10 @@ pub(crate) fn invalid_header(name: &'static str) -> Rejection {
102102
known(InvalidHeader { name })
103103
}
104104

105+
// 400 Bad Request
106+
#[inline]
107+
pub(crate) fn invalid_encoded_url() -> Rejection { known(InvalidEncodedUrl { _p: () }) }
108+
105109
// 400 Bad Request
106110
#[inline]
107111
pub(crate) fn missing_cookie(name: &'static str) -> Rejection {
@@ -289,6 +293,7 @@ enum_known! {
289293
MissingConnectionUpgrade(crate::ws::MissingConnectionUpgrade),
290294
MissingExtension(crate::ext::MissingExtension),
291295
BodyConsumedMultipleTimes(crate::body::BodyConsumedMultipleTimes),
296+
InvalidEncodedUrl(InvalidEncodedUrl),
292297
}
293298

294299
impl Rejection {
@@ -423,6 +428,7 @@ impl Rejections {
423428
| Known::MissingHeader(_)
424429
| Known::MissingCookie(_)
425430
| Known::InvalidQuery(_)
431+
| Known::InvalidEncodedUrl(_)
426432
| Known::BodyReadError(_)
427433
| Known::BodyDeserializeError(_) => StatusCode::BAD_REQUEST,
428434
#[cfg(feature = "websocket")]
@@ -514,6 +520,11 @@ unit_error! {
514520
pub InvalidQuery: "Invalid query string"
515521
}
516522

523+
unit_error! {
524+
/// Invalid encoded url
525+
pub InvalidEncodedUrl: "Invalid encoded url string"
526+
}
527+
517528
unit_error! {
518529
/// HTTP method not allowed
519530
pub MethodNotAllowed: "HTTP method not allowed"

tests/path.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,18 @@ async fn param() {
4242
let req = warp::test::request().path("/warp");
4343
assert_eq!(req.filter(&s).await.unwrap(), "warp");
4444

45+
// % encoding
46+
let req = warp::test::request().path("/warp%20speed");
47+
assert_eq!(req.filter(&s).await.unwrap(), "warp speed");
48+
49+
// % encoding as int
50+
let req = warp::test::request().path("/%35%30");
51+
assert_eq!(req.filter(&num).await.unwrap(), 50);
52+
53+
// + space encoding, is not decoded
54+
let req = warp::test::request().path("/warp+speed");
55+
assert_eq!(req.filter(&s).await.unwrap(), "warp+speed");
56+
4557
// u32 doesn't extract a non-int
4658
let req = warp::test::request().path("/warp");
4759
assert!(!req.matches(&num).await);

tests/query.rs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,3 +137,93 @@ async fn raw_query() {
137137
let extracted = req.filter(&as_raw).await.unwrap();
138138
assert_eq!(extracted, "foo=bar&baz=quux".to_owned());
139139
}
140+
141+
#[tokio::test]
142+
async fn url_encoded_raw_query() {
143+
let as_raw = warp::query::raw();
144+
145+
let req = warp::test::request().path("/?foo=bar%20hi&baz=quux");
146+
147+
let extracted = req.filter(&as_raw).await.unwrap();
148+
assert_eq!(extracted, "foo=bar%20hi&baz=quux".to_owned());
149+
}
150+
151+
#[tokio::test]
152+
async fn plus_encoded_raw_query() {
153+
let as_raw = warp::query::raw();
154+
155+
let req = warp::test::request().path("/?foo=bar+hi&baz=quux");
156+
157+
let extracted = req.filter(&as_raw).await.unwrap();
158+
assert_eq!(extracted, "foo=bar+hi&baz=quux".to_owned());
159+
}
160+
161+
#[derive(Deserialize, Debug, Eq, PartialEq)]
162+
struct MyArgsWithInt {
163+
string: Option<String>,
164+
number: Option<u32>,
165+
}
166+
167+
#[tokio::test]
168+
async fn query_struct_with_int() {
169+
let as_struct = warp::query::<MyArgsWithInt>();
170+
171+
let req = warp::test::request().path("/?string=text&number=30");
172+
173+
let extracted = req.filter(&as_struct).await.unwrap();
174+
assert_eq!(
175+
extracted,
176+
MyArgsWithInt {
177+
string: Some("text".into()),
178+
number: Some(30)
179+
}
180+
);
181+
}
182+
183+
#[tokio::test]
184+
async fn missing_query_struct_with_int() {
185+
let as_struct = warp::query::<MyArgsWithInt>();
186+
187+
let req = warp::test::request().path("/");
188+
189+
let extracted = req.filter(&as_struct).await.unwrap();
190+
assert_eq!(
191+
extracted,
192+
MyArgsWithInt {
193+
string: None,
194+
number: None
195+
}
196+
);
197+
}
198+
199+
#[tokio::test]
200+
async fn url_encoded_query_struct_with_int() {
201+
let as_struct = warp::query::<MyArgsWithInt>();
202+
203+
let req = warp::test::request().path("/?string=test%20text&number=%33%30");
204+
205+
let extracted = req.filter(&as_struct).await.unwrap();
206+
assert_eq!(
207+
extracted,
208+
MyArgsWithInt {
209+
string: Some("test text".into()),
210+
number: Some(30)
211+
}
212+
);
213+
}
214+
215+
#[tokio::test]
216+
async fn plus_encoded_query_struct() {
217+
let as_struct = warp::query::<MyArgsWithInt>();
218+
219+
let req = warp::test::request().path("/?string=test+text");
220+
221+
let extracted = req.filter(&as_struct).await.unwrap();
222+
assert_eq!(
223+
extracted,
224+
MyArgsWithInt {
225+
string: Some("test text".into()),
226+
number: None
227+
}
228+
);
229+
}

0 commit comments

Comments
 (0)