Commit

Author:

Hash:

Timestamp:

+149 -140 +/-1 browse

Kevin Schoon [me@kevinschoon.com]

f2d54e635b0ad56859794529703c74c11392a25e

Wed, 10 Jan 2024 17:03:07 +0000 (1.3 years ago)

normalize paths with trailing slash
1diff --git a/src/web2/server.rs b/src/web2/server.rs
2index 75ef234..e8cb16f 100644
3--- a/src/web2/server.rs
4+++ b/src/web2/server.rs
5 @@ -4,11 +4,18 @@ use std::fs;
6 use std::net::SocketAddrV4;
7 use std::sync::Arc;
8
9- use axum::{body::Body, http::Request, middleware::from_fn_with_state, routing, Extension, Router};
10+ use axum::{
11+ body::Body, extract::Request, middleware::from_fn_with_state, routing, Extension, Router,
12+ ServiceExt,
13+ };
14 use globwalk::glob_builder;
15 use tera::Tera;
16 use tokio::net::TcpListener;
17- use tower_http::trace::{DefaultOnResponse, TraceLayer};
18+ use tower::Layer;
19+ use tower_http::{
20+ normalize_path::NormalizePathLayer,
21+ trace::{DefaultOnResponse, TraceLayer},
22+ };
23 use tracing::{Level, Span};
24
25 use crate::config::Config;
26 @@ -149,145 +156,147 @@ pub async fn serve(cfg: &Config) -> Result<(), Box<dyn Error>> {
27 let xmpp_required_plugins: &'static [rpc_initiator::Kind] = &[rpc_initiator::Kind::Xmpp];
28
29 let address: SocketAddrV4 = cfg.http.address.parse()?;
30- let router = Router::new()
31- .route("/robots.txt", routing::get(robots::serve))
32- .nest(
33- "/",
34- Router::new()
35- .route("/", routing::get(index::index))
36- .route("/browse", routing::get(index::index))
37- .route("/:collection", routing::get(index::collection))
38- .route("/rss/firehose.xml", routing::get(rss::feed_firehose))
39- .route("/rss/1d.xml", routing::get(rss::feed_1d))
40- .route("/rss/1w.xml", routing::get(rss::feed_1w))
41- .route("/rss/1m.xml", routing::get(rss::feed_1m))
42- .route("/about", routing::get(about::serve))
43- .route("/config", routing::get(config::serve).post(config::update))
44- .layer(from_fn_with_state(
45- Arc::new((cfg.clone(), templates.clone())),
46- template::middleware,
47- )),
48- )
49- .nest(
50- "/mail",
51- Router::new()
52- .route("/", routing::get(mail::lists))
53- .route("/:list_id", routing::get(mail::threads))
54- .route("/export/:list_id", routing::get(mail::export))
55- .route("/export/:list_id/:thread_id", routing::get(mail::export))
56- .route(
57- "/export/:list_id/:thread_id/:message_id",
58- routing::get(mail::export),
59- )
60- .route("/thread/:list_id/:thread_id", routing::get(mail::thread))
61- .route("/message/:list_id/:message_id", routing::get(mail::message))
62- .layer(from_fn_with_state(
63- Arc::new((cfg.clone(), templates.clone(), mail_required_plugins)),
64- rpc_initiator::required,
65- ))
66- .layer(from_fn_with_state(
67- Arc::new((cfg.clone(), templates.clone())),
68- template::middleware,
69- )),
70- )
71- .nest(
72- "/xmpp",
73- Router::new()
74- .route("/", routing::get(xmpp::channels))
75- .route("/:channel", routing::get(xmpp::channel))
76- .route("/:channel/:last_message", routing::get(xmpp::channel))
77- .layer(from_fn_with_state(
78- Arc::new((cfg.clone(), templates.clone(), xmpp_required_plugins)),
79- rpc_initiator::required,
80- ))
81- .layer(from_fn_with_state(
82- Arc::new((cfg.clone(), templates.clone())),
83- template::middleware,
84- )),
85- )
86- .nest(
87- "/:collection/:name",
88- Router::new()
89- .route("/", routing::get(repo::serve))
90- .route(
91- "/rss/firehose.xml",
92- routing::get(rss::feed_repository_firehose),
93- )
94- .route("/rss/1d.xml", routing::get(rss::feed_repository_1d))
95- .route("/rss/1w.xml", routing::get(rss::feed_repository_1w))
96- .route("/rss/1m.xml", routing::get(rss::feed_repository_1m))
97- .route("/commit/:commit_id", routing::get(commit::serve))
98- .route("/tree/:commitish", routing::get(repo::serve))
99- .route("/tree/:commitish/*file_path", routing::get(repo::serve))
100- .route("/log", routing::get(log_route::serve))
101- .route("/log/:commitish", routing::get(log_route::serve))
102- .route("/log/:commitish/*file_path", routing::get(log_route::serve))
103- .route("/charts", routing::get(chart::serve))
104- .route("/chart/:kind", routing::get(chart::serve))
105- .route("/chart/:kind/:commit_id", routing::get(chart::serve))
106- .route(
107- "/:kind/:commit_id/:width/:height",
108- routing::get(chart::serve),
109- )
110- .route("/authors", routing::get(authors::serve))
111- .route("/blame/:commitish/*file_path", routing::get(blame::serve))
112- .route("/blob/:commitish/*file_path", routing::get(blob::serve))
113- .route("/raw/:commitish/*file_path", routing::get(blob::serve_raw))
114- .route("/builds", routing::get(builds::serve))
115- .route("/builds/badge/:commitish", routing::get(builds::badge))
116- .route("/builds/:commit_id", routing::get(builds::details))
117- .route(
118- "/builds/:commit_id/:build_id",
119- routing::get(builds::details),
120- )
121- .route("/bugs", routing::get(bugs::bugs))
122- .route("/bugs/:bug_id", routing::get(bugs::bug))
123- .route("/refs", routing::get(refs::tags))
124- .route("/refs/branches", routing::get(refs::branches))
125- .route("/refs/tags", routing::get(refs::tags))
126- .route("/refs/archive/:ref_id", routing::get(refs::archive))
127- .layer(from_fn_with_state(cfg.clone(), repository::middleware))
128- .layer(from_fn_with_state(
129- Arc::new(cfg.clone()),
130- rpc_initiator::optional,
131- ))
132- .layer(from_fn_with_state(
133- Arc::new((cfg.clone(), templates.clone())),
134- template::middleware,
135- )),
136- )
137- .route(
138- "/.well-known/webfinger",
139- routing::get(finger::serve).layer(Extension(finger::CResolver {
140- domain: "todo",
141- config: Arc::new(cfg.clone()),
142- })),
143- )
144- .route("/static/main.min.css", routing::get(assets::serve_css))
145- .route("/static/assets/:asset", routing::get(assets::serve_asset))
146- .layer(Extension(cfg.clone()))
147- .layer(Extension(Arc::new(db)))
148- .layer(Extension(highlighter))
149- .layer(Extension(adapter))
150- // error handling
151- .layer(from_fn_with_state(
152- Arc::new((cfg.clone(), templates.clone())),
153- error::middleware,
154- ))
155- // git hosted static sites
156- .layer(from_fn_with_state(
157- (cfg.clone(), site_mapping),
158- sites::middleware,
159- ))
160- .layer(
161- TraceLayer::new_for_http()
162- .on_request(|request: &Request<Body>, _span: &Span| {
163- tracing::info!("started {} {}", request.method(), request.uri().path())
164- })
165- .on_response(DefaultOnResponse::new().level(Level::INFO)),
166- );
167+ let app = NormalizePathLayer::trim_trailing_slash().layer(
168+ Router::new()
169+ .route("/robots.txt", routing::get(robots::serve))
170+ .nest(
171+ "/",
172+ Router::new()
173+ .route("/", routing::get(index::index))
174+ .route("/browse", routing::get(index::index))
175+ .route("/:collection", routing::get(index::collection))
176+ .route("/rss/firehose.xml", routing::get(rss::feed_firehose))
177+ .route("/rss/1d.xml", routing::get(rss::feed_1d))
178+ .route("/rss/1w.xml", routing::get(rss::feed_1w))
179+ .route("/rss/1m.xml", routing::get(rss::feed_1m))
180+ .route("/about", routing::get(about::serve))
181+ .route("/config", routing::get(config::serve).post(config::update))
182+ .layer(from_fn_with_state(
183+ Arc::new((cfg.clone(), templates.clone())),
184+ template::middleware,
185+ )),
186+ )
187+ .nest(
188+ "/mail",
189+ Router::new()
190+ .route("/", routing::get(mail::lists))
191+ .route("/:list_id", routing::get(mail::threads))
192+ .route("/export/:list_id", routing::get(mail::export))
193+ .route("/export/:list_id/:thread_id", routing::get(mail::export))
194+ .route(
195+ "/export/:list_id/:thread_id/:message_id",
196+ routing::get(mail::export),
197+ )
198+ .route("/thread/:list_id/:thread_id", routing::get(mail::thread))
199+ .route("/message/:list_id/:message_id", routing::get(mail::message))
200+ .layer(from_fn_with_state(
201+ Arc::new((cfg.clone(), templates.clone(), mail_required_plugins)),
202+ rpc_initiator::required,
203+ ))
204+ .layer(from_fn_with_state(
205+ Arc::new((cfg.clone(), templates.clone())),
206+ template::middleware,
207+ )),
208+ )
209+ .nest(
210+ "/xmpp",
211+ Router::new()
212+ .route("/", routing::get(xmpp::channels))
213+ .route("/:channel", routing::get(xmpp::channel))
214+ .route("/:channel/:last_message", routing::get(xmpp::channel))
215+ .layer(from_fn_with_state(
216+ Arc::new((cfg.clone(), templates.clone(), xmpp_required_plugins)),
217+ rpc_initiator::required,
218+ ))
219+ .layer(from_fn_with_state(
220+ Arc::new((cfg.clone(), templates.clone())),
221+ template::middleware,
222+ )),
223+ )
224+ .nest(
225+ "/:collection/:name",
226+ Router::new()
227+ .route("/", routing::get(repo::serve))
228+ .route(
229+ "/rss/firehose.xml",
230+ routing::get(rss::feed_repository_firehose),
231+ )
232+ .route("/rss/1d.xml", routing::get(rss::feed_repository_1d))
233+ .route("/rss/1w.xml", routing::get(rss::feed_repository_1w))
234+ .route("/rss/1m.xml", routing::get(rss::feed_repository_1m))
235+ .route("/commit/:commit_id", routing::get(commit::serve))
236+ .route("/tree/:commitish", routing::get(repo::serve))
237+ .route("/tree/:commitish/*file_path", routing::get(repo::serve))
238+ .route("/log", routing::get(log_route::serve))
239+ .route("/log/:commitish", routing::get(log_route::serve))
240+ .route("/log/:commitish/*file_path", routing::get(log_route::serve))
241+ .route("/charts", routing::get(chart::serve))
242+ .route("/chart/:kind", routing::get(chart::serve))
243+ .route("/chart/:kind/:commit_id", routing::get(chart::serve))
244+ .route(
245+ "/:kind/:commit_id/:width/:height",
246+ routing::get(chart::serve),
247+ )
248+ .route("/authors", routing::get(authors::serve))
249+ .route("/blame/:commitish/*file_path", routing::get(blame::serve))
250+ .route("/blob/:commitish/*file_path", routing::get(blob::serve))
251+ .route("/raw/:commitish/*file_path", routing::get(blob::serve_raw))
252+ .route("/builds", routing::get(builds::serve))
253+ .route("/builds/badge/:commitish", routing::get(builds::badge))
254+ .route("/builds/:commit_id", routing::get(builds::details))
255+ .route(
256+ "/builds/:commit_id/:build_id",
257+ routing::get(builds::details),
258+ )
259+ .route("/bugs", routing::get(bugs::bugs))
260+ .route("/bugs/:bug_id", routing::get(bugs::bug))
261+ .route("/refs", routing::get(refs::tags))
262+ .route("/refs/branches", routing::get(refs::branches))
263+ .route("/refs/tags", routing::get(refs::tags))
264+ .route("/refs/archive/:ref_id", routing::get(refs::archive))
265+ .layer(from_fn_with_state(cfg.clone(), repository::middleware))
266+ .layer(from_fn_with_state(
267+ Arc::new(cfg.clone()),
268+ rpc_initiator::optional,
269+ ))
270+ .layer(from_fn_with_state(
271+ Arc::new((cfg.clone(), templates.clone())),
272+ template::middleware,
273+ )),
274+ )
275+ .route(
276+ "/.well-known/webfinger",
277+ routing::get(finger::serve).layer(Extension(finger::CResolver {
278+ domain: "todo",
279+ config: Arc::new(cfg.clone()),
280+ })),
281+ )
282+ .route("/static/main.min.css", routing::get(assets::serve_css))
283+ .route("/static/assets/:asset", routing::get(assets::serve_asset))
284+ .layer(Extension(cfg.clone()))
285+ .layer(Extension(Arc::new(db)))
286+ .layer(Extension(highlighter))
287+ .layer(Extension(adapter))
288+ // error handling
289+ .layer(from_fn_with_state(
290+ Arc::new((cfg.clone(), templates.clone())),
291+ error::middleware,
292+ ))
293+ // git hosted static sites
294+ .layer(from_fn_with_state(
295+ (cfg.clone(), site_mapping),
296+ sites::middleware,
297+ ))
298+ .layer(
299+ TraceLayer::new_for_http()
300+ .on_request(|request: &Request<Body>, _span: &Span| {
301+ tracing::info!("started {} {}", request.method(), request.uri().path())
302+ })
303+ .on_response(DefaultOnResponse::new().level(Level::INFO)),
304+ ),
305+ );
306 log::info!("listening @ {}", cfg.http.address);
307 let listener = TcpListener::bind(address).await?;
308- axum::serve(listener, router.into_make_service()).await?;
309+ axum::serve(listener, ServiceExt::<Request>::into_make_service(app)).await?;
310 Ok(())
311 }