d6dc02f
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<S, B> Transform<S, ServiceRequest> for EnvelopeContext
where
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
B: 'static,
{
type Response = ServiceResponse<B>;
type Error = Error;
type Transform = EnvelopeContextMiddleware<S>;
type InitError = ();
type Future = Ready<Result<Self::Transform, Self::InitError>>;
fn new_transform(&self, service: S) -> Self::Future {
ready(Ok(EnvelopeContextMiddleware {
service: Rc::new(service),
}))
}
}
pub struct EnvelopeContextMiddleware<S> {
service: Rc<S>,
}
impl<S, B> Service<ServiceRequest> for EnvelopeContextMiddleware<S>
where
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
B: 'static,
{
type Response = ServiceResponse<B>;
type Error = Error;
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
fn poll_ready(&self, context: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
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::<RequestId>()
.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);
}
}