[][src]Crate amiya

Amiya is a experimental middleware-based minimalism async HTTP server framework built up on the smol async runtime.

It's currently still working in progress and in a very early alpha stage.

API design may changes every day, DO NOT use it in any condition except for test or study!

Goal

The goal of this project is try to build a (by importance order):

  • Safe, with #![forbid(unsafe_code)]
  • Async
  • Minimalism
  • Easy to use
  • Easy to extend

HTTP framework for myself to write simple web services.

Amiya uses async-h1 to parse and process requests, so only HTTP version 1.1 is supported for now. HTTP 1.0 or 2.0 is not in goal list, at least in the near future.

Performance is NOT in the list too, after all, Amiya is just a experimental for now, it uses many heap alloc (Box) and dynamic dispatch (Trait Object) so there may be some performance loss compare to use async-h1 directly.

Concepts

To understand how this framework works, there are some concept need to be described first.

Request, Response and the process pipeline

For every HTTP request comes to a Amiya server, the framework will create a Request struct to represent it. It's immutable in the whole request process pipeline.

And a Response is created at the same time. It's a normal 200 OK empty header empty body response at first, but it's mutable and can be edit by middleware.

After all middleware has been executed, the Response maybe edited by many middleware, and as the final result we will send to the client.

Middleware

For ease of understanding, you can think this word is a abbreviation of "A function read some propety of Request and edit Response" or, a request handler, for now.

Context

But middleware do not works on Request and Response directly. Context wraps the immutable Request and mutable Response with some other information and shortcut methods.

Onion model

The execution process of middleware uses the onion model:

We reuse this famous picture from Nodejs' Koa framework.

If we add middleware A, B and C to Amiya server, the running order(if not interrupted in the middle) will be: A -> B -> C -> C -> B -> A

So every middleware will be executed twice, but this does not mean same code is executed twice.

That's why next method exists.

next

The most important method Context gives us is next.

When a middleware calls ctx.next().await, the method will return after all inner middleware finish, or, some of them returns a Error.

there is a simplest example:

use amiya::{Context, Result, m};

async fn a(mut ctx: Context<'_, ()>) -> Result {
    println!("A - before");
    ctx.next().await?;
    println!("A - out");
    Ok(())
}

async fn b(mut ctx: Context<'_, ()>) -> Result {
    println!("B - before");
    ctx.next().await?;
    println!("B - out");
    Ok(())
}

async fn c(mut ctx: Context<'_, ()>) -> Result {
    println!("C - before");
    ctx.next().await?;
    println!("C - out");
    Ok(())
}

let amiya = amiya::new().uses(m!(a)).uses(m!(b)).uses(m!(c));

When a request in, the output will be:

A - before
B - before
C - before
C - after
B - after
A - after

You can referer to examples/middleware.rs for a more meaningful example.

Middleware, the truth

So with the help of next method, a middleware can not only be a request handler, it can be:

  • a error handler, by catpure inner middleware's return Result
  • a Router, by looking the path then delegate Context to other corresponding middleware
  • a access logger or time measurer, by print log before and after the next call
  • etc...

A middleware even does not have to call next, in that statution no inner middlewares will be executed. Middleware like Router or login state checker can use this mechanism to make unprocessable requests responsed early.

You can create you own Middleware by implement the trait for your type, or using the m macro, see their document for detail.

Examples

To start a very simple HTTP service that returns Hello World to the client in all paths:

use amiya::m;

fn main() {
    let app = amiya::new().uses(m!(ctx =>
        ctx.resp.set_body(format!("Hello World from: {}", ctx.path()));
    ));

    let fut = app.listen("[::]:8080");

    // ... start a async runtime and block on `fut` ...
}

You can await or block on this fut to start the service.

See Readme - Examples section for more examples to check.

Re-exports

pub use middleware::Middleware;

Modules

middleware

Built-in middleware.

Macros

m

Writer middleware easily.

Structs

Amiya

Amiya HTTP Server.

Context

The context middleware works on.

Mime

An IANA media type.

Request

An HTTP request.

Response

An HTTP response.

Enums

Method

HTTP request methods.

StatusCode

HTTP response status codes.

Functions

new

Create a Amiya instance with extra data type ().

with_ex

Create a Amiya instance with user defined extra data.

Type Definitions

Result

The Result type all middleware should returns.

Attribute Macros

async_trait