Commit Graph

418 Commits

Author SHA1 Message Date
Eray Karatay f6b47478da
Move `axum-debug` crate to workspace (#497)
* add axum-debug to workspace

* update readme

* add changes to changelog

* little docs update

* fix the gap

a tab has leaked into workspace Cargo.toml, it must be fixed

* address clippy warnings
2021-11-11 15:18:40 +01:00
Eray Karatay 2507463706
update tls-rustls example (#494) 2021-11-10 15:07:09 +01:00
Kelly Thomas Kline 0990e27964
Correct grammar (#492) 2021-11-10 12:13:47 +00:00
David Pedersen fdd889525d
Implement `FromRequest` for `http::request::Parts` (#489)
Its a convenient way to extract everything from a request except the
body. Also makes sense for axum to provide this since other crates
can't.
2021-11-09 20:37:24 +00:00
David Pedersen 62be8aaa18
Implement `IntoResponse` for `http::response::Parts` (#490)
* Implement `IntoResponse` for `http::response::Parts`

Not sure there are many use cases for this but still thinks it makes to
provide since other crates can't.

* Use `Response::from_parts`
2021-11-09 20:50:29 +01:00
Imbolc 2ee66e812f
`FromStr` based `empty_string_as_none` implementation (#486) 2021-11-09 13:05:31 +01:00
Jonas Platte 9c77ee0295
Use nightly clippy & rustfmt for more style checks (#487) 2021-11-09 11:53:57 +00:00
Jonas Platte bcd18a03bc
Split first sentence into its own paragraph in TypedHeader docs (#485) 2021-11-09 12:14:42 +01:00
David Pedersen 3f84eca753 Add `axum-handle-error-extract` to ecosystem 2021-11-09 09:39:03 +01:00
Folyd 028f8a7242
Fix docs links (#481) 2021-11-09 07:26:53 +00:00
David Pedersen 7710f2cf7f Specify version of axum that axum-handle-error-extract depends on
Seems to be required by `cargo publish`
2021-11-08 19:28:28 +01:00
David Pedersen 7e40312cff
Add `axum-handle-error-extract` crate (#468)
* Add `axum-handle-error-extract` crate

* fixup

* Fix readme link

* Add `HandleErrorExt` for new error handling middleware

* Revert "fixup"

This reverts commit 83e04954df.
2021-11-08 19:24:54 +01:00
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