[][src]Crate roa

Build status codecov Rust Docs Crate version Download Version License: MIT

Introduction

Roa is an async web framework inspired by koajs, lightweight but powerful.

Application

A Roa application is a structure containing a middleware group which composes and executes middleware functions in a stack-like manner.

The obligatory hello world application:

use roa::App;
use roa::preload::*;
use log::info;
use std::error::Error as StdError;

#[async_std::main]
async fn main() -> Result<(), Box<dyn StdError>> {
    let mut app = App::new(());
    app.end(|mut ctx| async move {
        ctx.write_text("Hello, World")
    });
    app.listen("127.0.0.1:8000", |addr| {
        info!("Server is listening on {}", addr)
    })?
    .await?;
    Ok(())
}

Cascading

Like koajs, middleware suspends and passes control to "downstream" by invoking next.await. Then control flows back "upstream" when next.await returns.

The following example responds with "Hello World", however first the request flows through the x-response-time and logging middleware to mark when the request started, then continue to yield control through the response middleware. When a middleware invokes next the function suspends and passes control to the next middleware defined. After there are no more middleware to execute downstream, the stack will unwind and each middleware is resumed to perform its upstream behaviour.

use roa::App;
use roa::preload::*;
use log::info;
use std::error::Error as StdError;
use std::time::Instant;

#[async_std::main]
async fn main() -> Result<(), Box<dyn StdError>> {
    let mut app = App::new(());
    // logger
    app.gate_fn(|ctx, next| async move {
      next.await?;
      let rt = ctx.resp().must_get("x-response-time")?;
      info!("{} {} - {}", ctx.method(), ctx.uri(), rt);
      Ok(())
    });

    // x-response-time
    app.gate_fn(|mut ctx, next| async move {
        let start = Instant::now();
        next.await?;
        let ms = start.elapsed().as_millis();
        ctx.resp_mut().insert("x-response-time", format!("{}ms", ms))?;
        Ok(())
    });

    // response
    app.end(|mut ctx| async move {
        ctx.write_text("Hello, World")
    });

    app.listen("127.0.0.1:8000", |addr| {
        info!("Server is listening on {}", addr)
    })?
    .await?;
    Ok(())
}

Error Handling

You can catch or straightly throw an error returned by next.

use roa::{App, throw};
use roa::preload::*;
use roa::http::StatusCode;
use async_std::task::spawn;
use log::info;

#[async_std::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    App::new(())
        .gate_fn(|ctx, next| async move {
            // catch
            if let Err(err) = next.await {
                // teapot is ok
                if err.status_code != StatusCode::IM_A_TEAPOT {
                    return Err(err)
                }
            }
            Ok(())
        })
        .gate_fn(|ctx, next| async move {
            next.await?; // just throw
            unreachable!()
        })
        .end(|_ctx| async move {
            throw!(StatusCode::IM_A_TEAPOT, "I'm a teapot!")
        })
        .listen("127.0.0.1:8000", |addr| {
            info!("Server is listening on {}", addr)
        })?
        .await?;
    Ok(())
}

error_handler

App has an error_handler to handle error thrown by the top middleware. This is the error_handler:

use roa_core::{Context, Error, Result, State, ErrorKind};
pub async fn error_handler<S: State>(mut context: Context<S>, err: Error) -> Result {
    // set status code to err.status_code.
    context.resp_mut().status = err.status_code;
    if err.expose {
        // write err.message to response body if err.expose.
        context.resp_mut().write_str(&err.message);
    }
    if err.kind == ErrorKind::ServerError {
        // thrown to hyper
        Err(err)
    } else {
        // caught
        Ok(())
    }
}

The error thrown by this error_handler will be handled by hyper.

Router.

Roa provides a configurable and nestable router.

use roa::preload::*;
use roa::router::Router;
use roa::App;
use async_std::task::spawn;
use log::info;

#[async_std::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut router = Router::<()>::new();
    // get dynamic "/:id"
    router.get("/:id", |ctx| async move {
        let id: u64 = ctx.must_param("id")?.parse()?;
        // do something
        Ok(())
    });
    App::new(())
        // route with prefix "/user"
        .gate(router.routes("/user")?)
        .listen("127.0.0.1:8000", |addr| {
            info!("Server is listening on {}", addr)
        })?
        .await?;
     
    // get "/user/1", then id == 1.
    Ok(())
}

Query

Roa provides a middleware query_parser.

use roa::preload::*;
use roa::query::query_parser;
use roa::App;
use async_std::task::spawn;
use log::info;

#[async_std::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    App::new(())
        .gate(query_parser)
        .end( |ctx| async move {
            let id: u64 = ctx.must_query("id")?.parse()?;
            Ok(())
        })
        .listen("127.0.0.1:8080", |addr| {
            info!("Server is listening on {}", addr)
        })?
        .await?;     
    // request "/?id=1", then id == 1.
    Ok(())
}

Other modules

  • body: dealing with body more conviniently.
  • compress: supports transparent content compression.
  • cors: CORS support.
  • forward: "X-Forwarded-*" parser.
  • header: dealing with headers more conviniently.
  • jwt: json web token support.
  • logger: a logger middleware.

Re-exports

pub use roa_body as body;
pub use roa_router as router;
pub use roa_tcp as tcp;
pub use roa_tls as tls;
pub use roa_websocket as websocket;

Modules

compress

The compress module of roa. This module provides a middleware Compress.

cookie

The cookie module of roa. This module provides a middleware cookie_parser and a context extension Cookier.

cors

The cors module of roa. This module provides a middleware Cors.

forward

The forward module of roa. This module provides a context extension Forward, which is used to parse X-Forwarded-* request headers.

header

The header module of roa. This module provides a Request/Response extension FriendlyHeaders.

http

A general purpose library of common HTTP types

jwt

The jwt module of roa. This module provides middlewares guard and guard_by and a context extension JwtVerifier.

logger

The logger module of roa. This module provides a middleware logger.

preload

Reexport all extensional traits.

query

The query module of roa. This module provides a middleware query_parser and a context extension Query.

Macros

throw

Throw an Err(Error).

Structs

AddrStream

A transport returned yieled by AddrIncoming.

App

The Application of roa.

Context

A structure to share request, response and other data between middlewares.

Error

The Error of roa.

Executor

A type implementing hyper::rt::Executor

JoinHandle

A handle that awaits the result of a task.

Request

Http request type of roa.

Response

Http response type of roa.

Server

A listening HTTP server that accepts connections in both HTTP1 and HTTP2 by default.

SyncContext

Sync parts of Context.

Variable

A wrapper of Arc.

Enums

ErrorKind

Kind of Error.

Traits

Accept

Asynchronously accept incoming connections.

Middleware

Middleware

Spawn

Executor constraint.

State

The State trait, should be replace with trait alias. The App::state will be cloned when a request inbounds.

Functions

join

Join two middleware.

join_all

Join all middlewares in a vector.

last

The last.

Type Definitions

BlockingObj

Blocking task Object

FutureObj

Future Object

Next

Type of the second parameter in a middleware.

Result

Type alias for StdResult<R, Error>.

ResultFuture

Type alias for Pin<Box<dyn 'a + Future<Output = Result<R>>>>.

Attribute Macros

async_trait