Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better error handling story #168

Open
remexre opened this issue Feb 2, 2019 · 4 comments
Open

Better error handling story #168

remexre opened this issue Feb 2, 2019 · 4 comments

Comments

@remexre
Copy link
Contributor

remexre commented Feb 2, 2019

Right now, I'm handling errors in an (HTMLful) warp app with the following helper:

https://github.com/remexre/nihctfplat/blob/e10ef2befe1bfa07373096b511e81e35284d708e/src/router/util.rs#L56-L96

I'm naturally not thrilled about this; #165 is largely about mitigating the need for the .map(Ok).recover(|e| Ok(Err(e))).unify().and(auth::opt_auth()); is there a nicer way to do something like this that I'm not seeing?

@seanmonstar
Copy link
Owner

I'm sorry, apparently I also can't quite understand what you're trying to do. Care to explain a little more?

@remexre
Copy link
Contributor Author

remexre commented Feb 11, 2019

Right off the bat, I'm converting a fallible filter to an infallible one that extracts a result...

self.map(Ok)
    .recover(|e| Ok(Err(e)))
    .unify()

...so that I can get authentication from the request.

    .and(auth::opt_auth())

Then to actually process the error, I match on the result...

    .and_then(move |res: Result<T, Rejection>, me| match res {

...with success continuing through...

        Ok(r) => Ok(Either::Left(r)),

...and errors being pulled out, with a passed-in func returning the HTTP status, error names, and messages to "flash" (i.e. show on a form when form validation fails).

        Err(r) => match r.find_cause() {
            Some(err) => match func(err) {
                Some((status, codes, flashes)) => {

The authentication object and the flashes are added to the context used to render the template...

                    let mut hm = hashmap! {
                        "flashes" => serde_json::to_value(flashes).unwrap(),
                        "me" => serde_json::to_value(me).unwrap(),
                    };

...and the corresponding errors are enabled...

                    for code in codes {
                        let _ = hm.insert(code, Value::Bool(true));
                    }

...then the error template page is rendered to a Response.

                    render_html(template, hm).map(|mut r| {
                        *r.status_mut() = status;
                        Either::Right(r)
                    })
                }

(If the function didn't handle the error, pass it along.)

                None => Err(r),
            },

(If the error was of the wrong type, pass it along.)

            None => Err(dbg!(r)),
        },
    })

Box<Filter<Extract=(impl Reply,)>> is the return type here.

    .boxed()

Really, my main gripes are:

  • need to do .map().recover().unify() to chain more filters onto the failure
  • need to be in a filter to do warp::ext::get()

@seanmonstar
Copy link
Owner

Thanks for the breakdown! I really understand what is going on now.

I'll give this problem some thought, but do you have any suggestions?

@remexre
Copy link
Contributor Author

remexre commented Feb 11, 2019

idk how much implementation effort it would be, but some way to get a Filter for recovery, like:

foo()
    .and(bar())
    .and_then(...)
    .or(recover().and_then(...))

and probably a get() free function, so render_html could have the auth put into it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants