Commit graph

1506 commits

Author SHA1 Message Date
David Pedersen
3d78711e47
Version 0.3.2 (#483)
- **added:** Add `Router::route_layer` for applying middleware that
  will only run on requests that match a route. This is useful for middleware
  that return early, such as authorization ([#474])

[#474]: https://github.com/tokio-rs/axum/pull/474
2021-11-08 18:01:32 +00:00
David Pedersen
7eb2c40b24
Add Router::route_layer (#474)
This addresses something thats been bothering me for some time: Most middleware need to run regardless if the request matches a route or not. For example you don't wanna skip logging for unmatched requests.

However middleware such as authorization only make sense to run for matching requests. This previously wasn't possible to express and you'd have to manually apply the middleware to each handler. Consider this:

```rust
Router::new()
    .route("/foo", get(|| async {}))
    .layer(RequireAuthorizationLayer::bearer("password"));
```

Calling `GET /foo` with an invalid token would receive `401 Unauthorized` as expected however calling some unknown route like `GET /not-found` would also return `401 Unauthorized`. I think this is unexpected and have seen a few users ask questions about it.

It happened because the 404 you'd otherwise see is generated by a fallback service stored on `Router`. When adding a layer to the router the layer would also be applied to the fallback, which in the case of auth means the fallback would never be called for unauthorized requests.

I think what axum does today is the right default however I still think we should support this somehow. Especially since [`extractor_middleware`](https://docs.rs/axum/0.3.1/axum/extract/fn.extractor_middleware.html) is mainly useful for auth but it doesn't work great today due to this gotcha.

This PR proposes adding `Router::layer_on_matching_route` which only applies layers to routes, not the fallback, which fixes the issue. I'm not a big fan of the name `layer_on_matching_route`, would like something shorter, but I think it communicates the purpose decently.

The generics are a bit different since the request body used on the routes and the fallback must match, so layers that changes the request body type are not compatible with `layer_on_matching_route`. Such middleware are very rare so that should be fine.
2021-11-08 18:48:02 +01:00
David Pedersen
5d8ebce211 Add axum-flash to list of community maintained crates 2021-11-07 17:17:13 +01:00
Kelly Thomas Kline
b26fd03810
Add recommendation (#476) 2021-11-07 07:54:53 +00:00
EdorianDark
513c5a694d
Update tracing-subscriber to 0.3 (#472)
Fixes #469
2021-11-06 14:50:23 +00:00
David Pedersen
09bcd8862d
Version 0.3.1 (#473)
- **fixed:** Implement `Clone` for `IntoMakeServiceWithConnectInfo` ([#471])

[#471]: https://github.com/tokio-rs/axum/pull/471
2021-11-06 13:26:23 +00:00
David Pedersen
b3b377d467
Implement Clone for IntoMakeServiceWithConnectInfo (#471)
Fixes https://github.com/tokio-rs/axum/issues/470
2021-11-06 13:06:20 +00:00
Jonas Platte
b4b022da5f
Add turbo.fish to community showcase (#466) 2021-11-05 13:23:53 +01:00
Eray Karatay
394ba31385
update low-level-rustls example (#465) 2021-11-05 12:02:58 +01:00
Frederik-Baetens
07641450c5
Fix links in readme (#464) 2021-11-04 19:57:05 +01:00
David Pedersen
0be0ea6763 Actually run the routing tests 🤦 2021-11-04 12:48:37 +01:00
david-perez
a47d7eca40
Derive Debug for Router instead of implementing manually (#463) 2021-11-04 12:35:50 +01:00
david-perez
071a1b26e6
Fix typos (#461) 2021-11-03 19:03:46 +00:00
David Pedersen
ba4d8a2357
Move axum crate into workspace subfolder (#458)
* Move axum crate into workspace subfolder

Over time I imagine we're gonna have other crates in this repo that
provide utilities or integrations for axum. This prepares for that by
moving the main axum crate into its own folder.

The README situation is a bit annoying because we want `./README.md`
for viewing the repo on github but `axum/README.md` for crates.io. For
now I've just copy/pasted it and added CI step to make sure they're
identical.

* update changelog link

* Add licenses to all examples

* is this how you install `diff`?

* or maybe this is how?

* fix readme links

* like this?

* fix cargo-deny step

* Try making root readme a symlink

* remove compare readme step

not needed since readme in repo root is now a symlink

* Revert "Add licenses to all examples"

This reverts commit ab321b7fb9.
2021-11-03 12:38:48 +01:00
David Pedersen
b60bfd7f34 Mention "axum-typed-websockets" in community ecosystem 2021-11-03 10:26:20 +01:00
David Pedersen
0f1f28062d
Reorganize tests (#456)
* Reorganize tests

This breaks up the large `crate::tests` module by moving some of the
tests into a place that makes more sense. For example tests of JSON
serialization are moved to the `crate::json` module. The remaining routing
tests have been moved to `crate::routing::tests`.

I generally prefer having tests close to the code they're testing. Makes
it easier to see how/if something is tested.

* Try pinning to older version of async-graphql

* Revert "Try pinning to older version of async-graphql"

This reverts commit 2e2cae7d12f5e433a16d6607497d587863f04384.

* don't test examples on 1.54 on CI

* move ci steps around a bit
2021-11-03 10:22:31 +01:00
David Pedersen
420918a53a Clarify change to handling multiple HTTP methods 2021-11-03 09:58:59 +01:00
arctic-alpaca
a442920ba3
Restructure community showcase (#454) 2021-11-03 09:20:01 +01:00
David Pedersen
5367bc65d1 Fix typo 2021-11-03 08:26:13 +01:00
David Pedersen
708988bfae Fix readme example link 2021-11-03 08:25:31 +01:00
Jordan Gould
1c60255b9f
Add readme example project (#450)
* Add project based on the readme example

* Add readme project link to README.md

* Typo correction

* Update examples/readme-example/Cargo.toml

Use tracing-subscriber 0.2 to match other the other examples

Co-authored-by: David Pedersen <david.pdrsn@gmail.com>

* Update README.md

Use original readme phrasing for crate docs

Co-authored-by: David Pedersen <david.pdrsn@gmail.com>

* Rename readme-exmaple to readme

* Revert tracing call to debug from info

Co-authored-by: Jordan Gould <jordan@tineye.com>
Co-authored-by: David Pedersen <david.pdrsn@gmail.com>
2021-11-03 07:23:28 +00:00
Jonas Platte
2ba9cdce45
Fix typos in CHANGELOG.md (#449) 2021-11-02 20:12:27 +00:00
David Pedersen
baefa47634 0.3.0 is out! 2021-11-02 18:41:29 +01:00
David Pedersen
35dba50b32 Fix typo in changelog 2021-11-02 18:40:57 +01:00
David Pedersen
85731a4f1d
Version 0.3.0 (#448)
- Overall:
  - **fixed:** All known compile time issues are resolved, including those with
    `boxed` and those introduced by Rust 1.56 ([#404])
  - **breaking:** The router's type is now always `Router` regardless of how many routes or
    middleware are applied ([#404])

    This means router types are all always nameable:

    ```rust
    fn my_routes() -> Router {
        Router::new().route(
            "/users",
            post(|| async { "Hello, World!" }),
        )
    }
    ```
  - **breaking:** Added feature flags for HTTP1 and JSON. This enables removing a
    few dependencies if your app only uses HTTP2 or doesn't use JSON. Its only a
    breaking change if you depend on axum with `default_features = false`. ([#286])
  - **breaking:** `Route::boxed` and `BoxRoute` have been removed as they're no longer
    necessary ([#404])
  - **breaking:** `Nested`, `Or` types are now private. They no longer had to be
    public because `Router` is internally boxed ([#404])
  - **breaking:** Remove `routing::Layered` as it didn't actually do anything and
    thus wasn't necessary
  - **breaking:** Vendor `AddExtensionLayer` and `AddExtension` to reduce public
    dependencies
  - **breaking:** `body::BoxBody` is now a type alias for
    `http_body::combinators::UnsyncBoxBody` and thus is no longer `Sync`. This
    is because bodies are streams and requiring streams to be `Sync` is
    unnecessary.
  - **added:** Implement `IntoResponse` for `http_body::combinators::UnsyncBoxBody`.
  - **added:** Add `Handler::into_make_service` for serving a handler without a
    `Router`.
  - **added:** Add `Handler::into_make_service_with_connect_info` for serving a
    handler without a `Router`, and storing info about the incoming connection.
  - **breaking:** axum's minimum support rust version is not 1.54
- Routing:
  - Big internal refactoring of routing leading to several improvements ([#363])
    - **added:** Wildcard routes like `.route("/api/users/*rest", service)` are now supported.
    - **fixed:** The order routes are added in no longer matters.
    - **fixed:** Adding a conflicting route will now cause a panic instead of silently making
      a route unreachable.
    - **fixed:** Route matching is faster as number of routes increase.
  - **fixed:** Correctly handle trailing slashes in routes:
    - If a route with a trailing slash exists and a request without a trailing
      slash is received, axum will send a 301 redirection to the route with the
      trailing slash.
    - Or vice versa if a route without a trailing slash exists and a request
      with a trailing slash is received.
    - This can be overridden by explicitly defining two routes: One with and one
      without trailing a slash.
  - **breaking:** Method routing for handlers have been moved from `axum::handler`
    to `axum::routing`. So `axum::handler::get` now lives at `axum::routing::get`
    ([#405])
  - **breaking:** Method routing for services have been moved from `axum::service`
    to `axum::routing`. So `axum::service::get` now lives at, etc.
    `axum::routing::service_method_routing::get`, etc. ([#405])
  - **breaking:** `Router::or` renamed to `Router::merge` and will now panic on
    overlapping routes. It now only accepts `Router`s and not general `Service`s.
    Use `Router::fallback` for adding fallback routes ([#408])
  - **added:** `Router::fallback` for adding handlers for request that didn't
    match any routes. `Router::fallback` must be use instead of `nest("/", _)` ([#408])
  - **breaking:** `EmptyRouter` has been renamed to `MethodNotAllowed` as its only
    used in method routers and not in path routers (`Router`)
  - **breaking:** Remove support for routing based on the `CONNECT` method. An
    example of combining axum with and HTTP proxy can be found [here][proxy] ([#428])
- Extractors:
  - **fixed:** Expand accepted content types for JSON requests ([#378])
  - **fixed:** Support deserializing `i128` and `u128` in `extract::Path`
  - **breaking:** Automatically do percent decoding in `extract::Path`
    ([#272])
  - **breaking:** Change `Connected::connect_info` to return `Self` and remove
    the associated type `ConnectInfo` ([#396])
  - **added:** Add `extract::MatchedPath` for accessing path in router that
    matched the request ([#412])
- Error handling:
  - **breaking:** Simplify error handling model ([#402]):
    - All services part of the router are now required to be infallible.
    - Error handling utilities have been moved to an `error_handling` module.
    - `Router::check_infallible` has been removed since routers are always
      infallible with the error handling changes.
    - Error handling closures must now handle all errors and thus always return
      something that implements `IntoResponse`.

    With these changes handling errors from fallible middleware is done like so:

    ```rust,no_run
    use axum::{
        routing::get,
        http::StatusCode,
        error_handling::HandleErrorLayer,
        response::IntoResponse,
        Router, BoxError,
    };
    use tower::ServiceBuilder;
    use std::time::Duration;

    let middleware_stack = ServiceBuilder::new()
        // Handle errors from middleware
        //
        // This middleware most be added above any fallible
        // ones if you're using `ServiceBuilder`, due to how ordering works
        .layer(HandleErrorLayer::new(handle_error))
        // Return an error after 30 seconds
        .timeout(Duration::from_secs(30));

    let app = Router::new()
        .route("/", get(|| async { /* ... */ }))
        .layer(middleware_stack);

    fn handle_error(_error: BoxError) -> impl IntoResponse {
        StatusCode::REQUEST_TIMEOUT
    }
    ```

    And handling errors from fallible leaf services is done like so:

    ```rust
    use axum::{
        Router, service,
        body::Body,
        routing::service_method_routing::get,
        response::IntoResponse,
        http::{Request, Response},
        error_handling::HandleErrorExt, // for `.handle_error`
    };
    use std::{io, convert::Infallible};
    use tower::service_fn;

    let app = Router::new()
        .route(
            "/",
            get(service_fn(|_req: Request<Body>| async {
                let contents = tokio::fs::read_to_string("some_file").await?;
                Ok::<_, io::Error>(Response::new(Body::from(contents)))
            }))
            .handle_error(handle_io_error),
        );

    fn handle_io_error(error: io::Error) -> impl IntoResponse {
        // ...
    }
    ```
- Misc:
  - `InvalidWebsocketVersionHeader` has been renamed to `InvalidWebSocketVersionHeader` ([#416])
  - `WebsocketKeyHeaderMissing` has been renamed to `WebSocketKeyHeaderMissing` ([#416])

[#339]: https://github.com/tokio-rs/axum/pull/339
[#286]: https://github.com/tokio-rs/axum/pull/286
[#272]: https://github.com/tokio-rs/axum/pull/272
[#378]: https://github.com/tokio-rs/axum/pull/378
[#363]: https://github.com/tokio-rs/axum/pull/363
[#396]: https://github.com/tokio-rs/axum/pull/396
[#402]: https://github.com/tokio-rs/axum/pull/402
[#404]: https://github.com/tokio-rs/axum/pull/404
[#405]: https://github.com/tokio-rs/axum/pull/405
[#408]: https://github.com/tokio-rs/axum/pull/408
[#412]: https://github.com/tokio-rs/axum/pull/412
[#416]: https://github.com/tokio-rs/axum/pull/416
[#428]: https://github.com/tokio-rs/axum/pull/428
[proxy]: https://github.com/tokio-rs/axum/blob/main/examples/http-proxy/src/main.rs
2021-11-02 18:14:41 +01:00
David Pedersen
394d4b14f6 Mention handling extractor failures in error handling docs 2021-11-02 18:02:09 +01:00
David Pedersen
2957f48ace Mention MSRV bump in changelog 2021-11-02 17:58:21 +01:00
David Pedersen
6b69368895 Small changelog fixes 2021-11-02 17:55:42 +01:00
David Pedersen
9512d14c99
Add Handler::{into_make_service, into_make_service_with_connect_info} (#444)
* Make `IntoMakeService(WithConnectInfo)?` work with any Service

* Add `Handler::{into_make_service, into_make_service_with_connect_info}`

These are useful if you want to run a handler without a `Router`, for
example to make a proxy.
2021-11-02 17:11:18 +01:00
David Pedersen
98907b8887
Add example showing how to handle empty vs missing query params (#443) 2021-11-02 14:42:47 +01:00
David Pedersen
a048d6443b Revert hello-world example
Was accidentally changed in another PR
2021-11-02 13:07:49 +01:00
Michael Grigoryan
5e44295a82
Fix typo in CHANGELOG (#442) 2021-11-02 07:50:10 +01:00
David Pedersen
1b98328dc8
Clean up some todos (#441) 2021-11-01 22:36:17 +00:00
David Pedersen
3d07d40e02
Remove Sync requirement from response bodies (#440)
As we learned [in Tonic] bodies don't need to be `Sync` because they can
only be polled from one thread at a time.

This changes axum's bodies to no longer require `Sync` and makes
`BoxBody` an alias for `UnsyncBoxBody<Bytes, axum::Error>`.

[in Tonic]: https://github.com/hyperium/tonic/issues/117
2021-11-01 22:37:16 +01:00
David Pedersen
9465128f3e
Rework docs (#437)
This reworks axum's docs in an attempt to make things easier to find. Previously I wasn't a fan of those docs for the same topic were spread across the root module docs and more specific places like types and methods.

This changes it such that the root module docs only gives a high level introduction to a topic, perhaps with a small example, and then link to other places where all the details are. This means `Router` is now the single place to learn about routing, and etc for the topics like handlers and error handling.
2021-11-01 21:13:37 +00:00
David Pedersen
1c6038c09f
Depend on tower 0.4.10 (#439)
axum uses `impl Layer for ServiceBuilder` internally which requires
tower 0.4.10. Making that explicit should help users updating to axum
0.3 without also having to manually bump their tower dependency.
2021-11-01 13:15:34 +00:00
David Pedersen
2b8494a576
Make MethodNotAllowed public (#436)
Not sure why this wasn't caught by `#![deny(unreachable_pub, private_in_public)]`
2021-10-31 20:57:32 +01:00
David Pedersen
857ecd7314
Break root module docs into separate files (#432)
For 0.3 I'm thinking about some changes I wanna make to the docs. I
don't like how information is currently spread over so many places.
Still thinking about how I wanna re-organize it.

However I do think it makes sense to break the root module docs into
separate files that get included with `#![doc = include_str!("file")]`.
Makes working on a single section easier and more focused. It looks the
same for the user reading the docs.

This means axum's MSRV is now 1.54 but since thats two releases ago I'm
fine with that.
2021-10-31 20:37:56 +01:00
Paul Butler
9145c0e396
Add notify.run to community showcase (#435) 2021-10-31 08:22:46 +01:00
david-perez
be62f49b7e
Fix typo in sealed trait used in Handler (#430) 2021-10-28 16:44:04 +00:00
david-perez
21e5b654ab
Fix NotFound docs (#431) 2021-10-28 16:41:52 +00:00
David Pedersen
2d1635a6d7
Fix sse and ws examples (#429)
* Fix sse example

* Fix websockets example
2021-10-27 15:52:41 +00:00
David Pedersen
a3822cb175 Add test for wildcards with trailing slashes
Its already working, just to make sure we don't regress in the future
2021-10-27 09:32:14 +02:00
David Pedersen
a2627e9084
Remove support for CONNECT (#428)
Given how different `CONNECT` requests are I think its fine to require
users to handle them manually. Since they don't contain paths you
probably don't need a full routing framework 🤷

An example can be found [here](https://github.com/tokio-rs/axum/blob/main/examples/http-proxy/src/main.rs)

Fixes #418
2021-10-26 21:11:33 +00:00
David Pedersen
94330b7796
Panic if routing to router (#427)
This panics if you pass a `Router` to `Router::route`. Thats invalid and
will result in unreachable routes.

Unfortunately I don't think we can make it a type error while supporting
general `Service`s. So I think this is a decent workaround.

Fixes https://github.com/tokio-rs/axum/issues/174
2021-10-26 20:44:16 +00:00
David Pedersen
92f96a201c
Implement nesting as regular wildcard routes (#426)
When fixing bugs with `MatchedPath` (introduced to fix https://github.com/tokio-rs/axum/issues/386) I realized nesting could basically be implemented using regular routes, if we can detect that the service passed to `nest` is in fact a `Router`. Then we can transfer over all its routes and add the prefix.

This makes nesting much simpler in general and should also be slightly faster since we're no longer nesting routers.
2021-10-26 20:31:22 +00:00
David Pedersen
fc9bfb8a50
Add HTTP proxy example (#425) 2021-10-26 20:46:40 +02:00
David Pedersen
302a720271
Breakup routing module (#424)
The routing module had gotten quite hard to navigate so this breaks it
into smaller modules. No user facing changes.
2021-10-26 17:51:22 +00:00
David Pedersen
91981db8c7
Fix middleware not seeing the full URI for nested routes (#423)
We stripped the prefix before calling any middleware. Instead the prefix
should be stripped just before calling the actual route. This way
middleware still see the full URI, as it used to work in 0.2.

Fixes #419
2021-10-26 19:29:59 +02:00
David Pedersen
8fe4eaf1d5
Run middleware for requests with no matching route (#422)
While thinking about #419 I realized that `main` had a bug where
middleware wouldn't be called if no route matched the incoming request.
`Router` would just directly return a 404 without calling any
middleware.

This fixes by making the fallback default to a service that always
returns 404 and always calling that if no route matches.

Layers applied to the router is then also applied to the fallback.

Unfortunately this breaks #380 but I don't currently see a way to
support both. Auth middleware need to run _after_ routing because you
don't care about auth for unknown paths, but logging middleware need to
run _before_ routing because they do care about seeing requests for
unknown paths so they can log them...

Part of #419
2021-10-26 18:39:05 +02:00