use actix_web::{ dev::{Payload, Service, ServiceRequest, Transform}, http::StatusCode, HttpMessage, HttpResponse, ResponseError, }; use bytes::BytesMut; use futures::{ future::{ok, try_join, LocalBoxFuture, Ready}, stream::StreamExt, }; use log::{error, info}; use std::task::{Context, Poll}; use tokio::sync::mpsc::channel; #[derive(Clone, Debug)] pub struct DebugPayload(pub bool); #[doc(hidden)] #[derive(Clone, Debug)] pub struct DebugPayloadMiddleware(bool, S); #[derive(Clone, Debug, thiserror::Error)] #[error("Failed to read payload")] pub struct DebugError; impl ResponseError for DebugError { fn status_code(&self) -> StatusCode { StatusCode::BAD_REQUEST } fn error_response(&self) -> HttpResponse { HttpResponse::new(self.status_code()) } } impl Transform for DebugPayload where S: Service, S::Future: 'static, S::Error: 'static, { type Request = S::Request; type Response = S::Response; type Error = S::Error; type InitError = (); type Transform = DebugPayloadMiddleware; type Future = Ready>; fn new_transform(&self, service: S) -> Self::Future { ok(DebugPayloadMiddleware(self.0, service)) } } impl Service for DebugPayloadMiddleware where S: Service, S::Future: 'static, S::Error: 'static, { type Request = S::Request; type Response = S::Response; type Error = S::Error; type Future = LocalBoxFuture<'static, Result>; fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { self.1.poll_ready(cx) } fn call(&mut self, mut req: S::Request) -> Self::Future { if self.0 { let (mut tx, rx) = channel(1); let mut pl = req.take_payload(); req.set_payload(Payload::Stream(Box::pin(rx))); let fut = self.1.call(req); let payload_fut = async move { let mut bytes = BytesMut::new(); while let Some(res) = pl.next().await { let b = res.map_err(|e| { error!("Payload error, {}", e); DebugError })?; bytes.extend(b); } info!("{}", String::from_utf8_lossy(bytes.as_ref())); tx.send(Ok(bytes.freeze())).await.map_err(|e| { error!("Error sending bytes, {}", e); DebugError })?; Ok(()) as Result<(), actix_web::Error> }; Box::pin(async move { let (res, _) = try_join(fut, payload_fut).await?; Ok(res) }) } else { let fut = self.1.call(req); Box::pin(async move { fut.await }) } } }