@@ -5,6 +5,7 @@ pub mod page;
5
5
use crate :: utils:: get_correct_docsrs_style_file;
6
6
use crate :: utils:: spawn_blocking;
7
7
use anyhow:: { anyhow, bail, Context as _, Result } ;
8
+ use axum_extra:: middleware:: option_layer;
8
9
use serde_json:: Value ;
9
10
use tracing:: { info, instrument} ;
10
11
@@ -30,7 +31,13 @@ mod statics;
30
31
use crate :: { db:: Pool , impl_axum_webpage, Context } ;
31
32
use anyhow:: Error ;
32
33
use 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 ,
34
41
} ;
35
42
use chrono:: { DateTime , Utc } ;
36
43
use error:: AxumNope ;
@@ -40,9 +47,10 @@ use postgres::Client;
40
47
use semver:: { Version , VersionReq } ;
41
48
use serde:: Serialize ;
42
49
use std:: borrow:: Borrow ;
50
+ use std:: time:: Duration ;
43
51
use std:: { borrow:: Cow , net:: SocketAddr , sync:: Arc } ;
44
52
use tower:: ServiceBuilder ;
45
- use tower_http:: trace:: TraceLayer ;
53
+ use tower_http:: { timeout :: TimeoutLayer , trace:: TraceLayer } ;
46
54
use url:: form_urlencoded;
47
55
48
56
// from https://github.com/servo/rust-url/blob/master/url/src/parser.rs
@@ -243,18 +251,39 @@ async fn match_version_axum(
243
251
. await
244
252
}
245
253
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
+
246
266
#[ instrument( skip_all) ]
247
267
pub ( crate ) fn build_axum_app (
248
268
context : & dyn Context ,
249
269
template_data : Arc < TemplateData > ,
250
270
) -> Result < AxumRouter , Error > {
271
+ let config = context. config ( ) ?;
251
272
Ok ( routes:: build_axum_routes ( ) . layer (
252
273
// It’s recommended to use tower::ServiceBuilder to apply multiple middleware at once,
253
274
// instead of calling Router::layer repeatedly:
254
275
ServiceBuilder :: new ( )
255
276
. layer ( TraceLayer :: new_for_http ( ) )
256
277
. layer ( sentry_tower:: NewSentryLayer :: new_from_top ( ) )
257
278
. 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
+ } ) ) )
258
287
. layer ( Extension ( context. pool ( ) ?) )
259
288
. layer ( Extension ( context. build_queue ( ) ?) )
260
289
. layer ( Extension ( context. metrics ( ) ?) )
0 commit comments