Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Transport Layer

AFast supports multiple transport layers that can run simultaneously on different ports.

HTTP

HTTP server endpoints:

MethodPathDescription
POST/_apiBinary handler dispatch
GET/_wsWebSocket upgrade (merged mode)
GET/code/{service}/{lang}On-demand code gen (requires code)
GET/docAPI docs index (requires doc)
GET/doc/{service}Service-specific docs (requires doc)
*Ordinary routesRESTful endpoints (requires ordinary-http)

HTTP Response Format:

  • Success: [0u8][0i64][data: bytes]
  • Error: [1u8][code: i64][message: bytes]

WebSocket

WS frame format:

Request:  [req_id: SeqId][handler_id: u32][len: Len][payload]
Push:     [0: SeqId][conn_id: u32][len: Len][payload]
Heartbeat:[0xFFFFFFFF: SeqId][len: Len][conn_id1: u32]...

SeqId type is controlled by the seq64 feature (i32 or i64), Len type by the len64 feature (u32 or u64).

TCP

TCP uses 4-byte big-endian length-prefix framing with complete binary payloads per frame. Suitable for embedded devices or raw TCP scenarios.

HTTP + WS Port Merging

When ws_addr and http_addr are set to the same address, AFast merges WebSocket into the HTTP server via HTTP Upgrade:

#![allow(unused)]
fn main() {
// Same port for both HTTP and WebSocket
let app = AFast::new()
    .service(svc)
    .ws("0.0.0.0:5000")
    .http("0.0.0.0:5000");  // Same address, auto-merged
}

Ordinary HTTP (REST)

With ordinary-http, define RESTful routes using get/post/put/patch/delete inside the service! macro:

#![allow(unused)]
fn main() {
use afast::{get, Query, Param, Body, Header, Json, HttpResult};

#[derive(Deserialize)]
struct UserQuery { page: i64, size: i64 }

#[derive(Serialize)]
struct UserResponse { id: i64, name: String }

#[get(":id")]
async fn get_user(
    state: State<AppState>,
    param: Param<UserPath>,
    query: Query<UserQuery>,
) -> HttpResult<Json<UserResponse>> {
    Ok(Json(UserResponse { id: param.id, name: format!("User {}", param.id) }))
}
}

Response Types

TypeHTTP StatusContent-Type
Json<T>200application/json
Text200text/plain
Html200text/html
File200Custom + Content-Disposition: attachment
Status(code)Custom
Redirect(url)302Location header

Long Connections

Handlers using Receiver/Sender are auto-detected as long-connection mode:

#![allow(unused)]
fn main() {
#[handler(desc("Chat"))]
async fn chat(
    state: State<AppState>,
    auth: Custom<Auth>,
    mut receiver: Receiver,
    sender: Sender,
) -> Result<()> {
    sender.send(b"Welcome!".to_vec()).await?;
    while let Some(msg) = receiver.recv().await {
        sender.send(msg).await?;  // echo
    }
    Ok(())
}
}

Generated clients return a Socket object for long-connection handlers, with send()/close() and onMessage callback.