[][src]Crate roa_core

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_core::App;
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(|ctx| async move {
        ctx.resp_mut().await.write_str("Hello, World");
        Ok(())
    });
    app.listen("127.0.0.1:8000", |addr| {
        info!("Server is listening on {}", addr)
    })?
    .await?;
    Ok(())
}

Cascading

The following example responds with "Hello World", however, the request flows through the logging middleware to mark when the request started, then continue to yield control through the response middleware. When a middleware invokes next().await 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_core::App;
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(());
    app.gate_fn(|_ctx, next| async move {
        let inbound = Instant::now();
        next().await?;
        info!("time elapsed: {} ms", inbound.elapsed().as_millis());
        Ok(())
    });

    app.end(|ctx| async move {
        ctx.resp_mut().await.write_str("Hello, World");
        Ok(())
    });
    app.listen("127.0.0.1:8000", |addr| {
        info!("Server is listening on {}", addr)
    })?
    .await?;
    Ok(())
}

Error Handling

You can catch or straightly throw a Error returned by next.

use roa_core::{App, throw};
use async_std::task::spawn;
use http::StatusCode;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let (addr, server) = 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!")
        })
        .run_local()?;
    spawn(server);
    let resp = reqwest::get(&format!("http://{}", addr)).await?;
    assert_eq!(StatusCode::OK, resp.status());
    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, Model, ErrorKind};
pub async fn error_handler<M: Model>(context: Context<M>, err: Error) -> Result {
    context.resp_mut().await.status = err.status_code;
    if err.expose {
        context.resp_mut().await.write_str(&err.message);
    }
    if err.kind == ErrorKind::ServerError {
        Err(err)
    } else {
        Ok(())
    }
}

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

Use custom runtime.

Use hyper::Server::builder to construct a hyper server with your custom runtime.

Example

use roa_core::{App, Server, AddrIncoming, Executor};
use std::future::Future;

/// An implementation of hyper::rt::Executor based on tokio
#[derive(Copy, Clone)]
pub struct Exec;

impl<F> Executor<F> for Exec
where
    F: 'static + Send + Future,
    F::Output: 'static + Send,
{
    #[inline]
    fn execute(&self, fut: F) {
        tokio::task::spawn(fut);
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let app = App::new(());
    let server = Server::builder(AddrIncoming::bind("127.0.0.1:8080")?)
        .executor(Exec)
        .serve(app);
    server.await?;
    Ok(())
}

Modules

header

HTTP header types

Macros

throw

Throw an Err(Error).

Structs

AddrIncoming

A stream of connections from binding to an address. As an implementation of hyper::server::accept::Accept.

App

The Application of roa.

Body

The Body of Request and Response.

Bucket

A wrapper of HashMap<String, String>, method get return a Variable.

Context

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

Error

The Error of roa.

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.

StatusCode

An HTTP status code (status-code in RFC 7230 et al.).

Variable

A wrapper of String.

Enums

ErrorKind

Kind of Error.

Traits

Executor

An executor of futures.

Middleware

Middleware

Model

The Model trait. The new_state method will be called when a request inbound.

State

The State trait, should be replace with trait alias.

Functions

join

Join two middleware.

join_all

Join all middlewares in a vector.

Type Definitions

BodyCallback

Callback when body is finished.

Next

Type of the second parameter in a middleware.

Result

Type alias for StdResult<R, Error>.

ResultFuture

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

Attribute Macros

async_trait