Stores routes in a map (#408)

With https://github.com/tokio-rs/axum/pull/404 and https://github.com/tokio-rs/axum/pull/402 all routes now have the same types and thus we don't need to nest them but can instead store them all in a map. This simplifies the routing quite a bit and is faster as well.

High level changes:
- Routes are now stored in a `HashMap<RouteId, Route<B>>`.
- `Router::or` is renamed to `Router::merge` because thats what it does now. It copies all routes from one router to another. This also means overlapping routes will cause a panic which is nice win.
- `Router::merge` now only accepts `Router`s so added `Router::fallback` for adding a global 404 handler.
- The `Or` service has been removed.
- `Router::layer` now only adds layers to the routes you actually have meaning middleware runs _after_ routing. I believe that addresses https://github.com/tokio-rs/axum/issues/380 but will test that on another branch.
This commit is contained in:
David Pedersen
2021-10-25 20:49:39 +02:00
committed by GitHub
parent fb87a6a4d3
commit 1634e67e99
8 changed files with 572 additions and 541 deletions
+96
View File
@@ -0,0 +1,96 @@
use super::*;
use crate::handler::Handler;
#[tokio::test]
async fn basic() {
let app = Router::new()
.route("/foo", get(|| async {}))
.fallback((|| async { "fallback" }).into_service());
let client = TestClient::new(app);
assert_eq!(client.get("/foo").send().await.status(), StatusCode::OK);
let res = client.get("/does-not-exist").send().await;
assert_eq!(res.status(), StatusCode::OK);
assert_eq!(res.text().await, "fallback");
}
#[tokio::test]
async fn nest() {
let app = Router::new()
.nest("/foo", Router::new().route("/bar", get(|| async {})))
.fallback((|| async { "fallback" }).into_service());
let client = TestClient::new(app);
assert_eq!(client.get("/foo/bar").send().await.status(), StatusCode::OK);
let res = client.get("/does-not-exist").send().await;
assert_eq!(res.status(), StatusCode::OK);
assert_eq!(res.text().await, "fallback");
}
#[tokio::test]
async fn nesting_with_fallback() {
let app = Router::new().nest(
"/foo",
Router::new()
.route("/bar", get(|| async {}))
.fallback((|| async { "fallback" }).into_service()),
);
let client = TestClient::new(app);
assert_eq!(client.get("/foo/bar").send().await.status(), StatusCode::OK);
// this shouldn't exist because the fallback is inside the nested router
let res = client.get("/does-not-exist").send().await;
assert_eq!(res.status(), StatusCode::NOT_FOUND);
// this should work since we get into the nested router
let res = client.get("/foo/does-not-exist").send().await;
assert_eq!(res.status(), StatusCode::OK);
assert_eq!(res.text().await, "fallback");
}
#[tokio::test]
async fn or() {
let one = Router::new().route("/one", get(|| async {}));
let two = Router::new().route("/two", get(|| async {}));
let app = one
.merge(two)
.fallback((|| async { "fallback" }).into_service());
let client = TestClient::new(app);
assert_eq!(client.get("/one").send().await.status(), StatusCode::OK);
assert_eq!(client.get("/two").send().await.status(), StatusCode::OK);
let res = client.get("/does-not-exist").send().await;
assert_eq!(res.status(), StatusCode::OK);
assert_eq!(res.text().await, "fallback");
}
#[tokio::test]
async fn fallback_on_or() {
let one = Router::new()
.route("/one", get(|| async {}))
.fallback((|| async { "fallback one" }).into_service());
let two = Router::new()
.route("/two", get(|| async {}))
.fallback((|| async { "fallback two" }).into_service());
let app = one.merge(two);
let client = TestClient::new(app);
assert_eq!(client.get("/one").send().await.status(), StatusCode::OK);
assert_eq!(client.get("/two").send().await.status(), StatusCode::OK);
let res = client.get("/does-not-exist").send().await;
assert_eq!(res.status(), StatusCode::OK);
assert_eq!(res.text().await, "fallback two");
}
+47 -47
View File
@@ -9,7 +9,7 @@ async fn basic() {
.route("/foo", get(|| async {}))
.route("/bar", get(|| async {}));
let two = Router::new().route("/baz", get(|| async {}));
let app = one.or(two);
let app = one.merge(two);
let client = TestClient::new(app);
@@ -36,28 +36,28 @@ async fn multiple_ors_balanced_differently() {
test(
"one",
one.clone()
.or(two.clone())
.or(three.clone())
.or(four.clone()),
.merge(two.clone())
.merge(three.clone())
.merge(four.clone()),
)
.await;
test(
"two",
one.clone()
.or(two.clone())
.or(three.clone().or(four.clone())),
.merge(two.clone())
.merge(three.clone().merge(four.clone())),
)
.await;
test(
"three",
one.clone()
.or(two.clone().or(three.clone()).or(four.clone())),
.merge(two.clone().merge(three.clone()).merge(four.clone())),
)
.await;
test("four", one.or(two.or(three.or(four)))).await;
test("four", one.merge(two.merge(three.merge(four)))).await;
async fn test<S, ResBody>(name: &str, app: S)
where
@@ -84,7 +84,7 @@ async fn nested_or() {
let bar = Router::new().route("/bar", get(|| async { "bar" }));
let baz = Router::new().route("/baz", get(|| async { "baz" }));
let bar_or_baz = bar.or(baz);
let bar_or_baz = bar.merge(baz);
let client = TestClient::new(bar_or_baz.clone());
assert_eq!(client.get("/bar").send().await.text().await, "bar");
@@ -99,7 +99,7 @@ async fn nested_or() {
async fn or_with_route_following() {
let one = Router::new().route("/one", get(|| async { "one" }));
let two = Router::new().route("/two", get(|| async { "two" }));
let app = one.or(two).route("/three", get(|| async { "three" }));
let app = one.merge(two).route("/three", get(|| async { "three" }));
let client = TestClient::new(app);
@@ -119,7 +119,7 @@ async fn layer() {
let two = Router::new()
.route("/bar", get(|| async {}))
.layer(ConcurrencyLimitLayer::new(10));
let app = one.or(two);
let app = one.merge(two);
let client = TestClient::new(app);
@@ -140,7 +140,7 @@ async fn layer_and_handle_error() {
.layer(HandleErrorLayer::new(|_| StatusCode::REQUEST_TIMEOUT))
.layer(TimeoutLayer::new(Duration::from_millis(10))),
);
let app = one.or(two);
let app = one.merge(two);
let client = TestClient::new(app);
@@ -152,7 +152,7 @@ async fn layer_and_handle_error() {
async fn nesting() {
let one = Router::new().route("/foo", get(|| async {}));
let two = Router::new().nest("/bar", Router::new().route("/baz", get(|| async {})));
let app = one.or(two);
let app = one.merge(two);
let client = TestClient::new(app);
@@ -164,7 +164,7 @@ async fn nesting() {
async fn boxed() {
let one = Router::new().route("/foo", get(|| async {}));
let two = Router::new().route("/bar", get(|| async {}));
let app = one.or(two);
let app = one.merge(two);
let client = TestClient::new(app);
@@ -176,12 +176,12 @@ async fn boxed() {
async fn many_ors() {
let app = Router::new()
.route("/r1", get(|| async {}))
.or(Router::new().route("/r2", get(|| async {})))
.or(Router::new().route("/r3", get(|| async {})))
.or(Router::new().route("/r4", get(|| async {})))
.or(Router::new().route("/r5", get(|| async {})))
.or(Router::new().route("/r6", get(|| async {})))
.or(Router::new().route("/r7", get(|| async {})));
.merge(Router::new().route("/r2", get(|| async {})))
.merge(Router::new().route("/r3", get(|| async {})))
.merge(Router::new().route("/r4", get(|| async {})))
.merge(Router::new().route("/r5", get(|| async {})))
.merge(Router::new().route("/r6", get(|| async {})))
.merge(Router::new().route("/r7", get(|| async {})));
let client = TestClient::new(app);
@@ -205,7 +205,7 @@ async fn services() {
Ok::<_, Infallible>(Response::new(Body::empty()))
})),
)
.or(Router::new().route(
.merge(Router::new().route(
"/bar",
get(service_fn(|_: Request<Body>| async {
Ok::<_, Infallible>(Response::new(Body::empty()))
@@ -238,7 +238,7 @@ async fn nesting_and_seeing_the_right_uri() {
let one = Router::new().nest("/foo", Router::new().route("/bar", get(all_the_uris)));
let two = Router::new().route("/foo", get(all_the_uris));
let client = TestClient::new(one.or(two));
let client = TestClient::new(one.merge(two));
let res = client.get("/foo/bar").send().await;
assert_eq!(res.status(), StatusCode::OK);
@@ -271,7 +271,7 @@ async fn nesting_and_seeing_the_right_uri_at_more_levels_of_nesting() {
);
let two = Router::new().route("/foo", get(all_the_uris));
let client = TestClient::new(one.or(two));
let client = TestClient::new(one.merge(two));
let res = client.get("/foo/bar/baz").send().await;
assert_eq!(res.status(), StatusCode::OK);
@@ -299,44 +299,44 @@ async fn nesting_and_seeing_the_right_uri_at_more_levels_of_nesting() {
#[tokio::test]
async fn nesting_and_seeing_the_right_uri_ors_with_nesting() {
let one = Router::new().nest(
"/foo",
"/one",
Router::new().nest("/bar", Router::new().route("/baz", get(all_the_uris))),
);
let two = Router::new().nest("/foo", Router::new().route("/qux", get(all_the_uris)));
let three = Router::new().route("/foo", get(all_the_uris));
let two = Router::new().nest("/two", Router::new().route("/qux", get(all_the_uris)));
let three = Router::new().route("/three", get(all_the_uris));
let client = TestClient::new(one.or(two).or(three));
let client = TestClient::new(one.merge(two).merge(three));
let res = client.get("/foo/bar/baz").send().await;
let res = client.get("/one/bar/baz").send().await;
assert_eq!(res.status(), StatusCode::OK);
assert_eq!(
res.json::<Value>().await,
json!({
"uri": "/baz",
"request_uri": "/baz",
"original_uri": "/foo/bar/baz",
"original_uri": "/one/bar/baz",
})
);
let res = client.get("/foo/qux").send().await;
let res = client.get("/two/qux").send().await;
assert_eq!(res.status(), StatusCode::OK);
assert_eq!(
res.json::<Value>().await,
json!({
"uri": "/qux",
"request_uri": "/qux",
"original_uri": "/foo/qux",
"original_uri": "/two/qux",
})
);
let res = client.get("/foo").send().await;
let res = client.get("/three").send().await;
assert_eq!(res.status(), StatusCode::OK);
assert_eq!(
res.json::<Value>().await,
json!({
"uri": "/foo",
"request_uri": "/foo",
"original_uri": "/foo",
"uri": "/three",
"request_uri": "/three",
"original_uri": "/three",
})
);
}
@@ -344,32 +344,32 @@ async fn nesting_and_seeing_the_right_uri_ors_with_nesting() {
#[tokio::test]
async fn nesting_and_seeing_the_right_uri_ors_with_multi_segment_uris() {
let one = Router::new().nest(
"/foo",
Router::new().nest("/bar", Router::new().route("/baz", get(all_the_uris))),
"/one",
Router::new().nest("/foo", Router::new().route("/bar", get(all_the_uris))),
);
let two = Router::new().route("/foo/bar", get(all_the_uris));
let two = Router::new().route("/two/foo", get(all_the_uris));
let client = TestClient::new(one.or(two));
let client = TestClient::new(one.merge(two));
let res = client.get("/foo/bar/baz").send().await;
let res = client.get("/one/foo/bar").send().await;
assert_eq!(res.status(), StatusCode::OK);
assert_eq!(
res.json::<Value>().await,
json!({
"uri": "/baz",
"request_uri": "/baz",
"original_uri": "/foo/bar/baz",
"uri": "/bar",
"request_uri": "/bar",
"original_uri": "/one/foo/bar",
})
);
let res = client.get("/foo/bar").send().await;
let res = client.get("/two/foo").send().await;
assert_eq!(res.status(), StatusCode::OK);
assert_eq!(
res.json::<Value>().await,
json!({
"uri": "/foo/bar",
"request_uri": "/foo/bar",
"original_uri": "/foo/bar",
"uri": "/two/foo",
"request_uri": "/two/foo",
"original_uri": "/two/foo",
})
);
}
+2 -1
View File
@@ -31,11 +31,12 @@ use tower_service::Service;
pub(crate) use helpers::*;
mod fallback;
mod get_to_head;
mod handle_error;
mod helpers;
mod merge;
mod nest;
mod or;
#[tokio::test]
async fn hello_world() {