use std::future::{Ready, ready}; use std::rc::Rc; use std::task::{Context, Poll}; use ::session::{REQUEST_ID_HEADER, RequestId, validate_request_id}; use actix_web::dev::{Service, ServiceRequest, ServiceResponse, Transform}; use actix_web::http::header::{HeaderMap, HeaderName, HeaderValue}; use actix_web::{Error, HttpMessage}; use futures_util::future::LocalBoxFuture; use uuid::Uuid; use crate::context; #[derive(Debug, Clone, Copy, Default)] pub struct EnvelopeContext; impl Transform for EnvelopeContext where S: Service, Error = Error> + 'static, B: 'static, { type Response = ServiceResponse; type Error = Error; type Transform = EnvelopeContextMiddleware; type InitError = (); type Future = Ready>; fn new_transform(&self, service: S) -> Self::Future { ready(Ok(EnvelopeContextMiddleware { service: Rc::new(service), })) } } pub struct EnvelopeContextMiddleware { service: Rc, } impl Service for EnvelopeContextMiddleware where S: Service, Error = Error> + 'static, B: 'static, { type Response = ServiceResponse; type Error = Error; type Future = LocalBoxFuture<'static, Result>; fn poll_ready(&self, context: &mut Context<'_>) -> Poll> { self.service.poll_ready(context) } fn call(&self, request: ServiceRequest) -> Self::Future { let service = Rc::clone(&self.service); Box::pin(async move { let request_id = { let request = &request; request .extensions() .get::() .map(|value| value.0.clone()) .or_else(|| { validate_request_id( request .headers() .get(REQUEST_ID_HEADER) .and_then(|value| value.to_str().ok()), ) }) .unwrap_or_else(|| Uuid::now_v7().to_string()) }; request .extensions_mut() .insert(RequestId(request_id.clone())); context::with_request_id(request_id.clone(), async move { let mut response = service.call(request).await?; insert_header(response.headers_mut(), REQUEST_ID_HEADER, &request_id); { let headers = response.headers_mut(); const X_CONTENT_TYPE_OPTIONS: HeaderName = HeaderName::from_static("x-content-type-options"); const REFERRER_POLICY: HeaderName = HeaderName::from_static("referrer-policy"); const CSP: HeaderName = HeaderName::from_static("content-security-policy"); headers.insert(X_CONTENT_TYPE_OPTIONS, HeaderValue::from_static("nosniff")); headers.insert( REFERRER_POLICY, HeaderValue::from_static("strict-origin-when-cross-origin"), ); headers.insert(CSP, HeaderValue::from_static("frame-ancestors 'none'")); } Ok(response) }) .await }) } } fn insert_header(headers: &mut HeaderMap, name: &'static str, value: &str) { if let Ok(value) = HeaderValue::from_str(value) { headers.insert(HeaderName::from_static(name), value); } }