@@ -5,6 +5,7 @@ pub mod page;
55use crate :: utils:: get_correct_docsrs_style_file;
66use crate :: utils:: spawn_blocking;
77use anyhow:: { anyhow, bail, Context as _, Result } ;
8+ use axum_extra:: middleware:: option_layer;
89use serde_json:: Value ;
910use tracing:: { info, instrument} ;
1011
@@ -30,7 +31,13 @@ mod statics;
3031use crate :: { db:: Pool , impl_axum_webpage, Context } ;
3132use anyhow:: Error ;
3233use axum:: {
33- extract:: Extension , http:: StatusCode , middleware, response:: IntoResponse , Router as AxumRouter ,
34+ extract:: Extension ,
35+ http:: Request as AxumRequest ,
36+ http:: StatusCode ,
37+ middleware,
38+ middleware:: Next ,
39+ response:: { IntoResponse , Response as AxumResponse } ,
40+ Router as AxumRouter ,
3441} ;
3542use chrono:: { DateTime , Utc } ;
3643use error:: AxumNope ;
@@ -40,9 +47,10 @@ use postgres::Client;
4047use semver:: { Version , VersionReq } ;
4148use serde:: Serialize ;
4249use std:: borrow:: Borrow ;
50+ use std:: time:: Duration ;
4351use std:: { borrow:: Cow , net:: SocketAddr , sync:: Arc } ;
4452use tower:: ServiceBuilder ;
45- use tower_http:: trace:: TraceLayer ;
53+ use tower_http:: { timeout :: TimeoutLayer , trace:: TraceLayer } ;
4654use url:: form_urlencoded;
4755
4856// from https://github.com/servo/rust-url/blob/master/url/src/parser.rs
@@ -243,18 +251,39 @@ async fn match_version_axum(
243251 . await
244252}
245253
254+ async fn log_timeouts_to_sentry < B > ( req : AxumRequest < B > , next : Next < B > ) -> AxumResponse {
255+ let uri = req. uri ( ) . clone ( ) ;
256+
257+ let response = next. run ( req) . await ;
258+
259+ if response. status ( ) == StatusCode :: REQUEST_TIMEOUT {
260+ tracing:: error!( ?uri, "request timeout" ) ;
261+ }
262+
263+ response
264+ }
265+
246266#[ instrument( skip_all) ]
247267pub ( crate ) fn build_axum_app (
248268 context : & dyn Context ,
249269 template_data : Arc < TemplateData > ,
250270) -> Result < AxumRouter , Error > {
271+ let config = context. config ( ) ?;
251272 Ok ( routes:: build_axum_routes ( ) . layer (
252273 // It’s recommended to use tower::ServiceBuilder to apply multiple middleware at once,
253274 // instead of calling Router::layer repeatedly:
254275 ServiceBuilder :: new ( )
255276 . layer ( TraceLayer :: new_for_http ( ) )
256277 . layer ( sentry_tower:: NewSentryLayer :: new_from_top ( ) )
257278 . layer ( sentry_tower:: SentryHttpLayer :: with_transaction ( ) )
279+ . layer ( option_layer (
280+ config
281+ . report_request_timeouts
282+ . then_some ( middleware:: from_fn ( log_timeouts_to_sentry) ) ,
283+ ) )
284+ . layer ( option_layer ( config. request_timeout . map ( |timeout| {
285+ TimeoutLayer :: new ( Duration :: from_secs ( timeout) )
286+ } ) ) )
258287 . layer ( Extension ( context. pool ( ) ?) )
259288 . layer ( Extension ( context. build_queue ( ) ?) )
260289 . layer ( Extension ( context. metrics ( ) ?) )
0 commit comments