Skip to content

Commit 443b6ce

Browse files
authored
Upgrade Axum to 0.7, tower to 0.5, opentelemetry to 0.23, and hyper to 1.0 (#183)
* refactor(deps): Upgrade deps to support axum 0.7, tower 0.5, opentelemetry 0.23, and hyper 1.0 * style: Run rustfmt on code changes * build: Upgrade minimum rust version to 1.75, required by the updated zip library * build: Fix a few more lints - Allow unused for HeaderValue - Use identity function in map - Dont use default for unit struct - Fix doc indents - tokio-unstable config * build: fix warnings and allow mpl for webpki-roots * refactor: Cleanup differences between axum::http::StatusCode and http::StatusCode * refactor(deps): Upgrade dependency versions for reqwest, reqwest middleware, reqwest retry, reqwest telemetry, etc
1 parent b4cf101 commit 443b6ce

28 files changed

+240
-237
lines changed

Diff for: rust+wasm/.github/workflows/tests_and_checks.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ jobs:
2121
- stable
2222
- nightly
2323
# minimum version
24-
- 1.67
24+
- "1.75"
2525
steps:{% if axum %}
2626
- name: Install Protoc
2727
uses: arduino/setup-protoc@v1

Diff for: rust+wasm/deny.toml

+1
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ exceptions = [
118118
# this is not a problem for us. See https://github.com/dtolnay/unicode-ident/pull/9/files
119119
{ allow = ["Unicode-DFS-2016"], name = "unicode-ident", version = "*"},
120120
{ allow = ["OpenSSL"], name = "ring", version = "*" },
121+
{ allow = ["MPL-2.0"], name = "webpki-roots", version = "*"},
121122
]
122123

123124
# Some crates don't have (easily) machine readable licensing information,

Diff for: rust+wasm/{{project-name}}/Cargo.axum.toml

+21-19
Original file line numberDiff line numberDiff line change
@@ -40,35 +40,37 @@ bench = false
4040
ansi_term = { version = "0.12", optional = true, default-features = false }
4141
anyhow = { version = "1.0", features = ["backtrace"] }
4242
async-trait = "0.1"
43-
axum = { version = "0.6", features = ["headers"] }
44-
axum-tracing-opentelemetry = { version = "0.10", features = ["otlp"] }
43+
axum = { version = "0.7" }
44+
axum-extra = { version = "0.9", features = ["typed-header"] }
45+
axum-tracing-opentelemetry = { version = "0.19" }
4546
base64 = "0.21"
4647
chrono = { version = "0.4", default-features = false, features = ["clock"] }
4748
config = "0.13"
4849
console-subscriber = { version = "0.1", default-features = false, features = [ "parking_lot" ], optional = true }
4950
const_format = "0.2"
5051
futures = "0.3"
51-
headers = "0.3"
52-
http = "0.2"
53-
http-serde = "1.1"
54-
hyper = "0.14"
52+
headers = "0.4"
53+
http = "1.1"
54+
http-serde = "2.1"
55+
hyper = "1.0.1"
5556
metrics = "0.20"
5657
metrics-exporter-prometheus = "0.11"
5758
metrics-util = { version = "0.14", default-features = true }
5859
mime = "0.3"
5960
num_cpus = "1.0"
6061
once_cell = "1.14"
6162
openssl = { version = "0.10", features = ["vendored"], default-features = false }
62-
opentelemetry = { version = "0.18", features = ["rt-tokio", "trace"] }
63-
opentelemetry-otlp = { version = "0.11", features = ["metrics", "grpc-tonic", "tls-roots"], default-features = false }
64-
opentelemetry-semantic-conventions = "0.10"
63+
opentelemetry = { version = "0.23" }
64+
opentelemetry-otlp = { version = "0.16", features = ["metrics", "grpc-tonic", "tls-roots", "trace"], default-features = false }
65+
opentelemetry-semantic-conventions = "0.15"
66+
opentelemetry_sdk = { version = "0.23", features = ["rt-tokio", "trace"] }
6567
parking_lot = "0.12"{% if bench %}
6668
proptest = { version = "1.1", optional = true }{% endif %}
67-
reqwest = { version = "0.11", features = ["json"] }
68-
reqwest-middleware = "0.2"
69-
reqwest-retry = "0.2"
70-
reqwest-tracing = { version = "0.4", features = ["opentelemetry_0_17"] }
71-
retry-policies = "0.1"
69+
reqwest = { version = "0.12", features = ["json"] }
70+
reqwest-middleware = "0.3"
71+
reqwest-retry = "0.6"
72+
reqwest-tracing = { version = "0.5", features = ["opentelemetry_0_23"] }
73+
retry-policies = "0.4"
7274
serde = { version = "1.0", features = ["derive"] }
7375
serde_json = "1.0"
7476
serde_path_to_error = "0.1"
@@ -79,17 +81,17 @@ thiserror = "1.0"
7981
time = { version = "0.3", features = ["serde-well-known", "serde-human-readable"] }
8082
tokio = { version = "1.26", features = ["full", "parking_lot"] }
8183
## Tied to opentelemetry-otlp dependency
82-
tonic = { version = "0.8" }
84+
tonic = { version = "0.11" }
8385
tower = "0.4"
84-
tower-http = { version = "0.4", features = ["catch-panic", "request-id", "sensitive-headers", "timeout", "trace", "util"] }
86+
tower-http = { version = "0.5", features = ["catch-panic", "request-id", "sensitive-headers", "timeout", "trace", "util"] }
8587
tracing = "0.1"
8688
tracing-appender = "0.2"
87-
tracing-opentelemetry = "0.18"
89+
tracing-opentelemetry = "0.24"
8890
tracing-subscriber = { version = "0.3", features = ["env-filter", "json", "parking_lot", "registry"] }
8991
ulid = { version = "1.0", features = ["serde"] }
9092
url = "2.3"
91-
utoipa = { version = "3.3", features = ["uuid", "axum_extras"] }
92-
utoipa-swagger-ui = { version = "3.1", features = ["axum"] }
93+
utoipa = { version = "4.2.3", features = ["uuid", "axum_extras"] }
94+
utoipa-swagger-ui = { version = "7.1.0", features = ["axum"] }
9395

9496
[dev-dependencies]
9597
assert-json-diff = "2.0"{% if bench %}

Diff for: rust+wasm/{{project-name}}/src.axum/error.rs

+6-5
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ use axum::{
55
response::{IntoResponse, Response},
66
Json,
77
};
8-
98
use serde::{Deserialize, Serialize};
109
use tracing::warn;
1110
use ulid::Ulid;
@@ -84,8 +83,8 @@ impl From<AppError> for (StatusCode, Json<ErrorResponse>) {
8483

8584
impl IntoResponse for AppError {
8685
fn into_response(self) -> Response {
87-
let error_response: (StatusCode, Json<ErrorResponse>) = self.into();
88-
error_response.into_response()
86+
let resp: (StatusCode, Json<ErrorResponse>) = self.into();
87+
resp.into_response()
8988
}
9089
}
9190

@@ -115,7 +114,7 @@ impl From<anyhow::Error> for AppError {
115114
///
116115
/// We could have used http_serde, but it encodes the status code as a NUMBER.
117116
pub mod serde_status_code {
118-
use http::StatusCode;
117+
use axum::http::StatusCode;
119118
use serde::{de::Unexpected, Deserialize, Deserializer, Serialize, Serializer};
120119

121120
/// Serialize [StatusCode]s.
@@ -148,7 +147,9 @@ impl std::fmt::Display for AppError {
148147
#[cfg(test)]
149148
/// Parse the app error out of the json body
150149
pub async fn parse_error(response: Response) -> AppError {
151-
let body_bytes = hyper::body::to_bytes(response.into_body()).await.unwrap();
150+
let body_bytes = axum::body::to_bytes(response.into_body(), usize::MAX)
151+
.await
152+
.unwrap();
152153
let mut err_response: ErrorResponse = serde_json::from_slice(&body_bytes).unwrap();
153154
err_response.errors.remove(0)
154155
}

Diff for: rust+wasm/{{project-name}}/src.axum/extract/json.rs

+22-17
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,12 @@
22
33
use async_trait::async_trait;
44
use axum::{
5-
body::{Bytes, HttpBody},
6-
extract::FromRequest,
5+
body::Bytes,
6+
extract::{FromRequest, Request},
7+
http::{header, StatusCode},
78
response::{IntoResponse, Response},
8-
BoxError,
9-
};
10-
use http::{
11-
header::{self, HeaderMap, HeaderValue},
12-
Request, StatusCode,
139
};
10+
use axum_extra::headers::{HeaderMap, HeaderValue};
1411
use serde::{de::DeserializeOwned, Serialize};
1512
use std::ops::{Deref, DerefMut};
1613
use tracing::warn;
@@ -38,6 +35,7 @@ use crate::error::AppError;
3835
/// # Extractor example
3936
///
4037
/// ```rust,no_run
38+
/// use std::net::SocketAddr;
4139
/// use axum::{
4240
/// response,
4341
/// routing::post,
@@ -46,6 +44,8 @@ use crate::error::AppError;
4644
/// use {{crate_name}}::extract;
4745
/// use serde::Deserialize;
4846
///
47+
/// use tokio::net::TcpListener;
48+
///
4949
/// #[derive(Deserialize)]
5050
/// struct CreateUser {
5151
/// email: String,
@@ -58,24 +58,25 @@ use crate::error::AppError;
5858
///
5959
/// let app = Router::new().route("/users", post(create_user));
6060
/// # async {
61-
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
61+
/// # let port = 3000;
62+
/// # let listener = TcpListener::bind(format!("0.0.0.0:{port}")).await.unwrap();
63+
/// # axum::serve(listener, app.into_make_service_with_connect_info::<SocketAddr>())
64+
/// # .await
65+
/// # .unwrap();
6266
/// # };
6367
/// ```
6468
#[derive(Debug, Clone, Copy, Default)]
6569
pub struct Json<T>(pub T);
6670

6771
#[async_trait]
68-
impl<T, S, B> FromRequest<S, B> for Json<T>
72+
impl<T, S> FromRequest<S> for Json<T>
6973
where
7074
T: DeserializeOwned,
71-
B: HttpBody + Send + 'static,
72-
B::Data: Send,
73-
B::Error: Into<BoxError>,
7475
S: Send + Sync,
7576
{
7677
type Rejection = AppError;
7778

78-
async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
79+
async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> {
7980
if json_content_type(req.headers()) {
8081
let bytes = Bytes::from_request(req, state).await.map_err(|err| {
8182
warn!(
@@ -202,9 +203,11 @@ where
202203
#[cfg(test)]
203204
mod tests {
204205
use super::*;
205-
use axum::routing::{get, Router};
206-
use http::Request;
207-
use hyper::Body;
206+
use axum::{
207+
body::Body,
208+
http::Request,
209+
routing::{get, Router},
210+
};
208211
use serde::Deserialize;
209212
use tower::ServiceExt;
210213

@@ -232,7 +235,9 @@ mod tests {
232235
.await
233236
.unwrap();
234237

235-
let body = hyper::body::to_bytes(response.into_body()).await.unwrap();
238+
let body = axum::body::to_bytes(response.into_body(), usize::MAX)
239+
.await
240+
.unwrap();
236241
let body_text = std::str::from_utf8(&body[..]).unwrap();
237242
dbg!(body_text);
238243
assert_eq!(body_text, "bar");

Diff for: rust+wasm/{{project-name}}/src.axum/headers/header.rs

+13-15
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
use axum::{extract::TypedHeader, headers::Header};
1+
use axum_extra::{headers::Header, TypedHeader};
22

33
/// Generate String-focused, generic, custom typed [`Header`]'s.
44
#[allow(unused)]
55
macro_rules! header {
66
($tname:ident, $hname:ident, $sname:expr) => {
7-
static $hname: once_cell::sync::Lazy<axum::headers::HeaderName> =
8-
once_cell::sync::Lazy::new(|| axum::headers::HeaderName::from_static($sname));
7+
static $hname: once_cell::sync::Lazy<axum_extra::headers::HeaderName> =
8+
once_cell::sync::Lazy::new(|| axum_extra::headers::HeaderName::from_static($sname));
99

10-
#[doc = "Generated custom [`axum::headers::Header`] for "]
10+
#[doc = "Generated custom [`axum_extra::headers::Header`] for "]
1111
#[doc = $sname]
1212
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1313
pub(crate) struct $tname(pub(crate) String);
@@ -24,27 +24,27 @@ macro_rules! header {
2424
}
2525
}
2626

27-
impl axum::headers::Header for $tname {
28-
fn name() -> &'static axum::headers::HeaderName {
27+
impl axum_extra::headers::Header for $tname {
28+
fn name() -> &'static axum_extra::headers::HeaderName {
2929
&$hname
3030
}
3131

32-
fn decode<'i, I>(values: &mut I) -> Result<Self, axum::headers::Error>
32+
fn decode<'i, I>(values: &mut I) -> Result<Self, axum_extra::headers::Error>
3333
where
34-
I: Iterator<Item = &'i axum::headers::HeaderValue>,
34+
I: Iterator<Item = &'i axum_extra::headers::HeaderValue>,
3535
{
3636
values
3737
.next()
3838
.and_then(|v| v.to_str().ok())
3939
.map(|x| $tname(x.to_string()))
40-
.ok_or_else(axum::headers::Error::invalid)
40+
.ok_or_else(axum_extra::headers::Error::invalid)
4141
}
4242

4343
fn encode<E>(&self, values: &mut E)
4444
where
45-
E: Extend<axum::headers::HeaderValue>,
45+
E: Extend<axum_extra::headers::HeaderValue>,
4646
{
47-
if let Ok(value) = axum::headers::HeaderValue::from_str(&self.0) {
47+
if let Ok(value) = axum_extra::headers::HeaderValue::from_str(&self.0) {
4848
values.extend(std::iter::once(value));
4949
}
5050
}
@@ -79,10 +79,8 @@ where
7979

8080
#[cfg(test)]
8181
pub(crate) mod tests {
82-
use axum::{
83-
headers::{Header, HeaderMapExt},
84-
http,
85-
};
82+
use axum::http;
83+
use axum_extra::headers::{Header, HeaderMapExt};
8684

8785
header!(XDummyId, XDUMMY_ID, "x-dummy-id");
8886

Diff for: rust+wasm/{{project-name}}/src.axum/main.rs

+22-21
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,14 @@
11
//! {{project-name}}
22
33
use anyhow::Result;
4-
use axum::{extract::Extension, headers::HeaderName, routing::get, Router};
5-
use axum_tracing_opentelemetry::{opentelemetry_tracing_layer, response_with_trace_layer};
4+
use axum::{extract::Extension, routing::get, Router};
5+
use axum_tracing_opentelemetry::middleware::{OtelAxumLayer, OtelInResponseLayer};
6+
use headers::HeaderName;
67
use http::header;
7-
use std::{
8-
future::ready,
9-
io,
10-
net::{IpAddr, Ipv4Addr, SocketAddr},
11-
time::Duration,
12-
};
13-
use tokio::signal::{
14-
self,
15-
unix::{signal, SignalKind},
16-
};
8+
use std::{future::ready, io, iter::once, net::SocketAddr, time::Duration};
9+
#[cfg(unix)]
10+
use tokio::signal::unix::{signal, SignalKind};
11+
use tokio::{net::TcpListener, signal};
1712
use tower::ServiceBuilder;
1813
use tower_http::{
1914
catch_panic::CatchPanicLayer, sensitive_headers::SetSensitiveHeadersLayer,
@@ -83,11 +78,11 @@ async fn main() -> Result<()> {
8378
.route_layer(axum::middleware::from_fn(middleware::metrics::track))
8479
.layer(Extension(env))
8580
// Include trace context as header into the response.
86-
.layer(response_with_trace_layer())
81+
.layer(OtelInResponseLayer)
8782
// Opentelemetry tracing middleware.
8883
// This returns a `TraceLayer` configured to use
8984
// OpenTelemetry’s conventional span field names.
90-
.layer(opentelemetry_tracing_layer())
85+
.layer(OtelAxumLayer::default())
9186
// Set and propagate "request_id" (as a ulid) per request.
9287
.layer(
9388
ServiceBuilder::new()
@@ -103,7 +98,9 @@ async fn main() -> Result<()> {
10398
// `500 Internal Server` responses.
10499
.layer(CatchPanicLayer::custom(runtime::catch_panic))
105100
// Mark headers as sensitive on both requests and responses.
106-
.layer(SetSensitiveHeadersLayer::new([header::AUTHORIZATION]))
101+
.layer(SetSensitiveHeadersLayer::new(once(
102+
HeaderName::from_static(header::AUTHORIZATION.as_str()),
103+
)))
107104
.merge(SwaggerUi::new("/swagger-ui").url("/api-doc/openapi.json", ApiDoc::openapi()));
108105

109106
serve("Application", router, settings.server().port).await
@@ -114,19 +111,23 @@ async fn main() -> Result<()> {
114111
}
115112

116113
async fn serve(name: &str, app: Router, port: u16) -> Result<()> {
117-
let bind_addr: SocketAddr = SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), port);
114+
// let bind_addr: SocketAddr = SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), port);
115+
let listener = TcpListener::bind(format!("0.0.0.0:{port}")).await.unwrap();
118116
info!(
119117
subject = "app_start",
120118
category = "init",
121119
"{} server listening on {}",
122120
name,
123-
bind_addr
121+
port
124122
);
125123

126-
axum::Server::bind(&bind_addr)
127-
.serve(app.into_make_service_with_connect_info::<SocketAddr>())
128-
.with_graceful_shutdown(shutdown())
129-
.await?;
124+
axum::serve(
125+
listener,
126+
app.into_make_service_with_connect_info::<SocketAddr>(),
127+
)
128+
.with_graceful_shutdown(shutdown())
129+
.await
130+
.unwrap();
130131

131132
Ok(())
132133
}

Diff for: rust+wasm/{{project-name}}/src.axum/middleware/client/metrics.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
//! Middleware for tracking metrics on each client [reqwest::Request].
22
3+
use http::Extensions;
34
use reqwest_middleware::Middleware as ReqwestMiddleware;
45
use std::time::Instant;
5-
use task_local_extensions::Extensions;
66

77
const OK: &str = "ok";
88
const ERROR: &str = "error";

0 commit comments

Comments
 (0)