From 8886b83d62a051fdfd230f5539215ac83a2144f5 Mon Sep 17 00:00:00 2001 From: Denis Cornehl Date: Wed, 1 Jan 2025 17:39:54 +0100 Subject: [PATCH] upgrade axum to 0.8 --- Cargo.lock | 83 +++++++++++++------------------- Cargo.toml | 7 +-- src/repositories/github.rs | 2 +- src/repositories/gitlab.rs | 2 +- src/repositories/updater.rs | 2 +- src/web/extractors.rs | 25 ++++++++-- src/web/metrics.rs | 17 ++++--- src/web/routes.rs | 94 +++++++++++++++++++------------------ src/web/statics.rs | 3 +- 9 files changed, 120 insertions(+), 115 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 051090efe..fdd536a2b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -696,14 +696,14 @@ dependencies = [ [[package]] name = "axum" -version = "0.7.9" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" +checksum = "6d6fd624c75e18b3b4c6b9caf42b1afe24437daaee904069137d8bab077be8b8" dependencies = [ - "async-trait", "axum-core", "axum-macros", "bytes", + "form_urlencoded", "futures-util", "http 1.2.0", "http-body 1.0.1", @@ -731,11 +731,10 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.4.5" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" +checksum = "df1362f362fd16024ae199c1970ce98f9661bf5ef94b9808fee734bc3698b733" dependencies = [ - "async-trait", "bytes", "futures-util", "http 1.2.0", @@ -752,21 +751,19 @@ dependencies = [ [[package]] name = "axum-extra" -version = "0.9.6" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c794b30c904f0a1c2fb7740f7df7f7972dfaa14ef6f57cb6178dc63e5dca2f04" +checksum = "460fc6f625a1f7705c6cf62d0d070794e94668988b1c38111baeec177c715f7b" dependencies = [ "axum", "axum-core", "bytes", - "fastrand", "futures-util", "headers", "http 1.2.0", "http-body 1.0.1", "http-body-util", "mime", - "multer", "pin-project-lite", "serde", "tower", @@ -776,9 +773,9 @@ dependencies = [ [[package]] name = "axum-macros" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d123550fa8d071b7255cb0cc04dc302baa6c8c4a79f55701552684d8399bce" +checksum = "604fde5e028fea851ce1d8570bbdc034bec850d157f7569d10f347d06808c05c" dependencies = [ "proc-macro2", "quote", @@ -1669,6 +1666,7 @@ version = "0.6.0" dependencies = [ "anyhow", "async-stream", + "async-trait", "aws-config", "aws-sdk-cloudfront", "aws-sdk-s3", @@ -4375,9 +4373,9 @@ checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" [[package]] name = "matchit" -version = "0.7.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" [[package]] name = "maybe-async" @@ -4481,23 +4479,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "multer" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b" -dependencies = [ - "bytes", - "encoding_rs", - "futures-util", - "http 1.2.0", - "httparse", - "memchr", - "mime", - "spin", - "version_check", -] - [[package]] name = "native-tls" version = "0.2.12" @@ -5814,9 +5795,9 @@ dependencies = [ [[package]] name = "sentry" -version = "0.35.0" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "016958f51b96861dead7c1e02290f138411d05e94fad175c8636a835dee6e51e" +checksum = "3a7332159e544e34db06b251b1eda5e546bd90285c3f58d9c8ff8450b484e0da" dependencies = [ "httpdate", "native-tls", @@ -5835,9 +5816,9 @@ dependencies = [ [[package]] name = "sentry-anyhow" -version = "0.35.0" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4084c497026eaa8115c31449881502b8b212d76cc10138014143a2c7595478ab" +checksum = "f4eebb1bbb6f3186ec1514e914324075d2168ceeb6861cd1ee650a8259d8cf3b" dependencies = [ "anyhow", "sentry-backtrace", @@ -5846,9 +5827,9 @@ dependencies = [ [[package]] name = "sentry-backtrace" -version = "0.35.0" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e57712c24e99252ef175b4b06c485294f10ad6bc5b5e1567ff3803ee7a0b7d3f" +checksum = "565ec31ad37bab8e6d9f289f34913ed8768347b133706192f10606dabd5c6bc4" dependencies = [ "backtrace", "once_cell", @@ -5858,9 +5839,9 @@ dependencies = [ [[package]] name = "sentry-contexts" -version = "0.35.0" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eba8754ec3b9279e00aa6d64916f211d44202370a1699afde1db2c16cbada089" +checksum = "e860275f25f27e8c0c7726ce116c7d5c928c5bba2ee73306e52b20a752298ea6" dependencies = [ "hostname", "libc", @@ -5872,9 +5853,9 @@ dependencies = [ [[package]] name = "sentry-core" -version = "0.35.0" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9f8b6dcd4fbae1e3e22b447f32670360b27e31b62ab040f7fb04e0f80c04d92" +checksum = "653942e6141f16651273159f4b8b1eaeedf37a7554c00cd798953e64b8a9bf72" dependencies = [ "once_cell", "rand 0.8.5", @@ -5885,9 +5866,9 @@ dependencies = [ [[package]] name = "sentry-debug-images" -version = "0.35.0" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8982a69133d3f5e4efdbfa0776937fca43c3a2e275a8fe184f50b1b0aa92e07c" +checksum = "2a60bc2154e6df59beed0ac13d58f8dfaf5ad20a88548a53e29e4d92e8e835c2" dependencies = [ "findshlibs", "once_cell", @@ -5896,9 +5877,9 @@ dependencies = [ [[package]] name = "sentry-panic" -version = "0.35.0" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de296dae6f01e931b65071ee5fe28d66a27909857f744018f107ed15fd1f6b25" +checksum = "105e3a956c8aa9dab1e4087b1657b03271bfc49d838c6ae9bfc7c58c802fd0ef" dependencies = [ "sentry-backtrace", "sentry-core", @@ -5906,9 +5887,9 @@ dependencies = [ [[package]] name = "sentry-tower" -version = "0.35.0" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcdaf9b1939589476bd57751d12a9653bbfe356610fc476d03d7683189183ab7" +checksum = "082f781dfc504d984e16d99f8dbf94d6ee4762dd0fc28de25713d0f900a8164d" dependencies = [ "http 1.2.0", "pin-project", @@ -5920,9 +5901,9 @@ dependencies = [ [[package]] name = "sentry-tracing" -version = "0.35.0" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "263f73c757ed7915d3e1e34625eae18cad498a95b4261603d4ce3f87b159a6f0" +checksum = "64e75c831b4d8b34a5aec1f65f67c5d46a26c7c5d3c7abd8b5ef430796900cf8" dependencies = [ "sentry-backtrace", "sentry-core", @@ -5932,9 +5913,9 @@ dependencies = [ [[package]] name = "sentry-types" -version = "0.35.0" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a71ed3a389948a6a6d92b98e997a2723ca22f09660c5a7b7388ecd509a70a527" +checksum = "2d4203359e60724aa05cf2385aaf5d4f147e837185d7dd2b9ccf1ee77f4420c8" dependencies = [ "debugid", "hex", diff --git a/Cargo.toml b/Cargo.toml index b28d29845..9b6681ad7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ exclude = [ ] [dependencies] -sentry = { version = "0.35.0", features = ["panic", "tracing", "tower-http", "anyhow", "backtrace"] } +sentry = { version = "0.36.0", features = ["panic", "tracing", "tower-http", "anyhow", "backtrace"] } log = "0.4" tracing = "0.1.37" tracing-subscriber = { version = "0.3.16", default-features = false, features = ["ansi", "fmt", "json", "env-filter", "tracing-log"] } @@ -78,8 +78,9 @@ serde_json = "1.0" serde_with = "3.4.0" # axum dependencies -axum = { version = "0.7.3", features = ["macros"] } -axum-extra = { version = "0.9.1", features = ["typed-header"] } +async-trait = "0.1.83" +axum = { version = "0.8.1", features = ["macros"] } +axum-extra = { version = "0.10.0", features = ["typed-header"] } tower = "0.5.1" tower-http = { version = "0.6.0", features = ["fs", "trace", "timeout", "catch-panic"] } mime = "0.3.16" diff --git a/src/repositories/github.rs b/src/repositories/github.rs index dbcda0953..6fd683d4c 100644 --- a/src/repositories/github.rs +++ b/src/repositories/github.rs @@ -1,6 +1,6 @@ use crate::error::Result; use crate::Config; -use axum::async_trait; +use async_trait::async_trait; use chrono::{DateTime, Utc}; use reqwest::{ header::{HeaderMap, HeaderValue, ACCEPT, AUTHORIZATION, USER_AGENT}, diff --git a/src/repositories/gitlab.rs b/src/repositories/gitlab.rs index c274fc652..4544dac94 100644 --- a/src/repositories/gitlab.rs +++ b/src/repositories/gitlab.rs @@ -1,5 +1,5 @@ use crate::error::Result; -use axum::async_trait; +use async_trait::async_trait; use chrono::{DateTime, Utc}; use reqwest::{ header::{HeaderMap, HeaderValue, ACCEPT, AUTHORIZATION, USER_AGENT}, diff --git a/src/repositories/updater.rs b/src/repositories/updater.rs index 4b41fbe1c..f9d1ce237 100644 --- a/src/repositories/updater.rs +++ b/src/repositories/updater.rs @@ -2,7 +2,7 @@ use crate::error::Result; use crate::repositories::{GitHub, GitLab, RateLimitReached}; use crate::utils::MetadataPackage; use crate::{db::Pool, Config}; -use axum::async_trait; +use async_trait::async_trait; use chrono::{DateTime, Utc}; use futures_util::stream::TryStreamExt; use once_cell::sync::Lazy; diff --git a/src/web/extractors.rs b/src/web/extractors.rs index 90552d1c0..23769582d 100644 --- a/src/web/extractors.rs +++ b/src/web/extractors.rs @@ -1,8 +1,7 @@ use crate::db::{AsyncPoolClient, Pool}; use anyhow::Context as _; use axum::{ - async_trait, - extract::{Extension, FromRequestParts}, + extract::{Extension, FromRequestParts, OptionalFromRequestParts}, http::request::Parts, RequestPartsExt, }; @@ -20,7 +19,6 @@ use super::error::AxumNope; #[derive(Debug)] pub(crate) struct DbConnection(AsyncPoolClient); -#[async_trait] impl FromRequestParts for DbConnection where S: Send + Sync, @@ -55,11 +53,32 @@ impl DerefMut for DbConnection { /// as error response instead of a plain text "bad request" #[allow(clippy::disallowed_types)] mod path_impl { + use serde::de::DeserializeOwned; + use super::*; #[derive(FromRequestParts)] #[from_request(via(axum::extract::Path), rejection(AxumNope))] pub(crate) struct Path(pub T); + + impl OptionalFromRequestParts for Path + where + T: DeserializeOwned + Send + 'static, + S: Send + Sync, + { + type Rejection = AxumNope; + + async fn from_request_parts( + parts: &mut Parts, + _state: &S, + ) -> Result, Self::Rejection> { + parts + .extract::>>() + .await + .map(|path| path.map(|obj| Path(obj.0))) + .map_err(|err| AxumNope::BadRequest(err.into())) + } + } } pub(crate) use path_impl::Path; diff --git a/src/web/metrics.rs b/src/web/metrics.rs index ef597377b..47788b272 100644 --- a/src/web/metrics.rs +++ b/src/web/metrics.rs @@ -118,15 +118,15 @@ mod tests { fn test_response_times_count_being_collected() { const ROUTES: &[(&str, &str)] = &[ ("/", "/"), - ("/crate/hexponent/0.2.0", "/crate/:name/:version"), - ("/crate/rcc/0.0.0", "/crate/:name/:version"), + ("/crate/hexponent/0.2.0", "/crate/{name}/{version}"), + ("/crate/rcc/0.0.0", "/crate/{name}/{version}"), ( "/crate/rcc/0.0.0/builds.json", - "/crate/:name/:version/builds.json", + "/crate/{name}/{version}/builds.json", ), ( "/crate/rcc/0.0.0/status.json", - "/crate/:name/:version/status.json", + "/crate/{name}/{version}/status.json", ), ("/-/static/index.js", "static resource"), ("/-/static/menu.js", "static resource"), @@ -139,12 +139,15 @@ mod tests { ("/releases/recent-failures", "/releases/recent-failures"), ( "/releases/recent-failures/1", - "/releases/recent-failures/:page", + "/releases/recent-failures/{page}", ), - ("/releases/recent/1", "/releases/recent/:page"), + ("/releases/recent/1", "/releases/recent/{page}"), ("/-/static/robots.txt", "static resource"), ("/sitemap.xml", "/sitemap.xml"), - ("/-/sitemap/a/sitemap.xml", "/-/sitemap/:letter/sitemap.xml"), + ( + "/-/sitemap/a/sitemap.xml", + "/-/sitemap/{letter}/sitemap.xml", + ), ("/-/static/style.css", "static resource"), ("/-/static/vendored.css", "static resource"), ("/rustdoc/rcc/0.0.0/rcc/index.html", "rustdoc page"), diff --git a/src/web/routes.rs b/src/web/routes.rs index 73d9a5b17..0900445b1 100644 --- a/src/web/routes.rs +++ b/src/web/routes.rs @@ -108,13 +108,13 @@ pub(super) fn build_axum_routes() -> AxumRouter { // have to use the same name: // // These routes work together: - // - `/:name/:version/settings.html` - // - `/:name/:version/:target` + // - `/{name}/{version}/settings.html` + // - `/{name}/{version}/{target}` // and axum can prioritize the more specific route. // // This panics because of conflicting routes: - // - `/:name/:version/settings.html` - // - `/:crate/:version/:target` + // - `/{name}/{version}/settings.html` + // - `/{crate}/{version}/{target}` // AxumRouter::new() // Well known resources, robots.txt and favicon.ico support redirection, the sitemap.xml @@ -129,7 +129,9 @@ pub(super) fn build_axum_routes() -> AxumRouter { "/favicon.ico", get_static(|| async { Redirect::permanent("/-/static/favicon.ico") }), ) - .nest("/-/static/", build_static_router()) + // `.nest` with fallbacks is currently broken, `.nest_service works + // https://github.com/tokio-rs/axum/issues/3138 + .nest_service("/-/static", build_static_router()) .route( "/opensearch.xml", get_static(|| async { Redirect::permanent("/-/static/opensearch.xml") }), @@ -139,7 +141,7 @@ pub(super) fn build_axum_routes() -> AxumRouter { get_internal(super::sitemap::sitemapindex_handler), ) .route_with_tsr( - "/-/sitemap/:letter/sitemap.xml", + "/-/sitemap/{letter}/sitemap.xml", get_internal(super::sitemap::sitemap_handler), ) .route_with_tsr( @@ -149,7 +151,7 @@ pub(super) fn build_axum_routes() -> AxumRouter { .merge(build_metric_routes()) .route_with_tsr("/about", get_internal(super::sitemap::about_handler)) .route_with_tsr( - "/about/:subpage", + "/about/{subpage}", get_internal(super::sitemap::about_handler), ) .route("/", get_internal(super::releases::home_page)) @@ -158,7 +160,7 @@ pub(super) fn build_axum_routes() -> AxumRouter { get_internal(super::releases::recent_releases_handler), ) .route_with_tsr( - "/releases/recent/:page", + "/releases/recent/{page}", get_internal(super::releases::recent_releases_handler), ) .route_with_tsr( @@ -166,7 +168,7 @@ pub(super) fn build_axum_routes() -> AxumRouter { get_internal(super::releases::releases_by_stars_handler), ) .route_with_tsr( - "/releases/stars/:page", + "/releases/stars/{page}", get_internal(super::releases::releases_by_stars_handler), ) .route_with_tsr( @@ -174,7 +176,7 @@ pub(super) fn build_axum_routes() -> AxumRouter { get_internal(super::releases::releases_recent_failures_handler), ) .route_with_tsr( - "/releases/recent-failures/:page", + "/releases/recent-failures/{page}", get_internal(super::releases::releases_recent_failures_handler), ) .route_with_tsr( @@ -182,15 +184,15 @@ pub(super) fn build_axum_routes() -> AxumRouter { get_internal(super::releases::releases_failures_by_stars_handler), ) .route_with_tsr( - "/releases/failures/:page", + "/releases/failures/{page}", get_internal(super::releases::releases_failures_by_stars_handler), ) .route_with_tsr( - "/crate/:name", + "/crate/{name}", get_internal(super::crate_details::crate_details_handler), ) .route_with_tsr( - "/crate/:name/:version", + "/crate/{name}/{version}", get_internal(super::crate_details::crate_details_handler), ) .route_with_tsr( @@ -198,11 +200,11 @@ pub(super) fn build_axum_routes() -> AxumRouter { get_internal(super::releases::releases_feed_handler), ) .route_with_tsr( - "/releases/:owner", + "/releases/{owner}", get_internal(super::releases::owner_handler), ) .route_with_tsr( - "/releases/:owner/:page", + "/releases/{owner}/{page}", get_internal(super::releases::owner_handler), ) .route_with_tsr( @@ -218,71 +220,71 @@ pub(super) fn build_axum_routes() -> AxumRouter { get_internal(super::releases::build_queue_handler), ) .route_with_tsr( - "/crate/:name/:version/builds", + "/crate/{name}/{version}/builds", get_internal(super::builds::build_list_handler), ) .route( - "/crate/:name/:version/builds.json", + "/crate/{name}/{version}/builds.json", get_internal(super::builds::build_list_json_handler), ) .route( - "/crate/:name/:version/rebuild", + "/crate/{name}/{version}/rebuild", post_internal(super::builds::build_trigger_rebuild_handler), ) .route( - "/crate/:name/:version/status.json", + "/crate/{name}/{version}/status.json", get_internal(super::status::status_handler), ) .route_with_tsr( - "/crate/:name/:version/builds/:id", + "/crate/{name}/{version}/builds/{id}", get_internal(super::build_details::build_details_handler), ) .route_with_tsr( - "/crate/:name/:version/builds/:id/:filename", + "/crate/{name}/{version}/builds/{id}/{filename}", get_internal(super::build_details::build_details_handler), ) .route_with_tsr( - "/crate/:name/:version/features", + "/crate/{name}/{version}/features", get_internal(super::features::build_features_handler), ) .route_with_tsr( - "/crate/:name/:version/source/", + "/crate/{name}/{version}/source/", get_internal(super::source::source_browser_handler), ) .route( - "/crate/:name/:version/source/*path", + "/crate/{name}/{version}/source/{*path}", get_internal(super::source::source_browser_handler), ) .route( - "/crate/:name/:version/menus/platforms/:target", + "/crate/{name}/{version}/menus/platforms/{target}", get_internal(super::crate_details::get_all_platforms), ) .route( - "/crate/:name/:version/menus/platforms/:target/*path", + "/crate/{name}/{version}/menus/platforms/{target}/{*path}", get_internal(super::crate_details::get_all_platforms), ) .route( - "/crate/:name/:version/menus/platforms", + "/crate/{name}/{version}/menus/platforms", get_internal(super::crate_details::get_all_platforms_root), ) .route( - "/crate/:name/:version/menus/releases/:target", + "/crate/{name}/{version}/menus/releases/{target}", get_internal(super::crate_details::get_all_releases), ) .route( - "/crate/:name/:version/menus/releases/:target/*path", + "/crate/{name}/{version}/menus/releases/{target}/{*path}", get_internal(super::crate_details::get_all_releases), ) .route( - "/crate/:name/:version/menus/releases", + "/crate/{name}/{version}/menus/releases", get_internal(super::crate_details::get_all_releases), ) .route( - "/crate/:name/:version/menus/releases/:target/", + "/crate/{name}/{version}/menus/releases/{target}/", get_internal(super::crate_details::get_all_releases), ) .route( - "/-/rustdoc.static/*path", + "/-/rustdoc.static/{*path}", get_internal(super::rustdoc::static_asset_handler), ) .route( @@ -304,59 +306,59 @@ pub(super) fn build_axum_routes() -> AxumRouter { }), ) .route_with_tsr( - "/crate/:name/:version/download", + "/crate/{name}/{version}/download", get_internal(super::rustdoc::download_handler), ) .route( - "/crate/:name/:version/target-redirect/*path", + "/crate/{name}/{version}/target-redirect/{*path}", get_internal(super::rustdoc::target_redirect_handler), ) .route( - "/:name/badge.svg", + "/{name}/badge.svg", get_internal(super::rustdoc::badge_handler), ) .route( - "/:name", + "/{name}", get_rustdoc(super::rustdoc::rustdoc_redirector_handler), ) .route( - "/:name/", + "/{name}/", get_rustdoc(super::rustdoc::rustdoc_redirector_handler), ) .route( - "/:name/:version", + "/{name}/{version}", get_rustdoc(super::rustdoc::rustdoc_redirector_handler), ) .route( - "/:name/:version/", + "/{name}/{version}/", get_rustdoc(super::rustdoc::rustdoc_redirector_handler), ) .route( - "/:name/:version/all.html", + "/{name}/{version}/all.html", get_rustdoc(super::rustdoc::rustdoc_html_server_handler), ) .route( - "/:name/:version/help.html", + "/{name}/{version}/help.html", get_rustdoc(super::rustdoc::rustdoc_html_server_handler), ) .route( - "/:name/:version/settings.html", + "/{name}/{version}/settings.html", get_rustdoc(super::rustdoc::rustdoc_html_server_handler), ) .route( - "/:name/:version/scrape-examples-help.html", + "/{name}/{version}/scrape-examples-help.html", get_rustdoc(super::rustdoc::rustdoc_html_server_handler), ) .route( - "/:name/:version/:target", + "/{name}/{version}/{target}", get_rustdoc(super::rustdoc::rustdoc_redirector_handler), ) .route( - "/:name/:version/:target/", + "/{name}/{version}/{target}/", get_rustdoc(super::rustdoc::rustdoc_html_server_handler), ) .route( - "/:name/:version/:target/*path", + "/{name}/{version}/{target}/{*path}", get_rustdoc(super::rustdoc::rustdoc_html_server_handler), ) .fallback(fallback) diff --git a/src/web/statics.rs b/src/web/statics.rs index 57fbd4dd5..ebdd78e34 100644 --- a/src/web/statics.rs +++ b/src/web/statics.rs @@ -67,8 +67,7 @@ pub(crate) fn build_static_router() -> AxumRouter { "/rustdoc-2021-12-05.css", get_static(|| async { build_static_css_response(RUSTDOC_2021_12_05_CSS) }), ) - .nest_service( - "/", + .fallback_service( get_service(ServeDir::new("static").fallback(ServeDir::new("vendor"))) .layer(middleware::from_fn(set_needed_static_headers)) .layer(middleware::from_fn(|request, next| async {