Skip to content

Instantly share code, notes, and snippets.

@reem
Created August 21, 2014 22:52
Show Gist options
  • Save reem/f13f0254f4eeced42b28 to your computer and use it in GitHub Desktop.
Save reem/f13f0254f4eeced42b28 to your computer and use it in GitHub Desktop.
#![license = "MIT"]
extern crate error;
use std::sync::Arc;
use error::Error;
pub type IronResult<T> = Result<T, Box<Error>>;
pub struct Request;
pub struct Response;
#[deriving(Clone)]
struct IronListener {
handler: Arc<Box<Handler + Send + Sync>>
}
pub trait Handler: Send + Sync {
fn call(&self, &mut Request) -> IronResult<Response>;
fn catch(&self, &mut Request, Box<Error>) -> (Response, IronResult<()>);
}
impl Handler for fn(&mut Request) -> IronResult<Response> {
fn call(&self, req: &mut Request) -> IronResult<Response> {
self.call(req)
}
fn catch(&self, _: &mut Request, err: Box<Error>) -> (Response, IronResult<()>) {
(Response, Err(err))
}
}
pub trait BeforeMiddleware: Send + Sync {
fn before(&self, &mut Request) -> IronResult<()>;
fn catch(&self, _: &mut Request, err: Box<Error>) -> IronResult<()> {
Err(err)
}
}
pub trait AfterMiddleware: Send + Sync {
fn after(&self, &mut Request, &mut Response) -> IronResult<()>;
// This response was generated by the `catch` function of Handlers and is abnormal in some way.
fn catch(&self, _: &mut Request, _: &mut Response, err: Box<Error>) -> IronResult<()> {
Err(err)
}
}
pub trait AroundMiddleware: Handler {
fn with_handler(&mut self, handler: Box<Handler + Send + Sync>);
}
pub trait Chain: Handler {
fn new<H: Handler>(H) -> Self;
}
pub struct StackChain {
befores: Vec<Box<BeforeMiddleware + Send + Sync>>,
afters: Vec<Box<AfterMiddleware + Send + Sync>>,
handler: Box<Handler + Send + Sync>
}
impl Handler for StackChain {
fn call(&self, req: &mut Request) -> IronResult<Response> {
let before_result = run_befores(req, self.befores.as_slice(), None);
let (res, err) = match before_result {
Ok(()) => match self.handler.call(req) {
Ok(res) => (res, None),
Err(e) => run_handler_catch(req, e, &self.handler)
},
Err(e) => run_handler_catch(req, e, &self.handler)
};
run_afters(req, res, err, self.afters.as_slice())
}
fn catch(&self, req: &mut Request, err: Box<Error>) -> (Response, IronResult<()>) {
let before_result = run_befores(req, self.befores.as_slice(), Some(err));
let (res, err) = match before_result {
Ok(()) => match self.handler.call(req) {
Ok(res) => (res, None),
Err(e) => run_handler_catch(req, e, &self.handler)
},
Err(e) => run_handler_catch(req, e, &self.handler)
};
match run_afters(req, res, err, self.afters.as_slice()) {
Ok(res) => (res, Ok(())),
Err(err) => (Response, Err(err))
}
}
}
impl Chain for StackChain {
fn new<H: Handler>(handler: H) -> StackChain {
StackChain {
befores: vec![],
afters: vec![],
handler: box handler as Box<Handler + Send + Sync>
}
}
}
fn run_befores(req: &mut Request, befores: &[Box<BeforeMiddleware>], err: Option<Box<Error>>) -> IronResult<()> {
match err {
Some(mut e) => {
for (i, before) in befores.iter().enumerate() {
match before.catch(req, e) {
Ok(_) => return run_befores(req, befores, None),
Err(new) => e = new
}
}
Err(e)
},
None => {
for (i, before) in befores.iter().enumerate() {
match before.before(req) {
Ok(_) => (),
Err(err) => return run_befores(req, befores.slice_from(i), Some(err))
}
}
Ok(())
}
}
}
fn run_afters(req: &mut Request, mut res: Response, err: Option<Box<Error>>,
afters: &[Box<AfterMiddleware>]) -> IronResult<Response> {
match err {
Some(mut e) => {
for (i, after) in afters.iter().enumerate() {
match after.catch(req, &mut res, e) {
Ok(_) => return run_afters(req, res, None, afters),
Err(new) => e = new
}
}
Err(e)
},
None => {
for (i, after) in afters.iter().enumerate() {
match after.after(req, &mut res) {
Ok(_) => (),
Err(err) => return run_afters(req, res, Some(err), afters.slice_from(i))
}
}
Ok(res)
}
}
}
fn run_handler_catch(req: &mut Request, err: Box<Error>,
handler: &Box<Handler>) -> (Response, Option<Box<Error>>) {
match handler.catch(req, err) {
(res, Ok(())) => (res, None),
(res, Err(e)) => (res, Some(e))
}
}
impl Handler for Box<Handler + Send + Sync> {
fn call(&self, req: &mut Request) -> IronResult<Response> {
self.call(req)
}
fn catch(&self, req: &mut Request, err: Box<Error>) -> (Response, IronResult<()>) {
self.catch(req, err)
}
}
impl Handler for Arc<Box<Handler + Send + Sync>> {
fn call(&self, req: &mut Request) -> IronResult<Response> {
self.call(req)
}
fn catch(&self, req: &mut Request, err: Box<Error>) -> (Response, IronResult<()>) {
self.catch(req, err)
}
}
@zzmp
Copy link

zzmp commented Aug 21, 2014

  • What is the purpose of AroundMiddleware?
  • Why does runbefores need to take an error in its signature? Shouldn't it always be run from a clean state?
  • Won't runafters be called twice in the event of an error: once through handler.call, and once through handler.catch?
    • It looks like runafters can be called twice in the event that an error is handled - just want to confirm that this is a correct reading.
  • I like that middleware can declare an error handled (L114). πŸ‘
  • Do you intend to only allow one handler? Isn't this less flexible? πŸ‘Ž

@reem
Copy link
Author

reem commented Aug 21, 2014

The purpose of AroundMiddleware is exactly to address that you can only have one handler, by allowing you to arbitrarily augment that handler.

You can also nest handlers using Mount or Router, so you have a master handler that internally calls another handler (just like here, Chain is a handler but calls a sub-handler).

run_befores needs to take an Error because it can be called in an errored state in the implementation of Handler::catch for StackChain.

The implementations of run_befores and run_afters could be made a bit shorter with some code sharing, but I was aiming to just get something working clearly.

@theptrk
Copy link

theptrk commented Aug 22, 2014

Looks like on ln 67 run_befores is always passed a None value as the third argument. Where does it ever get passed in an Error that it should handle?

πŸ‘

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment