Crate xitca_web

source ·
Expand description

a web framework focus on memory efficiency, composability, and fast compile time.

§Quick start

use xitca_web::{handler::handler_service, route::get, App};

fn main() -> std::io::Result<()> {
    App::new()
        .at("/", get(handler_service(|| async { "Hello,World!" })))
        .serve()
        .bind("localhost:8080")?
        .run()
        .wait()
}

§Memory efficient

  • zero copy magic types
  • zero cost service tree

§Zero copy

use xitca_web::{
    error::Error,
    handler::{handler_service, json::LazyJson, path::PathRef},
    route::{get, post},
    App
};

// PathRef is able to borrow http request's path string as reference
// without copying it.
async fn url_query(PathRef(path): PathRef<'_>) -> &'static str {
    println!("{path}");
    "zero copy path"    
}

// deserializable user type.
#[derive(serde::Deserialize)]
struct User<'a> {
    name: &'a str
}

// LazyJson is able to lazily deserialize User type with zero copy &str.
async fn json(lazy: LazyJson<User<'_>>) -> Result<&'static str, Error> {
    let User { name } = lazy.deserialize()?;
    println!("{name}");
    Ok("zero copy json")    
}

// Almost all magic extract types in xitca-web utilize zero copy
// to avoid unnecessary memory copy.
App::new()
    // a route handling incoming url query.
    .at("/query", get(handler_service(url_query)))
    // a route handling incoming json object.
    .at("/json", post(handler_service(json)))
    .serve()
    .bind("localhost:8080")?
    .run()
    .wait()

§Zero cost

use xitca_web::{
    handler::{handler_service},
    http::WebResponse,
    route::get,
    middleware::Extension,
    service::{Service, ServiceExt},
    App, WebContext
};
App::new()
    .at("/", get(handler_service(|| async { "hello,world!" })))
    // ServiceExt::enclosed_fn combinator enables async function as middleware.
    // the async function is unboxed and potentially inlined with other async services
    // for efficient binary code with less memory allocation.
    .enclosed_fn(middleware_fn)
    // ServiceExt::enclosed combinator enables type impl Service trait as middleware.
    // the middleware trait method is unboxed and potentially inlined with other async services
    // for efficient binary code with less memory allocation.
    .enclosed(Extension::new(()))
    .serve()
    .bind("localhost:8080")?
    .run()
    .wait()

// a simple middleware just forward request to inner service logic.
async fn middleware_fn<S, T, E>(service: &S, ctx: WebContext<'_>) -> Result<T, E>
where
    S: for<'r> Service<WebContext<'r>, Response = T, Error = E>
{
    service.call(ctx).await
}

§Composable

  • Easy mixture of various level of abstractions and less opinionated APIs
  • Common types and traits for cross crates integration of majority rust web ecosystem

§Abstraction variety

use xitca_web::{
    body::ResponseBody,
    error::Error,
    handler::{handler_service, handler_sync_service, FromRequest},
    http::{Method, WebResponse},
    route::get,
    service::fn_service,
    App, WebContext
};

App::new()
    // high level abstraction. see fn high for detail.
    .at("/high", get(handler_service(high)))
    // low level abstraction. see fn low for detail.
    .at("/low", get(fn_service(low)))
    // abstraction for synchronous. see fn sync for detail.
    .at("/sync", get(handler_sync_service(sync)))
    .serve()
    .bind("localhost:8080")?
    .run()
    .wait()

// magic function with arbitrary receiver type and output type
// that can be extracted from http requests and packed into http
// response.
async fn high(method: &Method) -> &'static str {
    // extract http method from http request.
    assert_eq!(method, Method::GET);
    // pack string literal into http response.
    "high level"     
}

// function with concrete typed input and output where http types
// are handled manually.
async fn low(ctx: WebContext<'_>) -> Result<WebResponse, Error> {
    // manually check http method.
    assert_eq!(ctx.req().method(), Method::GET);

    // high level abstraction can be opt-in explicitly if desired.
    // below is roughly what async fn high did to receive &Method as
    // function argument.
    let method = <&Method>::from_request(&ctx).await?;
    assert_eq!(method, Method::GET);
     
    // manually pack http response.
    Ok(WebResponse::new(ResponseBody::from("low level")))       
}

// high level abstraction but for synchronous function. this function
// is powered by background thread pool so it does not block the async
// code.
fn sync(method: Method) -> &'static str {
    assert_eq!(method, Method::GET);
    // blocking thread for long period of time does not impact xitca-web
    // async internal.
    std::thread::sleep(std::time::Duration::from_secs(3));
    "sync"    
}

§Middleware composability

use xitca_web::{
    error::Error,
    handler::{handler_service},
    http::WebResponse,
    route::get,
    service::{Service, ServiceExt},
    App, WebContext
};

// ServiceExt::enclosed_fn combinator enables async function as middleware.
// in xitca_web almost all service can be enclosed by an middleware.
App::new()
    .at("/",
        get(
            // apply middleware to handler_service
            handler_service(|| async { "hello,world!" })
                .enclosed_fn(middleware_fn)
        )
        // apply middleware to route
        .enclosed_fn(middleware_fn)
    )
    // apply middleware to application
    .enclosed_fn(middleware_fn)
    .serve()
    .bind("localhost:8080")?
    .run()
    .wait()

// a simple middleware just forward request to inner service logic.
async fn middleware_fn<S, T, E>(service: &S, ctx: WebContext<'_>) -> Result<T, E>
where
    S: for<'r> Service<WebContext<'r>, Response = T, Error = E>
{
    service.call(ctx).await
}

For more detailed middleware documentation please reference middleware

§Cross crates integration

use tower-http inside xitca-web application.

use tower_http::services::ServeDir;
use xitca_web::{
    service::tower_http_compat::TowerHttpCompat,
    App
};

App::new()
    .at("/", TowerHttpCompat::new(ServeDir::new("/some_folder")))
    .serve()
    .bind("localhost:8080")?
    .run()
    .wait()

§Fast compile time

  • additive proc macro
  • light weight dependency tree

§opt-in proc macro

in xitca-web proc macro is opt-in. This result in a fast compile time with zero public proc macro. That said you still can enable macros for a higher level style of API.

use xitca_web::{codegen::route, App};

#[tokio::main]
async fn main() -> std::io::Result<()> {
    App::new()
        .at_typed(index)
        .serve()
        .bind("localhost:8080")?
        .run()
        .await
}

#[route("/", method = get)]
async fn index() -> &'static str {
    "Hello,World!"
}

Re-exports§

Modules§

Structs§

  • composed application type with router, stateful context and default middlewares.
  • multi protocol handling http server
  • web context type focus on stateful and side effect based request data access.

Type Aliases§

  • object safe App instance. used for case where naming App’s type is needed.
  • type alias for concrete type of nested App.