Iron

Jonathan Reem                                                                                 Iron

A Type Driven Web Framework

About me 

  • <3 OSS, Rust
  • SE at Terminal.com
  • github.com/reem

Jonathan Reem                                                                                 Iron

Jonathan Reem                                                                                 Iron

Say Hello!

extern crate iron;

use iron::prelude::*;
use iron::status;

fn main() {
   Iron::new(|req: &mut Request| {
       Response::with((status::Ok, "Hello!"))
   }).http(("127.0.0.1", 3000)).unwrap()
}

Jonathan Reem                                                                                 Iron

Let's Break it Down

extern crate iron;

use iron::prelude::*;
use iron::status;

fn main() {
   Iron::new(|req: &mut Request| {
       Response::with((status::Ok, "Hello!"))
   }).http(("127.0.0.1", 3000)).unwrap()
}

Jonathan Reem                                                                                 Iron

Let's Break it Down

extern crate iron;

use iron::prelude::*;
use iron::status;

fn main() {
   Iron::new(|req: &mut Request| {
       Response::with((status::Ok, "Hello!"))
   }).http(("127.0.0.1", 3000)).unwrap()
}
impl<H> Iron<H> where H: Handler {
    pub fn new(handler: H) -> Iron<H>;
}

Jonathan Reem                                                                                 Iron

Handlers

pub trait Handler: Send + Sync + Any {
    fn handle(&self, &mut Request) -> IronResult<Response>;
}
  • Like "controllers", but less baggage
  • Can be closures:
impl<F> Handler for F
where F: Send + Sync + Any,
      F: Fn(&mut Request) -> IronResult<Response> {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        (*self)(req)
    }
}

Jonathan Reem                                                                                 Iron

Handlers

pub trait Handler: Send + Sync + Any {
    fn handle(&self, &mut Request) -> IronResult<Response>;
}
  • All requests go through a Handler
  • Can error but always produce a Response
  • Accessed concurrently
  • Can (and often do) contain other Handlers
  • ex: Router, Mount, Static, Chain, &c.

Jonathan Reem                                                                                 Iron

Our Handler

|req: &mut Request| -> IronResult<Response> {
    Ok(Response::with((status::Ok, "Hello!")))
}

What is Response::with?

impl Response {
    pub fn with<M: Modifier<Self>>(modifier: M) -> Self {
        Response::new().set(modifier)
    }
}

Jonathan Reem                                                                                 Iron

Modifiers

Come from the `modifier` crate

pub trait Modifier<F: ?Sized> {
    fn modify(self, &mut F);
}

pub trait Set {
    fn set<M: Modifier<Self>>(mut self, modifier: M) -> Self where Self: Sized {
        modifier.modify(&mut self);
        self
    }

    fn set_mut<M: Modifier<Self>>(&mut self, modifier: M) -> &mut Self {
        modifier.modify(self);
        self
    }
}

That's it!

Jonathan Reem                                                                                 Iron

Modifiers

Allow chaining with both `Self` and `&mut self`:

let res = get_response()
    .set(status::Ok)
    .set("This will become the body!")
    .set(Path::new("./or/this/file.txt"))
    .set(Redirect(Url::parse("localhost:3000/to/here")));

let response = &mut res;

response
    .set_mut(status::Ok)
    .set_mut("This will become the body!")
    .set_mut(Path::new("./or/this/file.txt"))
    .set_mut(Redirect(Url::parse("localhost:3000/to/here")));

Jonathan Reem                                                                                 Iron

Modifiers

Can define new Modifiers anywhere

pub struct NewHeaders(Headers);

impl Modifier<Response> for NewHeaders {
    fn modify(self, response: &mut Response) {
        response.headers.extend(self.0)
    }
}

// Now we can do res.set(NewHeaders(headers))

Jonathan Reem                                                                                 Iron

Modifiers

Can be composed as tuples!

impl<T, M1, M2> Modifier<T> for (M1, M2)
where M1: Modifier<T>, M2: Modifier<T> {
    fn modify(self, target: &mut T) {
        self.0.modify(target);
        self.1.modify(target);
    }
}

// And so on...

(M1, M2..) implements Modifier if M* does

Jonathan Reem                                                                                 Iron

Coming back...

extern crate iron;

use iron::prelude::*;
use iron::status;

fn main() {
   Iron::new(|req: &mut Request| {
       Response::with((status::Ok, "Hello!"))
   }).http(("127.0.0.1", 3000)).unwrap()
}

We now understand all of this!

Jonathan Reem                                                                                 Iron

Let's talk about Errors

What is an IronError?

pub struct IronError {
    pub error: Box<Error + Send>,
    pub response: Response
}
  • IronError answers 2 questions:
    • What happened?
    • What should we do about it?

Jonathan Reem                                                                                 Iron

Middleware and Chain

  • Middleware provided through Chain:
pub struct Chain { .. }

impl Handler for Chain { .. }

impl Chain {
    fn new<H: Handler>(H) -> Self;
    fn link_before<B: BeforeMiddleware>(&mut self, before: B);
    fn link_after<A: AfterMiddleware>(&mut self, after: A);
    fn around<A: AroundMiddleware>(&mut self, around: A);
}

No magic! Chain could be a third-party type.

Jonathan Reem                                                                                 Iron

Middleware

  • Three Kinds:
    • BeforeMiddleware
    • AfterMiddleware
    • AroundMiddleware

Jonathan Reem                                                                                 Iron

BeforeMiddleware

pub trait BeforeMiddleware {
    fn before(&self, &mut Request) -> IronResult<()>;
    fn catch(&self, _: &mut Request,
             err: IronError) -> IronResult<()> { Err(err) }
}
  • Run before the Handler
  • Only have access to the Request
  • Can respond to errors in other BM

Jonathan Reem                                                                                 Iron

AfterMiddleware

pub trait AfterMiddleware {
    fn after(&self, &mut Request, Response) -> IronResult<Response>;
    fn catch(&self, _: &mut Request,
             err: IronError) -> IronResult<()> { Err(err) }
}
  • Run after the Handler
  • Have access to both Request and Response
  • Can respond to all errors

Jonathan Reem                                                                                 Iron

AroundMiddleware

pub trait AfterMiddleware {
    fn around(self, handler: Box<Handler>) -> Box<Handler>;
}
  • `around` is called immediately
  • Used to wrap or modify the Handler in a Chain
  • Lets you add arbitrary "control flow"

Jonathan Reem                                                                                 Iron

Plugins and .extensions

  • Contain TypeMap, a reflection-based heterogenous key-value store
  • Can store arbitrary typed data
  • Keys are types
  • Used to extend Request and Response

{Request, Response}.extensions:

Iron

By Jonathan Reem

Iron

  • 1,342