Skip to content

Commit

Permalink
Merge branch 'main' into david/axum-core-rust-doc-features
Browse files Browse the repository at this point in the history
  • Loading branch information
davidpdrsn committed Sep 16, 2023
2 parents 87d3d85 + 65defdb commit f49fd4a
Show file tree
Hide file tree
Showing 27 changed files with 308 additions and 44 deletions.
2 changes: 2 additions & 0 deletions ECOSYSTEM.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ If your project isn't listed here and you would like it to be, please feel free
- [tower-governor](https://crates.io/crates/tower_governor): A Tower service and layer that provides a rate-limiting backend by [governor](https://crates.io/crates/governor)
- [axum-restful](https://github.com/gongzhengyang/axum-restful): A restful framework based on axum and sea-orm, inspired by django-rest-framework.
- [springtime-web-axum](https://crates.io/crates/springtime-web-axum): A web framework built on Springtime and axum, leveraging dependency injection for easy app development.
- [rust-axum-with-google-oauth](https://github.com/randommm/rust-axum-with-google-oauth): website template for Google OAuth authentication on Axum, using SQLite with SQLx or MongoDB and MiniJinja.
- [axum-htmx](https://github.com/robertwayne/axum-htmx): Htmx extractors and request guards for axum.

## Project showcase

Expand Down
1 change: 1 addition & 0 deletions axum-extra/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning].

- **added:** `TypedHeader` which used to be in `axum` ([#1850])
- **added:** `Clone` implementation for `ErasedJson` ([#2142])
- **breaking:** Update to prost 0.12. Used for the `Protobuf` extractor

[#1850]: https://github.com/tokio-rs/axum/pull/1850
[#2142]: https://github.com/tokio-rs/axum/pull/2142
Expand Down
2 changes: 1 addition & 1 deletion axum-extra/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ form_urlencoded = { version = "1.1.0", optional = true }
headers = { version = "0.3.8", optional = true }
multer = { version = "2.0.0", optional = true }
percent-encoding = { version = "2.1", optional = true }
prost = { version = "0.11", optional = true }
prost = { version = "0.12", optional = true }
serde_html_form = { version = "0.2.0", optional = true }
serde_json = { version = "1.0.71", optional = true }
tokio-stream = { version = "0.1.9", optional = true }
Expand Down
23 changes: 10 additions & 13 deletions axum-extra/src/routing/resource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,50 +164,47 @@ mod tests {
.update(|Path(id): Path<u64>| async move { format!("users#update id={id}") })
.destroy(|Path(id): Path<u64>| async move { format!("users#destroy id={id}") });

let mut app = Router::new().merge(users);
let app = Router::new().merge(users);

assert_eq!(
call_route(&mut app, Method::GET, "/users").await,
"users#index"
);
assert_eq!(call_route(&app, Method::GET, "/users").await, "users#index");

assert_eq!(
call_route(&mut app, Method::POST, "/users").await,
call_route(&app, Method::POST, "/users").await,
"users#create"
);

assert_eq!(
call_route(&mut app, Method::GET, "/users/new").await,
call_route(&app, Method::GET, "/users/new").await,
"users#new"
);

assert_eq!(
call_route(&mut app, Method::GET, "/users/1").await,
call_route(&app, Method::GET, "/users/1").await,
"users#show id=1"
);

assert_eq!(
call_route(&mut app, Method::GET, "/users/1/edit").await,
call_route(&app, Method::GET, "/users/1/edit").await,
"users#edit id=1"
);

assert_eq!(
call_route(&mut app, Method::PATCH, "/users/1").await,
call_route(&app, Method::PATCH, "/users/1").await,
"users#update id=1"
);

assert_eq!(
call_route(&mut app, Method::PUT, "/users/1").await,
call_route(&app, Method::PUT, "/users/1").await,
"users#update id=1"
);

assert_eq!(
call_route(&mut app, Method::DELETE, "/users/1").await,
call_route(&app, Method::DELETE, "/users/1").await,
"users#destroy id=1"
);
}

async fn call_route(app: &mut Router, method: Method, uri: &str) -> String {
async fn call_route(app: &Router, method: Method, uri: &str) -> String {
let res = app
.clone()
.oneshot(
Expand Down
2 changes: 1 addition & 1 deletion axum-macros/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 = "Macros for axum"
edition = "2021"
rust-version = "1.60"
rust-version = "1.63"
homepage = "https://github.com/tokio-rs/axum"
keywords = ["axum"]
license = "MIT"
Expand Down
2 changes: 1 addition & 1 deletion axum-macros/src/axum_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ fn replace_nest_with_nest_service(mut item_fn: ItemFn) -> Option<ItemFn> {
let mut visitor = NestToNestService::default();
syn::visit_mut::visit_item_fn_mut(&mut visitor, &mut item_fn);

(visitor.count > 0).then(|| item_fn)
(visitor.count > 0).then_some(item_fn)
}

#[derive(Default)]
Expand Down
5 changes: 3 additions & 2 deletions axum/src/docs/method_routing/route_layer.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ Apply a [`tower::Layer`] to the router that will only run if the request matches
a route.

Note that the middleware is only applied to existing routes. So you have to
first add your routes (and / or fallback) and then call `layer` afterwards. Additional
routes added after `layer` is called will not have the middleware added.
first add your routes (and / or fallback) and then call `route_layer`
afterwards. Additional routes added after `route_layer` is called will not have
the middleware added.

This works similarly to [`MethodRouter::layer`] except the middleware will only run if
the request matches a route. This is useful for middleware that return early
Expand Down
5 changes: 3 additions & 2 deletions axum/src/docs/routing/route_layer.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ Apply a [`tower::Layer`] to the router that will only run if the request matches
a route.

Note that the middleware is only applied to existing routes. So you have to
first add your routes (and / or fallback) and then call `layer` afterwards. Additional
routes added after `layer` is called will not have the middleware added.
first add your routes (and / or fallback) and then call `route_layer`
afterwards. Additional routes added after `route_layer` is called will not have
the middleware added.

This works similarly to [`Router::layer`] except the middleware will only run if
the request matches a route. This is useful for middleware that return early
Expand Down
2 changes: 1 addition & 1 deletion axum/src/extract/ws.rs
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ impl<F> WebSocketUpgrade<F> {

/// Finalize upgrading the connection and call the provided callback with
/// the stream.
#[must_use = "to setup the WebSocket connection, this response must be returned"]
#[must_use = "to set up the WebSocket connection, this response must be returned"]
pub fn on_upgrade<C, Fut>(self, callback: C) -> Response
where
C: FnOnce(WebSocket) -> Fut + Send + 'static,
Expand Down
1 change: 0 additions & 1 deletion axum/src/hyper1_tokio_io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,6 @@ where
cx: &mut Context<'_>,
tbuf: &mut tokio::io::ReadBuf<'_>,
) -> Poll<Result<(), std::io::Error>> {
//let init = tbuf.initialized().len();
let filled = tbuf.filled().len();
let sub_filled = unsafe {
let mut buf = hyper::rt::ReadBuf::uninit(tbuf.unfilled_mut());
Expand Down
2 changes: 1 addition & 1 deletion axum/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
//!
//! # Routing
//!
//! [`Router`] is used to setup which paths goes to which services:
//! [`Router`] is used to set up which paths goes to which services:
//!
//! ```rust
//! use axum::{Router, routing::get};
Expand Down
12 changes: 3 additions & 9 deletions axum/src/response/sse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -382,25 +382,19 @@ impl EventFlags {
const HAS_ID: Self = Self::from_bits(0b1000);

const fn bits(&self) -> u8 {
let bits = self;
bits.0
self.0
}

const fn from_bits(bits: u8) -> Self {
let bits = bits;
Self(bits)
}

const fn contains(&self, other: Self) -> bool {
let same = self;
let other = other;
same.bits() & other.bits() == other.bits()
self.bits() & other.bits() == other.bits()
}

fn insert(&mut self, other: Self) {
let same = self;
let other = other;
*same = Self::from_bits(same.bits() | other.bits());
*self = Self::from_bits(self.bits() | other.bits());
}
}

Expand Down
5 changes: 1 addition & 4 deletions axum/src/routing/method_filter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,11 @@ impl MethodFilter {
}

const fn from_bits(bits: u16) -> Self {
let bits = bits;
Self(bits)
}

pub(crate) const fn contains(&self, other: Self) -> bool {
let same = self;
let other = other;
same.bits() & other.bits() == other.bits()
self.bits() & other.bits() == other.bits()
}

/// Performs the OR operation between the [`MethodFilter`] in `self` with `other`.
Expand Down
5 changes: 3 additions & 2 deletions axum/src/routing/method_routing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -604,9 +604,10 @@ where
}

impl MethodRouter<(), Infallible> {
/// Convert the handler into a [`MakeService`].
/// Convert the router into a [`MakeService`].
///
/// This allows you to serve a single handler if you don't need any routing:
/// This allows you to serve a single `MethodRouter` if you don't need any
/// routing based on the path:
///
/// ```rust
/// use axum::{
Expand Down
3 changes: 1 addition & 2 deletions axum/src/routing/path_router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,6 @@ where

validate_path(path)?;

let id = self.next_route_id();

let endpoint = if let Some((route_id, Endpoint::MethodRouter(prev_method_router))) = self
.node
.path_to_route_id
Expand All @@ -74,6 +72,7 @@ where
Endpoint::MethodRouter(method_router)
};

let id = self.next_route_id();
self.set_node(path, id)?;
self.routes.insert(id, endpoint);

Expand Down
2 changes: 1 addition & 1 deletion axum/src/serve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ where
{
Ok(()) => {}
Err(_err) => {
// This error only appears when the client doesn't send a request and
// This error only appears when the client doesn't send a request and
// terminate the connection.
//
// If client sends one request then terminate connection whenever, it doesn't
Expand Down
2 changes: 2 additions & 0 deletions deny.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ skip-tree = [
{ name = "regex-syntax" },
# pulled in by tracing-subscriber
{ name = "regex-automata" },
# pulled in by hyper
{ name = "socket2" },
]

[sources]
Expand Down
2 changes: 1 addition & 1 deletion examples/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Examples

This folder contains numerous example showing how to use axum. Each example is
This folder contains numerous examples showing how to use axum. Each example is
setup as its own crate so its dependencies are clear.

For a list of what the community built with axum, please see the list
Expand Down
10 changes: 10 additions & 0 deletions examples/auto-reload/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[package]
name = "auto-reload"
version = "0.1.0"
edition = "2021"
publish = false

[dependencies]
axum = { path = "../../axum" }
listenfd = "1.0.1"
tokio = { version = "1.0", features = ["full"] }
18 changes: 18 additions & 0 deletions examples/auto-reload/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# auto-reload

This example shows how you can set up a development environment for your axum
service such that whenever the source code changes, the app is recompiled and
restarted. It uses `listenfd` to be able to migrate connections from an old
version of the app to a newly-compiled version.

## Setup

```sh
cargo install cargo-watch systemfd
```

## Running

```sh
systemfd --no-pid -s http::3000 -- cargo watch -x run
```
31 changes: 31 additions & 0 deletions examples/auto-reload/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//! Run with
//!
//! ```not_rust
//! cargo run -p example-hello-world
//! ```
use axum::{response::Html, routing::get, Router};
use listenfd::ListenFd;
use tokio::net::TcpListener;

#[tokio::main]
async fn main() {
// build our application with a route
let app = Router::new().route("/", get(handler));

let mut listenfd = ListenFd::from_env();
let listener = match listenfd.take_tcp_listener(0).unwrap() {
// if we are given a tcp listener on listen fd 0, we use that one
Some(listener) => TcpListener::from_std(listener).unwrap(),
// otherwise fall back to local listening
None => TcpListener::bind("127.0.0.1:3000").await.unwrap(),
};

// run it
println!("listening on {}", listener.local_addr().unwrap());
axum::serve(listener, app).await.unwrap();
}

async fn handler() -> Html<&'static str> {
Html("<h1>Hello, World!</h1>")
}
11 changes: 10 additions & 1 deletion examples/customize-extractor-error/src/derive_from_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,26 @@ use axum::{
extract::rejection::JsonRejection, extract::FromRequest, http::StatusCode,
response::IntoResponse,
};
use serde::Serialize;
use serde_json::{json, Value};

pub async fn handler(Json(value): Json<Value>) -> impl IntoResponse {
Json(dbg!(value));
Json(dbg!(value))
}

// create an extractor that internally uses `axum::Json` but has a custom rejection
#[derive(FromRequest)]
#[from_request(via(axum::Json), rejection(ApiError))]
pub struct Json<T>(T);

// We implement `IntoResponse` for our extractor so it can be used as a response
impl<T: Serialize> IntoResponse for Json<T> {
fn into_response(self) -> axum::response::Response {
let Self(value) = self;
axum::Json(value).into_response()
}
}

// We create our own rejection type
#[derive(Debug)]
pub struct ApiError {
Expand Down
2 changes: 1 addition & 1 deletion examples/sqlx-postgres/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ async fn main() {
let db_connection_str = std::env::var("DATABASE_URL")
.unwrap_or_else(|_| "postgres://postgres:password@localhost".to_string());

// setup connection pool
// set up connection pool
let pool = PgPoolOptions::new()
.max_connections(5)
.acquire_timeout(Duration::from_secs(3))
Expand Down
13 changes: 13 additions & 0 deletions examples/tls-graceful-shutdown/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "example-tls-graceful-shutdown"
version = "0.1.0"
edition = "2021"
publish = false

[dependencies]
axum = { path = "../../axum" }
axum-server = { version = "0.3", features = ["tls-rustls"] }
hyper = { version = "0.14", features = ["full"] }
tokio = { version = "1", features = ["full"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
22 changes: 22 additions & 0 deletions examples/tls-graceful-shutdown/self_signed_certs/cert.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
-----BEGIN CERTIFICATE-----
MIIDkzCCAnugAwIBAgIUXVYkRCrM/ge03DVymDtXCuybp7gwDQYJKoZIhvcNAQEL
BQAwWTELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MB4X
DTIxMDczMTE0MjIxMloXDTIyMDczMTE0MjIxMlowWTELMAkGA1UEBhMCVVMxEzAR
BgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5
IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEA02V5ZjmqLB/VQwTarrz/35qsa83L+DbAoa0001+jVmmC+G9Nufi0
daroFWj/Uicv2fZWETU8JoZKUrX4BK9og5cg5rln/CtBRWCUYIwRgY9R/CdBGPn4
kp+XkSJaCw74ZIyLy/Zfux6h8ES1m9YRnBza+s7U+ImRBRf4MRPtXQ3/mqJxAZYq
dOnKnvssRyD2qutgVTAxwMUvJWIivRhRYDj7WOpS4CEEeQxP1iH1/T5P7FdtTGdT
bVBABCA8JhL96uFGPpOYHcM/7R5EIA3yZ5FNg931QzoDITjtXGtQ6y9/l/IYkWm6
J67RWcN0IoTsZhz0WNU4gAeslVtJLofn8QIDAQABo1MwUTAdBgNVHQ4EFgQUzFnK
NfS4LAYuKeWwHbzooER0yZ0wHwYDVR0jBBgwFoAUzFnKNfS4LAYuKeWwHbzooER0
yZ0wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAk4O+e9jia59W
ZwetN4GU7OWcYhmOgSizRSs6u7mTfp62LDMt96WKU3THksOnZ44HnqWQxsSfdFVU
XJD12tjvVU8Z4FWzQajcHeemUYiDze8EAh6TnxnUcOrU8IcwiKGxCWRY/908jnWg
+MMscfMCMYTRdeTPqD8fGzAlUCtmyzH6KLE3s4Oo/r5+NR+Uvrwpdvb7xe0MwwO9
Q/zR4N8ep/HwHVEObcaBofE1ssZLksX7ZgCP9wMgXRWpNAtC5EWxMbxYjBfWFH24
fDJlBMiGJWg8HHcxK7wQhFh+fuyNzE+xEWPsI9VL1zDftd9x8/QsOagyEOnY8Vxr
AopvZ09uEQ==
-----END CERTIFICATE-----
Loading

0 comments on commit f49fd4a

Please sign in to comment.