mirror of
https://gitlab.com/msrd0/gotham-restful.git
synced 2025-02-23 04:52:28 +00:00
gotham finally has a release candidate
This commit is contained in:
parent
8321b63982
commit
c1cb0e692a
6 changed files with 10 additions and 436 deletions
|
@ -23,9 +23,9 @@ chrono = { version = "0.4.11", features = ["serde"], optional = true }
|
||||||
cookie = { version = "0.13.3", optional = true }
|
cookie = { version = "0.13.3", optional = true }
|
||||||
futures-core = "0.3.4"
|
futures-core = "0.3.4"
|
||||||
futures-util = "0.3.4"
|
futures-util = "0.3.4"
|
||||||
gotham = { git = "https://github.com/gotham-rs/gotham", version = "0.5.0-dev", default-features = false }
|
gotham = { version = "0.5.0-rc.1", default-features = false }
|
||||||
gotham_derive = { git = "https://github.com/gotham-rs/gotham", version = "0.5.0-dev" }
|
gotham_derive = "0.5.0-rc.1"
|
||||||
gotham_middleware_diesel = { git = "https://github.com/gotham-rs/gotham", version = "0.1.0", optional = true }
|
gotham_middleware_diesel = { version = "0.1.2", optional = true }
|
||||||
gotham_restful_derive = { version = "0.1.0-dev" }
|
gotham_restful_derive = { version = "0.1.0-dev" }
|
||||||
indexmap = { version = "1.3.2", optional = true }
|
indexmap = { version = "1.3.2", optional = true }
|
||||||
itertools = "0.9.0"
|
itertools = "0.9.0"
|
||||||
|
|
|
@ -15,8 +15,8 @@ gitlab = { repository = "msrd0/gotham-restful", branch = "master" }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
fake = "2.2"
|
fake = "2.2"
|
||||||
gotham = { git = "https://github.com/gotham-rs/gotham", version = "0.5.0-dev", default-features = false }
|
gotham = { version = "0.5.0-rc.1", default-features = false }
|
||||||
gotham_derive = { git = "https://github.com/gotham-rs/gotham", version = "0.5.0-dev", default-features = false }
|
gotham_derive = "0.5.0-rc.1"
|
||||||
gotham_restful = { version = "0.1.0-dev", features = ["auth", "openapi"] }
|
gotham_restful = { version = "0.1.0-dev", features = ["auth", "openapi"] }
|
||||||
log = "0.4.8"
|
log = "0.4.8"
|
||||||
log4rs = { version = "0.12", features = ["console_appender"], default-features = false }
|
log4rs = { version = "0.12", features = ["console_appender"], default-features = false }
|
||||||
|
|
|
@ -1,217 +0,0 @@
|
||||||
use super::{LookupTable, LookupTableFromTypes};
|
|
||||||
use gotham::{
|
|
||||||
hyper::{
|
|
||||||
header::{HeaderMap, ACCEPT},
|
|
||||||
StatusCode
|
|
||||||
},
|
|
||||||
router::{non_match::RouteNonMatch, route::matcher::RouteMatcher},
|
|
||||||
state::{FromState, State}
|
|
||||||
};
|
|
||||||
use mime::Mime;
|
|
||||||
use std::{
|
|
||||||
num::ParseFloatError,
|
|
||||||
str::FromStr
|
|
||||||
};
|
|
||||||
use thiserror::Error;
|
|
||||||
|
|
||||||
|
|
||||||
/// A mime type that is optionally weighted with a quality.
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct QMime
|
|
||||||
{
|
|
||||||
mime : Mime,
|
|
||||||
weight : Option<f32>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl QMime
|
|
||||||
{
|
|
||||||
fn new(mime : Mime, weight : Option<f32>) -> Self
|
|
||||||
{
|
|
||||||
Self { mime, weight }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
|
||||||
enum QMimeError
|
|
||||||
{
|
|
||||||
#[error("Unable to parse mime type: {0}")]
|
|
||||||
MimeError(#[from] mime::FromStrError),
|
|
||||||
#[error("Unable to parse mime quality: {0}")]
|
|
||||||
NumError(#[from] ParseFloatError)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for QMime
|
|
||||||
{
|
|
||||||
type Err = QMimeError;
|
|
||||||
|
|
||||||
fn from_str(str : &str) -> Result<Self, Self::Err>
|
|
||||||
{
|
|
||||||
match str.find(";q=") {
|
|
||||||
None => Ok(Self::new(str.parse()?, None)),
|
|
||||||
Some(index) => {
|
|
||||||
let mime = str[..index].parse()?;
|
|
||||||
let weight = str[index+3..].parse()?;
|
|
||||||
Ok(Self::new(mime, Some(weight)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
A route matcher that checks whether the supported types match the accept header of the request.
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
|
|
||||||
```
|
|
||||||
# use gotham::{helpers::http::response::create_response, hyper::StatusCode, router::builder::*};
|
|
||||||
# use gotham_restful::matcher::AcceptHeaderMatcher;
|
|
||||||
#
|
|
||||||
# const img_content : &[u8] = b"This is the content of a webp image";
|
|
||||||
#
|
|
||||||
# let IMAGE_WEBP : mime::Mime = "image/webp".parse().unwrap();
|
|
||||||
let types = vec![IMAGE_WEBP];
|
|
||||||
let matcher = AcceptHeaderMatcher::new(types);
|
|
||||||
|
|
||||||
# build_simple_router(|route| {
|
|
||||||
// use the matcher for your request
|
|
||||||
route.post("/foo")
|
|
||||||
.extend_route_matcher(matcher)
|
|
||||||
.to(|state| {
|
|
||||||
// we know that the client is a modern browser and can handle webp images
|
|
||||||
# let IMAGE_WEBP : mime::Mime = "image/webp".parse().unwrap();
|
|
||||||
let res = create_response(&state, StatusCode::OK, IMAGE_WEBP, img_content);
|
|
||||||
(state, res)
|
|
||||||
});
|
|
||||||
# });
|
|
||||||
```
|
|
||||||
*/
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct AcceptHeaderMatcher
|
|
||||||
{
|
|
||||||
types : Vec<Mime>,
|
|
||||||
lookup_table : LookupTable
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AcceptHeaderMatcher
|
|
||||||
{
|
|
||||||
/// Create a new `AcceptHeaderMatcher` with the given types that can be produced by the route.
|
|
||||||
pub fn new(types : Vec<Mime>) -> Self
|
|
||||||
{
|
|
||||||
let lookup_table = LookupTable::from_types(types.iter(), true);
|
|
||||||
Self { types, lookup_table }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn err() -> RouteNonMatch
|
|
||||||
{
|
|
||||||
RouteNonMatch::new(StatusCode::NOT_ACCEPTABLE)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RouteMatcher for AcceptHeaderMatcher
|
|
||||||
{
|
|
||||||
fn is_match(&self, state : &State) -> Result<(), RouteNonMatch>
|
|
||||||
{
|
|
||||||
HeaderMap::borrow_from(state).get(ACCEPT)
|
|
||||||
.map(|header| {
|
|
||||||
// parse mime types from the accept header
|
|
||||||
let acceptable = header.to_str()
|
|
||||||
.map_err(|_| err())?
|
|
||||||
.split(',')
|
|
||||||
.map(|str| str.trim().parse())
|
|
||||||
.collect::<Result<Vec<QMime>, _>>()
|
|
||||||
.map_err(|_| err())?;
|
|
||||||
|
|
||||||
for qmime in acceptable
|
|
||||||
{
|
|
||||||
// get mime type candidates from the lookup table
|
|
||||||
let essence = qmime.mime.essence_str();
|
|
||||||
let candidates = match self.lookup_table.get(essence) {
|
|
||||||
Some(candidates) => candidates,
|
|
||||||
None => continue
|
|
||||||
};
|
|
||||||
for i in candidates
|
|
||||||
{
|
|
||||||
let candidate = &self.types[*i];
|
|
||||||
|
|
||||||
// check that the candidates have the same suffix - this is not included in the
|
|
||||||
// essence string
|
|
||||||
if candidate.suffix() != qmime.mime.suffix()
|
|
||||||
{
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// this candidate matches - params don't play a role in accept header matching
|
|
||||||
return Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// no candidates found
|
|
||||||
Err(err())
|
|
||||||
}).unwrap_or_else(|| {
|
|
||||||
// no accept header - assume all types are acceptable
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test
|
|
||||||
{
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
fn with_state<F>(accept : Option<&str>, block : F)
|
|
||||||
where F : FnOnce(&mut State) -> ()
|
|
||||||
{
|
|
||||||
State::with_new(|state| {
|
|
||||||
let mut headers = HeaderMap::new();
|
|
||||||
if let Some(acc) = accept
|
|
||||||
{
|
|
||||||
headers.insert(ACCEPT, acc.parse().unwrap());
|
|
||||||
}
|
|
||||||
state.put(headers);
|
|
||||||
block(state);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn no_accept_header()
|
|
||||||
{
|
|
||||||
let matcher = AcceptHeaderMatcher::new(vec!(mime::TEXT_PLAIN));
|
|
||||||
with_state(None, |state| assert!(matcher.is_match(&state).is_ok()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn single_mime_type()
|
|
||||||
{
|
|
||||||
let matcher = AcceptHeaderMatcher::new(vec!(mime::TEXT_PLAIN, mime::IMAGE_PNG));
|
|
||||||
with_state(Some("text/plain"), |state| assert!(matcher.is_match(&state).is_ok()));
|
|
||||||
with_state(Some("text/html"), |state| assert!(matcher.is_match(&state).is_err()));
|
|
||||||
with_state(Some("image/png"), |state| assert!(matcher.is_match(&state).is_ok()));
|
|
||||||
with_state(Some("image/webp"), |state| assert!(matcher.is_match(&state).is_err()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn star_star()
|
|
||||||
{
|
|
||||||
let matcher = AcceptHeaderMatcher::new(vec!(mime::IMAGE_PNG));
|
|
||||||
with_state(Some("*/*"), |state| assert!(matcher.is_match(&state).is_ok()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn image_star()
|
|
||||||
{
|
|
||||||
let matcher = AcceptHeaderMatcher::new(vec!(mime::IMAGE_PNG));
|
|
||||||
with_state(Some("image/*"), |state| assert!(matcher.is_match(&state).is_ok()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn complex_header()
|
|
||||||
{
|
|
||||||
let matcher = AcceptHeaderMatcher::new(vec!(mime::IMAGE_PNG));
|
|
||||||
with_state(Some("text/html,image/webp;q=0.8"), |state| assert!(matcher.is_match(&state).is_err()));
|
|
||||||
with_state(Some("text/html,image/webp;q=0.8,*/*;q=0.1"), |state| assert!(matcher.is_match(&state).is_ok()));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,173 +0,0 @@
|
||||||
use super::{LookupTable, LookupTableFromTypes};
|
|
||||||
use gotham::{
|
|
||||||
hyper::{
|
|
||||||
header::{HeaderMap, CONTENT_TYPE},
|
|
||||||
StatusCode
|
|
||||||
},
|
|
||||||
router::{non_match::RouteNonMatch, route::matcher::RouteMatcher},
|
|
||||||
state::{FromState, State}
|
|
||||||
};
|
|
||||||
use mime::Mime;
|
|
||||||
|
|
||||||
/**
|
|
||||||
A route matcher that checks for the presence of a supported content type.
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
|
|
||||||
```
|
|
||||||
# use gotham::{helpers::http::response::create_response, hyper::StatusCode, router::builder::*};
|
|
||||||
# use gotham_restful::matcher::ContentTypeMatcher;
|
|
||||||
#
|
|
||||||
let types = vec![mime::TEXT_HTML, mime::TEXT_PLAIN];
|
|
||||||
let matcher = ContentTypeMatcher::new(types)
|
|
||||||
// optionally accept requests with no content type
|
|
||||||
.allow_no_type();
|
|
||||||
|
|
||||||
# build_simple_router(|route| {
|
|
||||||
// use the matcher for your request
|
|
||||||
route.post("/foo")
|
|
||||||
.extend_route_matcher(matcher)
|
|
||||||
.to(|state| {
|
|
||||||
let res = create_response(&state, StatusCode::OK, mime::TEXT_PLAIN, "Correct Content Type!");
|
|
||||||
(state, res)
|
|
||||||
});
|
|
||||||
# });
|
|
||||||
```
|
|
||||||
*/
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct ContentTypeMatcher
|
|
||||||
{
|
|
||||||
types : Vec<Mime>,
|
|
||||||
lookup_table : LookupTable,
|
|
||||||
allow_no_type : bool
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ContentTypeMatcher
|
|
||||||
{
|
|
||||||
/// Create a new `ContentTypeMatcher` with the given supported types that does not allow requests
|
|
||||||
/// that don't include a content-type header.
|
|
||||||
pub fn new(types : Vec<Mime>) -> Self
|
|
||||||
{
|
|
||||||
let lookup_table = LookupTable::from_types(types.iter(), false);
|
|
||||||
Self { types, lookup_table, allow_no_type: false }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Modify this matcher to allow requests that don't include a content-type header.
|
|
||||||
pub fn allow_no_type(mut self) -> Self
|
|
||||||
{
|
|
||||||
self.allow_no_type = true;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn err() -> RouteNonMatch
|
|
||||||
{
|
|
||||||
RouteNonMatch::new(StatusCode::UNSUPPORTED_MEDIA_TYPE)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RouteMatcher for ContentTypeMatcher
|
|
||||||
{
|
|
||||||
fn is_match(&self, state : &State) -> Result<(), RouteNonMatch>
|
|
||||||
{
|
|
||||||
HeaderMap::borrow_from(state).get(CONTENT_TYPE)
|
|
||||||
.map(|ty| {
|
|
||||||
// parse mime type from the content type header
|
|
||||||
let mime : Mime = ty.to_str()
|
|
||||||
.map_err(|_| err())?
|
|
||||||
.parse()
|
|
||||||
.map_err(|_| err())?;
|
|
||||||
|
|
||||||
// get mime type candidates from the lookup table
|
|
||||||
let essence = mime.essence_str();
|
|
||||||
let candidates = self.lookup_table.get(essence).ok_or_else(err)?;
|
|
||||||
for i in candidates
|
|
||||||
{
|
|
||||||
let candidate = &self.types[*i];
|
|
||||||
|
|
||||||
// check that the candidates have the same suffix - this is not included in the
|
|
||||||
// essence string
|
|
||||||
if candidate.suffix() != mime.suffix()
|
|
||||||
{
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// check that this candidate has at least the parameters that the content type
|
|
||||||
// has and that their values are equal
|
|
||||||
if candidate.params().any(|(key, value)| mime.get_param(key) != Some(value))
|
|
||||||
{
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// this candidate matches
|
|
||||||
return Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// no candidates found
|
|
||||||
Err(err())
|
|
||||||
}).unwrap_or_else(|| {
|
|
||||||
// no type present
|
|
||||||
if self.allow_no_type { Ok(()) } else { Err(err()) }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test
|
|
||||||
{
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
fn with_state<F>(content_type : Option<&str>, block : F)
|
|
||||||
where F : FnOnce(&mut State) -> ()
|
|
||||||
{
|
|
||||||
State::with_new(|state| {
|
|
||||||
let mut headers = HeaderMap::new();
|
|
||||||
if let Some(ty) = content_type
|
|
||||||
{
|
|
||||||
headers.insert(CONTENT_TYPE, ty.parse().unwrap());
|
|
||||||
}
|
|
||||||
state.put(headers);
|
|
||||||
block(state);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn empty_type_list()
|
|
||||||
{
|
|
||||||
let matcher = ContentTypeMatcher::new(Vec::new());
|
|
||||||
with_state(None, |state| assert!(matcher.is_match(&state).is_err()));
|
|
||||||
with_state(Some("text/plain"), |state| assert!(matcher.is_match(&state).is_err()));
|
|
||||||
|
|
||||||
let matcher = matcher.allow_no_type();
|
|
||||||
with_state(None, |state| assert!(matcher.is_match(&state).is_ok()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn simple_type()
|
|
||||||
{
|
|
||||||
let matcher = ContentTypeMatcher::new(vec![mime::TEXT_PLAIN]);
|
|
||||||
with_state(None, |state| assert!(matcher.is_match(&state).is_err()));
|
|
||||||
with_state(Some("text/plain"), |state| assert!(matcher.is_match(&state).is_ok()));
|
|
||||||
with_state(Some("text/plain; charset=utf-8"), |state| assert!(matcher.is_match(&state).is_ok()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn complex_type()
|
|
||||||
{
|
|
||||||
let matcher = ContentTypeMatcher::new(vec!["image/svg+xml; charset=utf-8".parse().unwrap()]);
|
|
||||||
with_state(Some("image/svg"), |state| assert!(matcher.is_match(&state).is_err()));
|
|
||||||
with_state(Some("image/svg+xml"), |state| assert!(matcher.is_match(&state).is_err()));
|
|
||||||
with_state(Some("image/svg+xml; charset=utf-8"), |state| assert!(matcher.is_match(&state).is_ok()));
|
|
||||||
with_state(Some("image/svg+xml; charset=utf-8; eol=lf"), |state| assert!(matcher.is_match(&state).is_ok()));
|
|
||||||
with_state(Some("image/svg+xml; charset=us-ascii"), |state| assert!(matcher.is_match(&state).is_err()));
|
|
||||||
with_state(Some("image/svg+json; charset=utf-8"), |state| assert!(matcher.is_match(&state).is_err()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn type_mismatch()
|
|
||||||
{
|
|
||||||
let matcher = ContentTypeMatcher::new(vec![mime::TEXT_HTML]);
|
|
||||||
with_state(Some("text/plain"), |state| assert!(matcher.is_match(&state).is_err()));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,40 +1,5 @@
|
||||||
use itertools::Itertools;
|
|
||||||
use mime::Mime;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
mod accept;
|
|
||||||
pub use accept::AcceptHeaderMatcher;
|
|
||||||
|
|
||||||
mod content_type;
|
|
||||||
pub use content_type::ContentTypeMatcher;
|
|
||||||
|
|
||||||
#[cfg(feature = "cors")]
|
#[cfg(feature = "cors")]
|
||||||
mod access_control_request_method;
|
mod access_control_request_method;
|
||||||
#[cfg(feature = "cors")]
|
#[cfg(feature = "cors")]
|
||||||
pub use access_control_request_method::AccessControlRequestMethodMatcher;
|
pub use access_control_request_method::AccessControlRequestMethodMatcher;
|
||||||
|
|
||||||
type LookupTable = HashMap<String, Vec<usize>>;
|
|
||||||
|
|
||||||
trait LookupTableFromTypes
|
|
||||||
{
|
|
||||||
fn from_types<'a, I : Iterator<Item = &'a Mime>>(types : I, include_stars : bool) -> Self;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LookupTableFromTypes for LookupTable
|
|
||||||
{
|
|
||||||
fn from_types<'a, I : Iterator<Item = &'a Mime>>(types : I, include_stars : bool) -> Self
|
|
||||||
{
|
|
||||||
if include_stars
|
|
||||||
{
|
|
||||||
return types
|
|
||||||
.enumerate()
|
|
||||||
.flat_map(|(i, mime)| vec![("*/*".to_owned(), i), (format!("{}/*", mime.type_()), i), (mime.essence_str().to_owned(), i)].into_iter())
|
|
||||||
.into_group_map();
|
|
||||||
}
|
|
||||||
|
|
||||||
types
|
|
||||||
.enumerate()
|
|
||||||
.map(|(i, mime)| (mime.essence_str().to_owned(), i))
|
|
||||||
.into_group_map()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
matcher::{AcceptHeaderMatcher, ContentTypeMatcher},
|
|
||||||
resource::*,
|
resource::*,
|
||||||
result::{ResourceError, ResourceResult},
|
result::{ResourceError, ResourceResult},
|
||||||
RequestBody,
|
RequestBody,
|
||||||
|
@ -22,7 +21,7 @@ use gotham::{
|
||||||
router::{
|
router::{
|
||||||
builder::*,
|
builder::*,
|
||||||
non_match::RouteNonMatch,
|
non_match::RouteNonMatch,
|
||||||
route::matcher::RouteMatcher
|
route::matcher::{AcceptHeaderRouteMatcher, ContentTypeHeaderRouteMatcher, RouteMatcher}
|
||||||
},
|
},
|
||||||
state::{FromState, State}
|
state::{FromState, State}
|
||||||
};
|
};
|
||||||
|
@ -262,7 +261,7 @@ fn remove_handler<Handler : ResourceRemove>(state : State) -> Pin<Box<HandlerFut
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct MaybeMatchAcceptHeader
|
struct MaybeMatchAcceptHeader
|
||||||
{
|
{
|
||||||
matcher : Option<AcceptHeaderMatcher>
|
matcher : Option<AcceptHeaderRouteMatcher>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RouteMatcher for MaybeMatchAcceptHeader
|
impl RouteMatcher for MaybeMatchAcceptHeader
|
||||||
|
@ -285,7 +284,7 @@ impl From<Option<Vec<Mime>>> for MaybeMatchAcceptHeader
|
||||||
types => types
|
types => types
|
||||||
};
|
};
|
||||||
Self {
|
Self {
|
||||||
matcher: types.map(AcceptHeaderMatcher::new)
|
matcher: types.map(AcceptHeaderRouteMatcher::new)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -293,7 +292,7 @@ impl From<Option<Vec<Mime>>> for MaybeMatchAcceptHeader
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct MaybeMatchContentTypeHeader
|
struct MaybeMatchContentTypeHeader
|
||||||
{
|
{
|
||||||
matcher : Option<ContentTypeMatcher>
|
matcher : Option<ContentTypeHeaderRouteMatcher>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RouteMatcher for MaybeMatchContentTypeHeader
|
impl RouteMatcher for MaybeMatchContentTypeHeader
|
||||||
|
@ -312,7 +311,7 @@ impl From<Option<Vec<Mime>>> for MaybeMatchContentTypeHeader
|
||||||
fn from(types : Option<Vec<Mime>>) -> Self
|
fn from(types : Option<Vec<Mime>>) -> Self
|
||||||
{
|
{
|
||||||
Self {
|
Self {
|
||||||
matcher: types.map(ContentTypeMatcher::new).map(ContentTypeMatcher::allow_no_type)
|
matcher: types.map(|types| ContentTypeHeaderRouteMatcher::new(types).allow_no_type())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue