diff --git a/middleware/Cargo.toml b/middleware/Cargo.toml index da904716..84e66433 100644 --- a/middleware/Cargo.toml +++ b/middleware/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "middleware-example" version = "0.1.0" -authors = ["Gorm Casper "] +authors = ["Gorm Casper ", "Sven-Hendrik Haase "] edition = "2018" workspace = ".." diff --git a/middleware/README.md b/middleware/README.md index 401b9fd9..68a13c8b 100644 --- a/middleware/README.md +++ b/middleware/README.md @@ -19,10 +19,14 @@ they function. A middleware implementing a request guard which sketches a rough approximation of what a login could look like. -### read_body::Logging +### read_request_body::Logging A middleware demonstrating how to read out the incoming request body. +### read_response_body::Logging + +A middleware demonstrating how to read out the outgoing response body. + ### simple::SayHi A minimal middleware demonstrating the sequence of operations in an actix middleware. diff --git a/middleware/src/main.rs b/middleware/src/main.rs index 49dfb98f..d4ff9e3e 100644 --- a/middleware/src/main.rs +++ b/middleware/src/main.rs @@ -5,7 +5,9 @@ use futures::future::Future; #[allow(dead_code)] mod redirect; #[allow(dead_code)] -mod read_body; +mod read_request_body; +#[allow(dead_code)] +mod read_response_body; #[allow(dead_code)] mod simple; @@ -16,7 +18,8 @@ fn main() -> std::io::Result<()> { HttpServer::new(|| { App::new() .wrap(redirect::CheckLogin) - .wrap(read_body::Logging) + .wrap(read_request_body::Logging) + .wrap(read_response_body::Logging) .wrap(simple::SayHi) .wrap_fn(|req, srv| { println!("Hi from start. You requested: {}", req.path()); diff --git a/middleware/src/read_body.rs b/middleware/src/read_request_body.rs similarity index 100% rename from middleware/src/read_body.rs rename to middleware/src/read_request_body.rs diff --git a/middleware/src/read_response_body.rs b/middleware/src/read_response_body.rs new file mode 100644 index 00000000..26497bea --- /dev/null +++ b/middleware/src/read_response_body.rs @@ -0,0 +1,111 @@ +use actix_service::{Service, Transform}; +use actix_web::body::{BodySize, MessageBody, ResponseBody}; +use std::marker::PhantomData; +use actix_web::{dev::ServiceRequest, dev::ServiceResponse, Error}; +use bytes::{Bytes, BytesMut}; +use futures::Async; +use futures::future::{ok, FutureResult}; +use futures::{Future, Poll}; + +pub struct Logging; + +impl Transform for Logging +where + S: Service, Error = Error>, + B: MessageBody + 'static, +{ + type Request = ServiceRequest; + type Response = ServiceResponse>; + type Error = Error; + type InitError = (); + type Transform = LoggingMiddleware; + type Future = FutureResult; + + fn new_transform(&self, service: S) -> Self::Future { + ok(LoggingMiddleware { + service, + }) + } +} + +pub struct LoggingMiddleware { + service: S, +} + +impl Service for LoggingMiddleware +where + S: Service, Error = Error>, + B: MessageBody, +{ + type Request = ServiceRequest; + type Response = ServiceResponse>; + type Error = Error; + type Future = WrapperStream; + + fn poll_ready(&mut self) -> Poll<(), Self::Error> { + self.service.poll_ready() + } + + fn call(&mut self, req: ServiceRequest) -> Self::Future { + WrapperStream { + fut: self.service.call(req), + _t: PhantomData, + } + } +} + +pub struct WrapperStream +where + B: MessageBody, + S: Service, +{ + fut: S::Future, + _t: PhantomData<(B,)>, +} + +impl Future for WrapperStream +where + B: MessageBody, + S: Service, Error = Error>, +{ + type Item = ServiceResponse>; + type Error = Error; + + fn poll(&mut self) -> Poll { + let res = futures::try_ready!(self.fut.poll()); + + Ok(Async::Ready(res.map_body(move |_, body| { + ResponseBody::Body(BodyLogger { + body, + body_accum: BytesMut::new(), + }) + }))) + } +} + +pub struct BodyLogger { + body: ResponseBody, + body_accum: BytesMut, +} + +impl Drop for BodyLogger { + fn drop(&mut self) { + println!("response body: {:?}", self.body_accum); + } +} + +impl MessageBody for BodyLogger { + fn size(&self) -> BodySize { + self.body.size() + } + + fn poll_next(&mut self) -> Poll, Error> { + match self.body.poll_next()? { + Async::Ready(Some(chunk)) => { + self.body_accum.extend_from_slice(&chunk); + Ok(Async::Ready(Some(chunk))) + } + val => Ok(val), + } + } +}