thruster-rs / Thruster

A fast, middleware based, web framework written in Rust
MIT License
1.06k stars 47 forks source link

Get POST body without introducing crash bug? #195

Closed williamleuschner closed 2 years ago

williamleuschner commented 3 years ago

Is it possible to get the body of a POST request without introducing a crash bug? I've made up a quick echo server project to learn how to handle POST bodies, and I can't find a way to read the request body without causing a panic, either from copying the context or as the only way to handle the error when the request is aborted while reading the body.

If I try to change the post_echo function to handle the error with a ThrusterError, I find that I can't construct it with the context passed in as an argument because context.get_body() causes a move, and I can't clone the context because the implementation for TypedHyperContext::Copy is a panic!(). I also can't copy the TypedHyperContext myself, because some of the fields are private.

Am I missing another way to get the body of a request? Is there some other way to use this API that I'm not seeing?

main.rs

use hyper::Body;
use thruster::context::hyper_request::HyperRequest;
use thruster::context::typed_hyper_context::TypedHyperContext;

use thruster::hyper_server::HyperServer;
// Required for ::new() implementation on HyperServer.
use thruster::ThrusterServer;
use thruster::{async_middleware, middleware_fn};
use thruster::App;
use thruster::{MiddlewareNext, MiddlewareResult};

type Ctx = TypedHyperContext<()>;

#[middleware_fn]
async fn post_echo(context: Ctx, _next: MiddlewareNext<Ctx>) -> MiddlewareResult<Ctx> {
    match context.get_body().await {
        Ok((body, mut context)) => {
            context.body = Body::from(body);
            Ok(context)
        },
        Err(_) => panic!("oops"),
    }
}

fn generate_context(request: HyperRequest, _state: &(), _path: &str) -> Ctx {
    Ctx::new(request, ())
}

fn main() {
    println!("Starting server...");

    let mut app = App::<HyperRequest, Ctx, ()>::create(generate_context, ());

    app.post("/echo", async_middleware!(Ctx, [post_echo]));

    let server = HyperServer::new(app);
    server.start("0.0.0.0", 4321);
}

Cargo.toml

[package]
name = "thruster-echo"
version = "0.1.0"
authors = ["nobody"]
edition = "2018"

[dependencies]
thruster = { version = "1", features = ["hyper_server"] }
hyper = "0.14.10"

(To reproduce the crash, create a POST request with a large enough body that you can kill the client halfway through sending it.)

trezm commented 3 years ago

Howdy there! I'll take a look as soon as I can! I'm on my honeymoon right now though, so you might have to wait a few days :-)

Would something using


context
.hyper_request
.as_ref()
.unwrap()
.request ``` Work to get access to the raw body?

On Wed, Jul 21, 2021, 6:42 PM williamleuschner ***@***.***>
wrote:

> Is it possible to get the body of a POST request without introducing a
> crash bug? I've made up a quick echo server project to learn how to handle
> POST bodies, and I can't find a way to read the request body without
> causing a panic, either from copying the context or as the only way to
> handle the error when the request is aborted while reading the body.
>
> If I try to change the post_echo function to handle the error with a
> ThrusterError, I find that I can't construct it with the context passed
> in as an argument because context.get_body() causes a move, and I can't
> clone the context because the implementation for TypedHyperContext::Copy
> is a panic!(). I also can't copy the TypedHyperContext myself, because
> some of the fields are private.
>
> Am I missing another way to get the body of a request? Is there some other
> way to use this API that I'm not seeing?
> main.rs
>
> use hyper::Body;use thruster::context::hyper_request::HyperRequest;use thruster::context::typed_hyper_context::TypedHyperContext;
> use thruster::hyper_server::HyperServer;// Required for ::new() implementation on HyperServer.use thruster::ThrusterServer;use thruster::{async_middleware, middleware_fn};use thruster::App;use thruster::{MiddlewareNext, MiddlewareResult};
> type Ctx = TypedHyperContext<()>;
>
> #[middleware_fn]async fn post_echo(context: Ctx, _next: MiddlewareNext<Ctx>) -> MiddlewareResult<Ctx> {
>     match context.get_body().await {
>         Ok((body, mut context)) => {
>             context.body = Body::from(body);
>             Ok(context)
>         },
>         Err(_) => panic!("oops"),
>     }
> }
> fn generate_context(request: HyperRequest, _state: &(), _path: &str) -> Ctx {
>     Ctx::new(request, ())
> }
> fn main() {
>     println!("Starting server...");
>
>     let mut app = App::<HyperRequest, Ctx, ()>::create(generate_context, ());
>
>     app.post("/echo", async_middleware!(Ctx, [post_echo]));
>
>     let server = HyperServer::new(app);
>     server.start("0.0.0.0", 4321);
> }
>
> Cargo.toml
>
> [package]name = "thruster-echo"version = "0.1.0"authors = ["nobody"]edition = "2018"
>
> [dependencies]thruster = { version = "1", features = ["hyper_server"] }hyper = "0.14.10"
>
> (To reproduce the crash, create a POST request with a large enough body
> that you can kill the client halfway through sending it.)
>
> —
> You are receiving this because you are subscribed to this thread.
> Reply to this email directly, view it on GitHub
> <https://github.com/thruster-rs/Thruster/issues/195>, or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AAJWOLKSZ4WZ47EQ64V7GELTY6OTNANCNFSM5AZHP3WQ>
> .
>
williamleuschner commented 3 years ago

Oh wow, enjoy your honeymoon, and congratulations! Take your time responding, you've got better things to do :P

That suggestion seems to be pointing in the right direction, I'm working on figuring out how to access the data that way. I'll update this issue once I find a reasonable way.