examples icon indicating copy to clipboard operation
examples copied to clipboard

Read request body Middleware panics when json Body is sent

Open franklinblanco opened this issue 3 years ago • 4 comments

Request with payload (Middleware activated) Screen Shot 2022-08-30 at 10 14 46 AM

Request with same payload (Middleware deactivated) Screen Shot 2022-08-30 at 10 15 41 AM

How I set the middleware: Screen Shot 2022-08-30 at 10 16 29 AM

The middleware struct & implementation. Copied straight from https://github.com/actix/examples/blob/master/middleware/middleware/src/read_request_body.rs

use std::{future::{ready, Ready}, rc::Rc, io::Read};

use actix_web::{
    dev::{self, Service, ServiceRequest, ServiceResponse, Transform},
    Error, HttpMessage, web::BytesMut,
};
use futures_util::{future::LocalBoxFuture, StreamExt, Stream};
// There are two steps in middleware processing.
// 1. Middleware initialization, middleware factory gets called with
//    next service in chain as parameter.
// 2. Middleware's call method gets called with normal request.
pub struct BodyLogger;

// Middleware factory is `Transform` trait from actix-service crate
// `S` - type of the next service
// `B` - type of response's body
impl<S: 'static, B> Transform<S, ServiceRequest> for BodyLogger
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
    B: 'static,
{
    type Response = ServiceResponse<B>;
    type Error = Error;
    type InitError = ();
    type Transform = BodyLoggerMiddleware<S>;
    type Future = Ready<Result<Self::Transform, Self::InitError>>;

    fn new_transform(&self, service: S) -> Self::Future {
        ready(Ok(BodyLoggerMiddleware { service: Rc::new(service) }))
    }
}

pub struct BodyLoggerMiddleware<S> {
    service: Rc<S>,
}

impl<S, B> Service<ServiceRequest> for BodyLoggerMiddleware<S>
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
    S::Future: 'static,
    B: 'static,
{
    type Response = ServiceResponse<B>;
    type Error = Error;
    type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;

    dev::forward_ready!(service);

    fn call(&self, mut req: ServiceRequest) -> Self::Future {
        let svc = self.service.clone();
        Box::pin(async move {

            let mut body = BytesMut::new();
            let mut stream = req.take_payload();
            while let Some(chunk) = stream.next().await {
                body.extend_from_slice(&chunk?);
            }

            println!("request body: {body:?}");
            let res = svc.call(req).await?;

            println!("response: {:?}", res.headers());
            Ok(res)
        })
    }
}

franklinblanco avatar Aug 30 '22 14:08 franklinblanco

Me too. image I'm using it contact,And Chinese is not displayed image It should be a coding problem. Where to set the coding

liunn123 avatar Aug 31 '22 08:08 liunn123

It should be a coding problem. Where to set the coding

Sorry, I know English isn't your first, but did you mean the Encoding? I don't get those characters that you get in the response. Have you got anything else?

franklinblanco avatar Aug 31 '22 13:08 franklinblanco

I have the same problem as you,Coding is another problem。Thank you for your reply

liunn123 avatar Sep 01 '22 01:09 liunn123

Using this complete example I am not able to re-produce a panic. Please paste full code reproduction and command snippets.

$ curl -i http://localhost:8080 --data '{"hello":"world"}'
HTTP/1.1 200 OK
content-length: 0
content-type: text/plain; charset=utf-8
date: Sun, 11 Sep 2022 15:53:17 GMT
use std::{
    future::{ready, Ready},
    rc::Rc,
};

use actix_web::{
    dev::{self, Service, ServiceRequest, ServiceResponse, Transform},
    post,
    web::{self, BytesMut},
    App, Error, HttpMessage, HttpServer, Responder,
};
use futures_util::{future::LocalBoxFuture, StreamExt as _};

// There are two steps in middleware processing.
// 1. Middleware initialization, middleware factory gets called with
//    next service in chain as parameter.
// 2. Middleware's call method gets called with normal request.
pub struct BodyLogger;

// Middleware factory is `Transform` trait from actix-service crate
// `S` - type of the next service
// `B` - type of response's body
impl<S: 'static, B> Transform<S, ServiceRequest> for BodyLogger
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
    B: 'static,
{
    type Response = ServiceResponse<B>;
    type Error = Error;
    type InitError = ();
    type Transform = BodyLoggerMiddleware<S>;
    type Future = Ready<Result<Self::Transform, Self::InitError>>;

    fn new_transform(&self, service: S) -> Self::Future {
        ready(Ok(BodyLoggerMiddleware {
            service: Rc::new(service),
        }))
    }
}

pub struct BodyLoggerMiddleware<S> {
    service: Rc<S>,
}

impl<S, B> Service<ServiceRequest> for BodyLoggerMiddleware<S>
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
    S::Future: 'static,
    B: 'static,
{
    type Response = ServiceResponse<B>;
    type Error = Error;
    type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;

    dev::forward_ready!(service);

    fn call(&self, mut req: ServiceRequest) -> Self::Future {
        let svc = self.service.clone();
        Box::pin(async move {
            let mut body = BytesMut::new();
            let mut stream = req.take_payload();

            while let Some(chunk) = stream.next().await {
                body.extend_from_slice(&chunk?);
            }

            println!("request body: {body:?}");
            let res = svc.call(req).await?;

            println!("response: {:?}", res.headers());
            Ok(res)
        })
    }
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .wrap(BodyLogger)
            .route("/", web::to(|body: String| async move { body }))
    })
    .bind(("127.0.0.1", 8080))?
    .workers(1)
    .run()
    .await
}

robjtede avatar Sep 11 '22 15:09 robjtede

will re-open if reproduction is confirmed

robjtede avatar Oct 26 '22 16:10 robjtede

image image image

If I comment out these three lines, the program can be accessed normally.

If I add these three lines, the program has problems and cannot be injected into the control layer

image image

liunn123 avatar Oct 27 '22 03:10 liunn123

@15249687908 that's not a panic, if you think this is a legitimate problem, please open a new issue with full minimal example of problem

robjtede avatar Oct 27 '22 07:10 robjtede

ok

liunn123 avatar Oct 27 '22 08:10 liunn123