Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backports to 0.6.x #2098

Merged
merged 21 commits into from
Jul 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
4ce5a32
Fix a typo in `axum::extract::rejection::FailedToDeserializeQueryStri…
j3rrywan9 May 19, 2023
31229cf
docs: add warning icon for extractor order (#2027)
azzamsa Jun 5, 2023
1a9fc5f
Fix a typo in `axum::middleware` (#2056)
cyril-marpaud Jun 22, 2023
3bd32bd
docs: update router::route multiple methods docs (#2051)
mohad12211 Jun 22, 2023
095bf2f
docs: Add links to more examples of Result-returning handlers. (#2049)
vi Jun 22, 2023
16b6388
Fix `.source()` of composite rejections (#2030)
SabrinaJewson Jun 22, 2023
cf83028
docs: Remove explicit auto deref from PrivateCookieJar example (#2028)
tyilo Jun 22, 2023
1fb9e0e
Allow unreachable code in `#[debug_handler]` (#2014)
alexander-jackson Jun 22, 2023
357adb0
Update tokio-tungstenite to 0.19 (#2021)
alexheretic Jun 22, 2023
7dec5c0
Implement `IntoResponse` for boxed slices (#2035)
alvra Jun 26, 2023
cf0ccec
Fix some typos in the docs
davidpdrsn Jul 3, 2023
ed2578e
Add `axum::extract::Query::try_from_uri` (#2058)
dobecad Jul 3, 2023
98eec13
Fix typo in docs (#2080)
z-o-n-n-e Jul 9, 2023
959f4d0
Fix example for accessing inner extrator errors (#2095)
davidpdrsn Jul 15, 2023
bb406b1
Fix bugs around merging routers with nested fallbacks (#2096)
davidpdrsn Jul 16, 2023
60eb560
Add missing changes to changelog
davidpdrsn Jul 16, 2023
24850c7
Fix misc warnings
davidpdrsn Jul 16, 2023
2e7ea31
Remove sessions example
davidpdrsn Jul 16, 2023
67d90a2
Fix deny.toml
davidpdrsn Jul 16, 2023
fd54d1a
Update UI tests
davidpdrsn Jul 16, 2023
feaa86d
Update to latest sqlx in example (#2099)
davidpdrsn Jul 16, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: CI

env:
CARGO_TERM_COLOR: always
MSRV: '1.60'
MSRV: '1.63'

on:
push:
Expand Down
42 changes: 41 additions & 1 deletion axum-core/src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,14 +203,54 @@ macro_rules! __composite_rejection {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
$(
Self::$variant(inner) => Some(inner),
Self::$variant(inner) => inner.source(),
)+
}
}
}
};
}

#[cfg(test)]
mod composite_rejection_tests {
use self::defs::*;
use crate::Error;
use std::error::Error as _;

#[allow(dead_code, unreachable_pub)]
mod defs {
use crate::{__composite_rejection, __define_rejection};

__define_rejection! {
#[status = BAD_REQUEST]
#[body = "error message 1"]
pub struct Inner1;
}
__define_rejection! {
#[status = BAD_REQUEST]
#[body = "error message 2"]
pub struct Inner2(Error);
}
__composite_rejection! {
pub enum Outer { Inner1, Inner2 }
}
}

/// The implementation of `.source()` on `Outer` should defer straight to the implementation
/// on its inner type instead of returning the inner type itself, because the `Display`
/// implementation on `Outer` already forwards to the inner type and so it would result in two
/// errors in the chain `Display`ing the same thing.
#[test]
fn source_gives_inner_source() {
let rejection = Outer::Inner1(Inner1);
assert!(rejection.source().is_none());

let msg = "hello world";
let rejection = Outer::Inner2(Inner2(Error::new(msg)));
assert_eq!(rejection.source().unwrap().to_string(), msg);
}
}

#[rustfmt::skip]
macro_rules! all_the_tuples {
($name:ident) => {
Expand Down
12 changes: 12 additions & 0 deletions axum-core/src/response/into_response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,12 @@ impl IntoResponse for String {
}
}

impl IntoResponse for Box<str> {
fn into_response(self) -> Response {
String::from(self).into_response()
}
}

impl IntoResponse for Cow<'static, str> {
fn into_response(self) -> Response {
let mut res = Full::from(self).into_response();
Expand Down Expand Up @@ -366,6 +372,12 @@ impl IntoResponse for Vec<u8> {
}
}

impl IntoResponse for Box<[u8]> {
fn into_response(self) -> Response {
Vec::from(self).into_response()
}
}

impl IntoResponse for Cow<'static, [u8]> {
fn into_response(self) -> Response {
let mut res = Full::from(self).into_response();
Expand Down
4 changes: 3 additions & 1 deletion axum-extra/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning].

# Unreleased

- None.
- **fixed:** Remove explicit auto deref from PrivateCookieJar example ([#2028])

[#2028]: https://github.com/tokio-rs/axum/pull/2028

# 0.7.4 (18. April, 2023)

Expand Down
2 changes: 1 addition & 1 deletion axum-extra/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
categories = ["asynchronous", "network-programming", "web-programming"]
description = "Extra utilities for axum"
edition = "2021"
rust-version = "1.60"
rust-version = "1.63"
homepage = "https://github.com/tokio-rs/axum"
keywords = ["http", "web", "framework"]
license = "MIT"
Expand Down
2 changes: 1 addition & 1 deletion axum-extra/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ This crate uses `#![forbid(unsafe_code)]` to ensure everything is implemented in

## Minimum supported Rust version

axum-extra's MSRV is 1.60.
axum-extra's MSRV is 1.63.

## Getting Help

Expand Down
2 changes: 1 addition & 1 deletion axum-extra/src/extract/cookie/private.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ use std::{convert::Infallible, fmt, marker::PhantomData};
/// type Target = InnerState;
///
/// fn deref(&self) -> &Self::Target {
/// &*self.0
/// &self.0
/// }
/// }
///
Expand Down
2 changes: 1 addition & 1 deletion axum-extra/src/extract/multipart.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use std::{

/// Extractor that parses `multipart/form-data` requests (commonly used with file uploads).
///
/// Since extracting multipart form data from the request requires consuming the body, the
/// ⚠️ Since extracting multipart form data from the request requires consuming the body, the
/// `Multipart` extractor must be *last* if there are multiple extractors in a handler.
/// See ["the order of extractors"][order-of-extractors]
///
Expand Down
2 changes: 1 addition & 1 deletion axum-extra/src/json_lines.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ mod tests {
let res = client
.post("/")
.body(
vec![
[
"{\"id\":1}",
"{\"id\":2}",
"{\"id\":3}",
Expand Down
4 changes: 3 additions & 1 deletion axum-macros/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

# Unreleased

- None.
- **fixed:** Allow unreachable code in `#[debug_handler]` ([#2014])

[#2014]: https://github.com/tokio-rs/axum/pull/2014

# 0.3.7 (22. March, 2023)

Expand Down
2 changes: 1 addition & 1 deletion axum-macros/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ This crate uses `#![forbid(unsafe_code)]` to ensure everything is implemented in

## Minimum supported Rust version

axum-macros's MSRV is 1.60.
axum-macros's MSRV is 1.63.

## Getting Help

Expand Down
8 changes: 8 additions & 0 deletions axum-macros/src/debug_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ fn check_inputs_impls_from_request(

quote_spanned! {span=>
#[allow(warnings)]
#[allow(unreachable_code)]
#[doc(hidden)]
fn #check_fn #check_fn_generics()
where
Expand All @@ -285,6 +286,7 @@ fn check_inputs_impls_from_request(
// we have to call the function to actually trigger a compile error
// since the function is generic, just defining it is not enough
#[allow(warnings)]
#[allow(unreachable_code)]
#[doc(hidden)]
fn #call_check_fn()
{
Expand Down Expand Up @@ -429,6 +431,7 @@ fn check_output_impls_into_response(item_fn: &ItemFn) -> TokenStream {
let make = if item_fn.sig.asyncness.is_some() {
quote_spanned! {span=>
#[allow(warnings)]
#[allow(unreachable_code)]
#[doc(hidden)]
async fn #make_value_name() -> #ty {
#declare_inputs
Expand All @@ -438,6 +441,7 @@ fn check_output_impls_into_response(item_fn: &ItemFn) -> TokenStream {
} else {
quote_spanned! {span=>
#[allow(warnings)]
#[allow(unreachable_code)]
#[doc(hidden)]
fn #make_value_name() -> #ty {
#declare_inputs
Expand All @@ -453,6 +457,7 @@ fn check_output_impls_into_response(item_fn: &ItemFn) -> TokenStream {
#make

#[allow(warnings)]
#[allow(unreachable_code)]
#[doc(hidden)]
async fn #name() {
let value = #receiver #make_value_name().await;
Expand All @@ -465,6 +470,7 @@ fn check_output_impls_into_response(item_fn: &ItemFn) -> TokenStream {
} else {
quote_spanned! {span=>
#[allow(warnings)]
#[allow(unreachable_code)]
#[doc(hidden)]
async fn #name() {
#make
Expand Down Expand Up @@ -515,6 +521,7 @@ fn check_future_send(item_fn: &ItemFn) -> TokenStream {
if let Some(receiver) = self_receiver(item_fn) {
quote! {
#[allow(warnings)]
#[allow(unreachable_code)]
#[doc(hidden)]
fn #name() {
let future = #receiver #handler_name(#(#args),*);
Expand All @@ -524,6 +531,7 @@ fn check_future_send(item_fn: &ItemFn) -> TokenStream {
} else {
quote! {
#[allow(warnings)]
#[allow(unreachable_code)]
#[doc(hidden)]
fn #name() {
#item_fn
Expand Down
8 changes: 8 additions & 0 deletions axum-macros/tests/debug_handler/pass/deny_unreachable_code.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#![deny(unreachable_code)]

use axum::extract::Path;

#[axum_macros::debug_handler]
async fn handler(Path(_): Path<String>) {}

fn main() {}
2 changes: 1 addition & 1 deletion axum-macros/tests/typed_path/fail/not_deserialize.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ error[E0277]: the trait bound `for<'de> MyPath: serde::de::Deserialize<'de>` is
(T0, T1)
(T0, T1, T2)
(T0, T1, T2, T3)
and 129 others
and $N others
= note: required for `MyPath` to implement `serde::de::DeserializeOwned`
= note: required for `axum::extract::Path<MyPath>` to implement `FromRequestParts<S>`
= note: this error originates in the derive macro `TypedPath` (in Nightly builds, run with -Z macro-backtrace for more info)
15 changes: 14 additions & 1 deletion axum/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

# Unreleased

- None.
- **added:** Add `axum::extract::Query::try_from_uri` ([#2058])
- **added:** Implement `IntoResponse` for `Box<str>` and `Box<[u8]>` ([#2035])
- **fixed:** Fix bugs around merging routers with nested fallbacks ([#2096])
- **fixed:** Fix `.source()` of composite rejections ([#2030])
- **fixed:** Allow unreachable code in `#[debug_handler]` ([#2014])
- **change:** Update tokio-tungstenite to 0.19 ([#2021])
- **change:** axum's MSRV is now 1.63 ([#2021])

[#2014]: https://github.com/tokio-rs/axum/pull/2014
[#2021]: https://github.com/tokio-rs/axum/pull/2021
[#2030]: https://github.com/tokio-rs/axum/pull/2030
[#2035]: https://github.com/tokio-rs/axum/pull/2035
[#2058]: https://github.com/tokio-rs/axum/pull/2058
[#2096]: https://github.com/tokio-rs/axum/pull/2096

# 0.6.18 (30. April, 2023)

Expand Down
4 changes: 2 additions & 2 deletions axum/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ version = "0.6.18"
categories = ["asynchronous", "network-programming", "web-programming::http-server"]
description = "Web framework that focuses on ergonomics and modularity"
edition = "2021"
rust-version = "1.60"
rust-version = "1.63"
homepage = "https://github.com/tokio-rs/axum"
keywords = ["http", "web", "framework"]
license = "MIT"
Expand Down Expand Up @@ -61,7 +61,7 @@ serde_path_to_error = { version = "0.1.8", optional = true }
serde_urlencoded = { version = "0.7", optional = true }
sha1 = { version = "0.10", optional = true }
tokio = { package = "tokio", version = "1.25.0", features = ["time"], optional = true }
tokio-tungstenite = { version = "0.18.0", optional = true }
tokio-tungstenite = { version = "0.19", optional = true }
tracing = { version = "0.1", default-features = false, optional = true }

[dependencies.tower-http]
Expand Down
2 changes: 1 addition & 1 deletion axum/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ This crate uses `#![forbid(unsafe_code)]` to ensure everything is implemented in

## Minimum supported Rust version

axum's MSRV is 1.60.
axum's MSRV is 1.63.

## Examples

Expand Down
10 changes: 10 additions & 0 deletions axum/src/docs/error_handling.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,16 @@ It doesn't matter whether you return `Err(StatusCode::NOT_FOUND)` or
`Err(StatusCode::INTERNAL_SERVER_ERROR)`. These are not considered errors in
axum.

Instead of a direct `StatusCode`, it makes sense to use intermediate error type
that can ultimately be converted to `Response`. This allows using `?` operator
in handlers. See those examples:

* [`anyhow-error-response`][anyhow] for generic boxed errors
* [`error-handling-and-dependency-injection`][ehdi] for application-specific detailed errors

[anyhow]:https://github.com/tokio-rs/axum/blob/main/examples/anyhow-error-response/src/main.rs
[ehdi]:https://github.com/tokio-rs/axum/blob/main/examples/error-handling-and-dependency-injection/src/main.rs

This also applies to extractors. If an extractor doesn't match the request the
request will be rejected and a response will be returned without calling your
handler. See [`extract`](crate::extract) to learn more about handling extractor
Expand Down
27 changes: 24 additions & 3 deletions axum/src/docs/extract.md
Original file line number Diff line number Diff line change
Expand Up @@ -366,13 +366,16 @@ async fn handler(
}
}

// attempt to extract the inner `serde_json::Error`, if that succeeds we can
// provide a more specific error
// attempt to extract the inner `serde_path_to_error::Error<serde_json::Error>`,
// if that succeeds we can provide a more specific error.
//
// `Json` uses `serde_path_to_error` so the error will be wrapped in `serde_path_to_error::Error`.
fn serde_json_error_response<E>(err: E) -> (StatusCode, String)
where
E: Error + 'static,
{
if let Some(serde_json_err) = find_error_source::<serde_json::Error>(&err) {
if let Some(err) = find_error_source::<serde_path_to_error::Error<serde_json::Error>>(&err) {
let serde_json_err = err.inner();
(
StatusCode::BAD_REQUEST,
format!(
Expand Down Expand Up @@ -400,6 +403,24 @@ where
None
}
}
#
# #[tokio::main]
# async fn main() {
# use axum::extract::FromRequest;
#
# let req = axum::http::Request::builder()
# .header("content-type", "application/json")
# .body(axum::body::Body::from("{"))
# .unwrap();
#
# let err = match Json::<serde_json::Value>::from_request(req, &()).await.unwrap_err() {
# JsonRejection::JsonSyntaxError(err) => err,
# _ => panic!(),
# };
#
# let (_, body) = serde_json_error_response(err);
# assert_eq!(body, "Invalid JSON at line 1 column 1");
# }
```

Note that while this approach works it might break in the future if axum changes
Expand Down
2 changes: 1 addition & 1 deletion axum/src/docs/middleware.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ That is:
- It then does its thing and passes the request onto `layer_two`
- Which passes the request onto `layer_one`
- Which passes the request onto `handler` where a response is produced
- That response is then passes to `layer_one`
- That response is then passed to `layer_one`
- Then to `layer_two`
- And finally to `layer_three` where it's returned out of your app

Expand Down
2 changes: 1 addition & 1 deletion axum/src/docs/routing/merge.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ let app = Router::new()
// Our app now accepts
// - GET /users
// - GET /users/:id
// - POST /teams
// - GET /teams
# async {
# hyper::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
# };
Expand Down
Loading
Loading