mirror of
https://github.com/tokio-rs/axum.git
synced 2025-01-04 02:01:23 +01:00
Panic on overlapping routes in MethodRouter
(#1102)
* Panic on overlapping routes in `MethodRouter` * changelog link * add test to ensure `head` and `get` don't overlap
This commit is contained in:
parent
661473dcbc
commit
2b386c0baa
2 changed files with 69 additions and 33 deletions
|
@ -35,6 +35,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
- **breaking:** Allow `Error: Into<Infallible>` for `Route::{layer, route_layer}` ([#924])
|
- **breaking:** Allow `Error: Into<Infallible>` for `Route::{layer, route_layer}` ([#924])
|
||||||
- **breaking:** Remove `extractor_middleware` which was previously deprecated.
|
- **breaking:** Remove `extractor_middleware` which was previously deprecated.
|
||||||
Use `axum::middleware::from_extractor` instead ([#1077])
|
Use `axum::middleware::from_extractor` instead ([#1077])
|
||||||
|
- **breaking:** `MethodRouter` now panics on overlapping routes ([#1102])
|
||||||
|
|
||||||
[#1077]: https://github.com/tokio-rs/axum/pull/1077
|
[#1077]: https://github.com/tokio-rs/axum/pull/1077
|
||||||
[#1078]: https://github.com/tokio-rs/axum/pull/1078
|
[#1078]: https://github.com/tokio-rs/axum/pull/1078
|
||||||
|
@ -43,6 +44,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
[#1095]: https://github.com/tokio-rs/axum/pull/1095
|
[#1095]: https://github.com/tokio-rs/axum/pull/1095
|
||||||
[#1098]: https://github.com/tokio-rs/axum/pull/1098
|
[#1098]: https://github.com/tokio-rs/axum/pull/1098
|
||||||
[#924]: https://github.com/tokio-rs/axum/pull/924
|
[#924]: https://github.com/tokio-rs/axum/pull/924
|
||||||
|
[#1102]: https://github.com/tokio-rs/axum/pull/1102
|
||||||
|
|
||||||
# 0.5.7 (08. June, 2022)
|
# 0.5.7 (08. June, 2022)
|
||||||
|
|
||||||
|
|
|
@ -912,6 +912,32 @@ impl<ReqBody, E> MethodRouter<ReqBody, E> {
|
||||||
S: Service<Request<ReqBody>, Response = Response, Error = E> + Clone + Send + 'static,
|
S: Service<Request<ReqBody>, Response = Response, Error = E> + Clone + Send + 'static,
|
||||||
S::Future: Send + 'static,
|
S::Future: Send + 'static,
|
||||||
{
|
{
|
||||||
|
macro_rules! set_service {
|
||||||
|
(
|
||||||
|
$filter:ident,
|
||||||
|
$svc:ident,
|
||||||
|
$allow_header:ident,
|
||||||
|
[
|
||||||
|
$(
|
||||||
|
($out:ident, $variant:ident, [$($method:literal),+])
|
||||||
|
),+
|
||||||
|
$(,)?
|
||||||
|
]
|
||||||
|
) => {
|
||||||
|
$(
|
||||||
|
if $filter.contains(MethodFilter::$variant) {
|
||||||
|
if $out.is_some() {
|
||||||
|
panic!("Overlapping method route. Cannot add two method routes that both handle `{}`", stringify!($variant))
|
||||||
|
}
|
||||||
|
$out = $svc.clone();
|
||||||
|
$(
|
||||||
|
append_allow_header(&mut $allow_header, $method);
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// written with a pattern match like this to ensure we update all fields
|
// written with a pattern match like this to ensure we update all fields
|
||||||
let Self {
|
let Self {
|
||||||
mut get,
|
mut get,
|
||||||
|
@ -927,39 +953,21 @@ impl<ReqBody, E> MethodRouter<ReqBody, E> {
|
||||||
_request_body: _,
|
_request_body: _,
|
||||||
} = self;
|
} = self;
|
||||||
let svc = Some(Route::new(svc));
|
let svc = Some(Route::new(svc));
|
||||||
if filter.contains(MethodFilter::GET) {
|
set_service!(
|
||||||
get = svc.clone();
|
filter,
|
||||||
append_allow_header(&mut allow_header, "GET");
|
svc,
|
||||||
append_allow_header(&mut allow_header, "HEAD");
|
allow_header,
|
||||||
}
|
[
|
||||||
if filter.contains(MethodFilter::HEAD) {
|
(get, GET, ["GET", "HEAD"]),
|
||||||
append_allow_header(&mut allow_header, "HEAD");
|
(head, HEAD, ["HEAD"]),
|
||||||
head = svc.clone();
|
(delete, DELETE, ["DELETE"]),
|
||||||
}
|
(options, OPTIONS, ["OPTIONS"]),
|
||||||
if filter.contains(MethodFilter::DELETE) {
|
(patch, PATCH, ["PATCH"]),
|
||||||
append_allow_header(&mut allow_header, "DELETE");
|
(post, POST, ["POST"]),
|
||||||
delete = svc.clone();
|
(put, PUT, ["PUT"]),
|
||||||
}
|
(trace, TRACE, ["TRACE"]),
|
||||||
if filter.contains(MethodFilter::OPTIONS) {
|
]
|
||||||
append_allow_header(&mut allow_header, "OPTIONS");
|
);
|
||||||
options = svc.clone();
|
|
||||||
}
|
|
||||||
if filter.contains(MethodFilter::PATCH) {
|
|
||||||
append_allow_header(&mut allow_header, "PATCH");
|
|
||||||
patch = svc.clone();
|
|
||||||
}
|
|
||||||
if filter.contains(MethodFilter::POST) {
|
|
||||||
append_allow_header(&mut allow_header, "POST");
|
|
||||||
post = svc.clone();
|
|
||||||
}
|
|
||||||
if filter.contains(MethodFilter::PUT) {
|
|
||||||
append_allow_header(&mut allow_header, "PUT");
|
|
||||||
put = svc.clone();
|
|
||||||
}
|
|
||||||
if filter.contains(MethodFilter::TRACE) {
|
|
||||||
append_allow_header(&mut allow_header, "TRACE");
|
|
||||||
trace = svc;
|
|
||||||
}
|
|
||||||
Self {
|
Self {
|
||||||
get,
|
get,
|
||||||
head,
|
head,
|
||||||
|
@ -1294,6 +1302,32 @@ mod tests {
|
||||||
assert_eq!(headers[ALLOW], "GET,POST");
|
assert_eq!(headers[ALLOW], "GET,POST");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
#[should_panic(
|
||||||
|
expected = "Overlapping method route. Cannot add two method routes that both handle `GET`"
|
||||||
|
)]
|
||||||
|
async fn handler_overlaps() {
|
||||||
|
let _: MethodRouter = get(ok).get(ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
#[should_panic(
|
||||||
|
expected = "Overlapping method route. Cannot add two method routes that both handle `POST`"
|
||||||
|
)]
|
||||||
|
async fn service_overlaps() {
|
||||||
|
let _: MethodRouter = post_service(ok.into_service()).post_service(ok.into_service());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn get_head_does_not_overlap() {
|
||||||
|
let _: MethodRouter = get(ok).head(ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn head_get_does_not_overlap() {
|
||||||
|
let _: MethodRouter = head(ok).get(ok);
|
||||||
|
}
|
||||||
|
|
||||||
async fn call<S>(method: Method, svc: &mut S) -> (StatusCode, HeaderMap, String)
|
async fn call<S>(method: Method, svc: &mut S) -> (StatusCode, HeaderMap, String)
|
||||||
where
|
where
|
||||||
S: Service<Request<Body>, Response = Response, Error = Infallible>,
|
S: Service<Request<Body>, Response = Response, Error = Infallible>,
|
||||||
|
|
Loading…
Reference in a new issue