Crate amiya[][src]

Amiya is a experimental middleware-based minimalism async HTTP server framework, built up on smol-rs related asynchronous components.

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
  • Middleware-based
  • 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 property 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 Python’s Pylons 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 referrer 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 capture inner middleware returned 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 situation no inner middleware will be executed. Middleware like Router or login state checker can use this mechanism to make invalid requests respond 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;

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

app.listen("[::]:8080").unwrap();

// ... do other things ...

Amiya has a built-in multi-thread async executor powered by async-executor and async-io, amiya server will run in it. So Amiya::listen do not block your thread.

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.

BuiltInExecutor

Amiya built-in multi-thread async executor.

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.

Traits

Executor

Provide you custom async executor to Amiya by impl this trait.

Functions

new

Create a Amiya instance with extra data type ().

with_ex

Create a Amiya instance with user defined extra data.

Type Definitions

Error

The Error type of middleware result type.

Result

The Result type all middleware should returns.

Attribute Macros

async_trait