1use std::{borrow::Cow, time::Duration};
4
5use http::{HeaderValue, Method, Request};
6use tower::{limit::RateLimitLayer as TowerRateLimitLayer, timeout::TimeoutLayer};
7use tower_http::compression::CompressionLayer;
8use tower_http::cors::{Any, CorsLayer};
9use tower_http::trace::{HttpMakeClassifier, MakeSpan, TraceLayer};
10use tracing::{Level, Span};
11
12mod api_defaults;
13mod catch_panic;
14mod metrics;
15mod rate_limit;
16mod request_context;
17mod request_id;
18mod request_scope;
19mod security;
20
21pub use crate::context::{
22 ClientKind, IdentityExtractor, RequestContext, RequestIdentity, api_key_identity,
23 client_ip_identity, context_identity, trusted_proxy_client_ip_identity,
24};
25pub use api_defaults::ApiDefaults;
26pub use catch_panic::{CatchPanicLayer, CatchPanicService, catch_panic_layer};
27pub use metrics::{
28 HttpMetricsHook, MetricsLayer, MetricsService, PrometheusMetrics, metrics_layer,
29 route_metrics_layer,
30};
31pub use rate_limit::{
32 InMemoryRateLimitStore, RateLimitConfig, RateLimitDecision, RateLimitError, RateLimitLayer,
33 RateLimitService, RateLimitStore,
34};
35pub use request_context::{RequestContextLayer, RequestContextService, request_context_layer};
36pub use request_id::{
37 RequestIdConfig, RequestIdLayer, RequestIdMode, RequestIdPolicy, RequestIdService,
38 ValidatedRequestIdLayer, ValidatedRequestIdService, validated_request_id_layer,
39};
40pub use request_scope::{RequestScopeLayer, RequestScopeService, request_scope_layer};
41pub use security::{
42 BodyLimitLayer, BodyLimitService, SecurityHeadersLayer, SecurityHeadersService,
43 TimeoutResponseLayer, TimeoutResponseService, body_limit_layer, security_headers_layer,
44 streaming_body_limit_layer, timeout_response_layer, webhook_body_limit_layer,
45};
46
47pub fn timeout_layer(timeout: Duration) -> TimeoutLayer {
49 TimeoutLayer::new(timeout)
50}
51
52pub fn rate_limit_layer(num: u64, per: Duration) -> TowerRateLimitLayer {
54 TowerRateLimitLayer::new(num, per)
55}
56
57pub fn request_id_layer() -> RequestIdLayer {
64 RequestIdLayer
65}
66
67pub fn cors_layer() -> CorsLayer {
69 CorsLayer::new()
70 .allow_origin(Any)
71 .allow_methods([
72 Method::GET,
73 Method::POST,
74 Method::PUT,
75 Method::PATCH,
76 Method::DELETE,
77 Method::OPTIONS,
78 ])
79 .allow_headers(Any)
80}
81
82pub fn cors_origin_layer(origin: HeaderValue) -> CorsLayer {
84 CorsLayer::new()
85 .allow_origin(origin)
86 .allow_methods([
87 Method::GET,
88 Method::POST,
89 Method::PUT,
90 Method::PATCH,
91 Method::DELETE,
92 Method::OPTIONS,
93 ])
94 .allow_headers(Any)
95}
96
97pub fn compression_layer() -> CompressionLayer {
99 CompressionLayer::new()
100}
101
102pub fn trace_layer() -> TraceLayer<HttpMakeClassifier> {
104 TraceLayer::new_for_http()
105}
106
107pub fn route_trace_layer(
109 route: impl Into<Cow<'static, str>>,
110) -> TraceLayer<HttpMakeClassifier, RouteMakeSpan> {
111 TraceLayer::new_for_http().make_span_with(RouteMakeSpan::new(route))
112}
113
114#[derive(Clone, Debug)]
116pub struct RouteMakeSpan {
117 route: Cow<'static, str>,
118}
119
120impl RouteMakeSpan {
121 pub fn new(route: impl Into<Cow<'static, str>>) -> Self {
123 Self {
124 route: route.into(),
125 }
126 }
127}
128
129impl<B> MakeSpan<B> for RouteMakeSpan {
130 fn make_span(&mut self, request: &Request<B>) -> Span {
131 tracing::span!(
132 Level::DEBUG,
133 "request",
134 method = %request.method(),
135 uri = %request.uri(),
136 route = %self.route,
137 )
138 }
139}