From 0f692a3bfb1bdf1791e8e0efaf00676379b9160c Mon Sep 17 00:00:00 2001 From: Sven-Hendrik Haase Date: Wed, 11 Sep 2019 15:12:08 +0200 Subject: [PATCH] Add middleware example for reading the Request body --- middleware/Cargo.toml | 3 +- middleware/README.md | 1 - middleware/src/main.rs | 3 ++ middleware/src/read_body.rs | 70 +++++++++++++++++++++++++++++++++++++ 4 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 middleware/src/read_body.rs diff --git a/middleware/Cargo.toml b/middleware/Cargo.toml index 76ecbf1c..da904716 100644 --- a/middleware/Cargo.toml +++ b/middleware/Cargo.toml @@ -9,4 +9,5 @@ workspace = ".." actix-service = "0.4.1" actix-web = "1.0.0" futures = "0.1.25" -env_logger = "0.6" \ No newline at end of file +env_logger = "0.6" +bytes = "0.4" diff --git a/middleware/README.md b/middleware/README.md index 1cd2b732..9c901cb6 100644 --- a/middleware/README.md +++ b/middleware/README.md @@ -8,4 +8,3 @@ cargo run Look in `src/main.rs` and comment the different middlewares in/out to see how they function. - diff --git a/middleware/src/main.rs b/middleware/src/main.rs index 0496db27..5beb0a9b 100644 --- a/middleware/src/main.rs +++ b/middleware/src/main.rs @@ -3,6 +3,8 @@ use actix_web::{web, App, HttpServer}; #[allow(dead_code)] mod redirect; #[allow(dead_code)] +mod read_body; +#[allow(dead_code)] mod simple; fn main() -> std::io::Result<()> { @@ -12,6 +14,7 @@ fn main() -> std::io::Result<()> { HttpServer::new(|| { App::new() .wrap(redirect::CheckLogin) + .wrap(read_body::Logging) .wrap(simple::SayHi) .service(web::resource("/login").to(|| { "You are on /login. Go to src/redirect.rs to change this behavior." diff --git a/middleware/src/read_body.rs b/middleware/src/read_body.rs new file mode 100644 index 00000000..48aed2cc --- /dev/null +++ b/middleware/src/read_body.rs @@ -0,0 +1,70 @@ +use actix_service::{Service, Transform}; +use actix_web::error::PayloadError; +use actix_web::{dev::ServiceRequest, dev::ServiceResponse, Error, HttpMessage}; +use bytes::BytesMut; +use futures::future::{ok, FutureResult}; +use futures::stream::Stream; +use futures::{Future, Poll}; +use std::cell::RefCell; +use std::rc::Rc; + +pub struct Logging; + +impl Transform for Logging +where + S: Service, Error = Error>, + S::Future: 'static, + B: '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: Rc::new(RefCell::new(service)), + }) + } +} + +pub struct LoggingMiddleware { + // This is special: We need this to avoid lifetime issues. + service: Rc>, +} + +impl Service for LoggingMiddleware +where + S: Service, Error = Error> + + 'static, + S::Future: 'static, + B: 'static, +{ + type Request = ServiceRequest; + type Response = ServiceResponse; + type Error = Error; + type Future = Box>; + + fn poll_ready(&mut self) -> Poll<(), Self::Error> { + self.service.poll_ready() + } + + fn call(&mut self, mut req: ServiceRequest) -> Self::Future { + let mut svc = self.service.clone(); + + Box::new( + req.take_payload() + .fold(BytesMut::new(), move |mut body, chunk| { + body.extend_from_slice(&chunk); + Ok::<_, PayloadError>(body) + }) + .map_err(|e| e.into()) + .and_then(move |bytes| { + println!("request body: {:?}", bytes); + svc.call(req).and_then(|res| Ok(res)) + }), + ) + } +}