[][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, Context};
use roa::preload::*;
use log::info;
use std::error::Error as StdError;

async fn end(ctx: &mut Context<()>) -> roa::Result {
    ctx.write_text("Hello, World");
    Ok(())
}

#[async_std::main]
async fn main() -> Result<(), Box<dyn StdError>> {
    let app = App::new(()).end(end);
    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, Context, Next};
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 app = App::new(())
        .gate(logger)
        .gate(x_response_time)
        .end(response);
    app.listen("127.0.0.1:8000", |addr| {
        info!("Server is listening on {}", addr)
    })?
    .await?;
    Ok(())
}

async fn logger(ctx: &mut Context<()>, next: Next<'_>) -> roa::Result {
    next.await?;
    let rt = ctx.resp.must_get("x-response-time")?;
    info!("{} {} - {}", ctx.method(), ctx.uri(), rt);
    Ok(())
}

async fn x_response_time(ctx: &mut Context<()>, next: Next<'_>) -> roa::Result {
    let start = Instant::now();
    next.await?;
    let ms = start.elapsed().as_millis();
    ctx.resp.insert("x-response-time", format!("{}ms", ms))?;
    Ok(())
}

async fn response(ctx: &mut Context<()>) -> roa::Result {
    ctx.write_text("Hello, World");
    Ok(())
}

Error Handling

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

use roa::{App, Context, Next, 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>> {
    let app = App::new(())
        .gate(catch)
        .gate(not_catch)
        .end(error);
    app.listen("127.0.0.1:8000", |addr| {
        info!("Server is listening on {}", addr)
    })?
    .await?;
    Ok(())
}

async fn catch(_ctx: &mut Context<()>, next: Next<'_>) -> roa::Result {
    // catch
    if let Err(status) = next.await {
        // teapot is ok
        if status.status_code != StatusCode::IM_A_TEAPOT {
            return Err(status);
        }
    }
    Ok(())
}

async fn not_catch(ctx: &mut Context<()>, next: Next<'_>) -> roa::Result {
    next.await?; // just throw
    unreachable!()
}

async fn error(ctx: &mut Context<()>) -> roa::Result {
    throw!(StatusCode::IM_A_TEAPOT, "I'm a teapot!")
}

status_handler

App has an status_handler to handle status thrown by the top middleware. This is the status_handler:

use roa::{Context, Status, Result, State};
pub async fn status_handler<S: State>(ctx: &mut Context<S>, status: Status) -> Result {
    ctx.resp.status = status.status_code;
    if status.expose {
        ctx.resp.write(status.message.clone());
    }
    if status.status_code.as_u16() / 100 == 5 {
        // internal server error, throw to hyper
        Err(status)
    } else {
        Ok(())
    }
}

The status thrown by this status_handler will be handled by hyper.

Router.

Roa provides a configurable and nestable router.

use roa::preload::*;
use roa::router::{Router, get};
use roa::{App, Context};
use async_std::task::spawn;
use log::info;

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

async fn end(ctx: &mut Context<()>) -> roa::Result {
    // get "/user/1", then id == 1.
    let id: u64 = ctx.must_param("id")?.parse()?;
    // do something
    Ok(())
}

Query

Roa provides a middleware query_parser.

use roa::preload::*;
use roa::query::query_parser;
use roa::{App, Context};
use async_std::task::spawn;
use log::info;

async fn must(ctx: &mut Context<()>) -> roa::Result {
    // request "/?id=1", then id == 1.
    let id: u64 = ctx.must_query("id")?.parse()?;
    Ok(())
}

#[async_std::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let app = App::new(())
        .gate(query_parser)
        .end(must);
    app.listen("127.0.0.1:8080", |addr| {
        info!("Server is listening on {}", addr)
    })?
    .await?;     
    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.

Modules

body

This module provides a context extension PowerBody.

compresscompress

This module provides a middleware Compress.

cookiecookies

This module provides a middleware cookie_parser and context extensions CookieGetter and CookieSetter.

cors

This module provides a middleware Cors.

dieselorm

This crate provides two context extensions.

forward

This module provides a context extension Forward, which is used to parse X-Forwarded-* headers.

header

This module provides a Request/Response extension FriendlyHeaders.

http

A general purpose library of common HTTP types

jwtjwt

This module provides middleware JwtGuard and a context extension JwtVerifier.

logger

This module provides a middleware logger.

pgpg

This module provides integration with tokio-postgres.

preload

Reexport all extensional traits.

query

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

routerrouter

The router module of roa. This crate provides many endpoint wrappers like Router, Dispatcher and a context extension RouterParam.

tcp

This crate provides an acceptor implementing roa_core::Accept and an app extension.

tlstls

This crate provides an acceptor implementing roa_core::Accept and an app extension.

tokiotokio_rt

This crate provides tokio-based runtime and acceptor for roa.

websocketwebsocket

This module provides a websocket endpoint.

Macros

throw

Throw an Err(Status).

Structs

AddrStream

A transport returned yieled by AddrIncoming.

App

The Application of roa.

Boxed

Boxed endpoint.

Chain

A middleware composing and executing other middlewares in a stack-like manner.

Context

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

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.

Shared

Shared middleware.

Status

The Status of roa.

Variable

A variable.

Enums

Body

The body of response.

Traits

Accept

Asynchronously accept incoming connections.

Endpoint

Endpoint

EndpointExt

Extra methods of endpoint.

Middleware

Middleware

MiddlewareExt

A set of method to chain middleware/endpoint to middleware or make middleware shared.

Spawn

Executor constraint.

State

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

Type Definitions

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