Skip to content

Commit d22f200

Browse files
authored
Parse accept header with quotes (#338)
* Parse accept header with quotes * cargo fmt * minimum mime version * commit suggestion * cargo fmt * Rework tests * cargo fmt
1 parent 8f02848 commit d22f200

File tree

2 files changed

+51
-18
lines changed

2 files changed

+51
-18
lines changed

tower-http/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ async-compression = { version = "0.3", optional = true, features = ["tokio"] }
2828
base64 = { version = "0.20", optional = true }
2929
http-range-header = "0.3.0"
3030
iri-string = { version = "0.7.0", optional = true }
31-
mime = { version = "0.3", optional = true, default_features = false }
31+
mime = { version = "0.3.17", optional = true, default_features = false }
3232
mime_guess = { version = "2", optional = true, default_features = false }
3333
percent-encoding = { version = "2.1.0", optional = true }
3434
tokio = { version = "1.6", optional = true, default_features = false }

tower-http/src/validate_request.rs

+50-17
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@
115115
116116
use http::{header, Request, Response, StatusCode};
117117
use http_body::Body;
118-
use mime::Mime;
118+
use mime::{Mime, MimeIter};
119119
use pin_project_lite::pin_project;
120120
use std::{
121121
fmt,
@@ -379,25 +379,24 @@ where
379379
.headers()
380380
.get_all(header::ACCEPT)
381381
.into_iter()
382-
.flat_map(|header| {
383-
header
384-
.to_str()
385-
.ok()
386-
.into_iter()
387-
.flat_map(|s| s.split(",").map(|typ| typ.trim()))
388-
})
382+
.filter_map(|header| header.to_str().ok())
389383
.any(|h| {
390-
h.parse::<Mime>()
384+
MimeIter::new(&h)
391385
.map(|mim| {
392-
let typ = self.header_value.type_();
393-
let subtype = self.header_value.subtype();
394-
match (mim.type_(), mim.subtype()) {
395-
(t, s) if t == typ && s == subtype => true,
396-
(t, mime::STAR) if t == typ => true,
397-
(mime::STAR, mime::STAR) => true,
398-
_ => false,
386+
if let Ok(mim) = mim {
387+
let typ = self.header_value.type_();
388+
let subtype = self.header_value.subtype();
389+
match (mim.type_(), mim.subtype()) {
390+
(t, s) if t == typ && s == subtype => true,
391+
(t, mime::STAR) if t == typ => true,
392+
(mime::STAR, mime::STAR) => true,
393+
_ => false,
394+
}
395+
} else {
396+
false
399397
}
400398
})
399+
.reduce(|acc, mim| acc || mim)
401400
.unwrap_or(false)
402401
})
403402
{
@@ -413,7 +412,7 @@ where
413412
mod tests {
414413
#[allow(unused_imports)]
415414
use super::*;
416-
use http::header;
415+
use http::{header, StatusCode};
417416
use hyper::Body;
418417
use tower::{BoxError, ServiceBuilder, ServiceExt};
419418

@@ -545,6 +544,40 @@ mod tests {
545544
assert_eq!(res.status(), StatusCode::OK);
546545
}
547546

547+
#[tokio::test]
548+
async fn accepted_header_with_quotes_valid() {
549+
let value = "foo/bar; parisien=\"baguette, text/html, jambon, fromage\", application/*";
550+
let mut service = ServiceBuilder::new()
551+
.layer(ValidateRequestHeaderLayer::accept("application/xml"))
552+
.service_fn(echo);
553+
554+
let request = Request::get("/")
555+
.header(header::ACCEPT, value)
556+
.body(Body::empty())
557+
.unwrap();
558+
559+
let res = service.ready().await.unwrap().call(request).await.unwrap();
560+
561+
assert_eq!(res.status(), StatusCode::OK);
562+
}
563+
564+
#[tokio::test]
565+
async fn accepted_header_with_quotes_invalid() {
566+
let value = "foo/bar; parisien=\"baguette, text/html, jambon, fromage\"";
567+
let mut service = ServiceBuilder::new()
568+
.layer(ValidateRequestHeaderLayer::accept("text/html"))
569+
.service_fn(echo);
570+
571+
let request = Request::get("/")
572+
.header(header::ACCEPT, value)
573+
.body(Body::empty())
574+
.unwrap();
575+
576+
let res = service.ready().await.unwrap().call(request).await.unwrap();
577+
578+
assert_eq!(res.status(), StatusCode::NOT_ACCEPTABLE);
579+
}
580+
548581
async fn echo(req: Request<Body>) -> Result<Response<Body>, BoxError> {
549582
Ok(Response::new(req.into_body()))
550583
}

0 commit comments

Comments
 (0)