Skip to main content

serve

Function serve 

Source
pub async fn serve(
    routes: Router<Ctx>,
    req: HttpRequest,
    env: Env,
) -> Result<Response>
Expand description

Runs one request through the application router: the Worker fetch entry point.

Builds a Ctx from env, gives it to routes as axum state, and wraps the router with the middleware every Ocre app runs (outermost first):

  1. Security headers on every response (X-Content-Type-Options: nosniff, X-Frame-Options: SAMEORIGIN, Referrer-Policy, HSTS on HTTPS…); a handler’s own value wins.
  2. Host authorization: when the ALLOWED_HOSTS Worker variable is set, other hosts get 403 Forbidden.
  3. CORS for the origins listed in the ALLOWED_ORIGINS Worker variable (no CORS layer when it is empty).
  4. Cross-origin request protection (CSRF) without tokens: unsafe requests a browser sends from another site (Sec-Fetch-Site, or Origin against Host) get 403 Forbidden.
  5. The encrypted cookie Session, keyed from the SECRET_KEY_BASE secret (and, during a rotation, SECRET_KEY_BASE_PREVIOUS).

A missing or short SECRET_KEY_BASE does not fail every request: only handlers that touch the session get Error::Internal, naming the fix (ocre secret, .dev.vars).

The request also carries the Ctx as an extension, so the app’s own middleware (axum::middleware::from_fn) can reach the bindings with an Extension(ctx): Extension<Ctx> argument, as handlers do with State.

Files from storage::serve go out as R2’s own stream, so the Worker spends no CPU copying them and Content-Length is kept.

Around the router, serve picks the request id (RequestId) and tags Ctx::log with it, the method and the path; answers with an X-Request-Id header; reports an Error::Internal response through Ctx::errors (logged as [ocre] <message>, then sent to the errors subscribers); and logs GET /posts 200 in 40 ms (db: 3 queries, 12 ms) at debug. Debug builds (ocre dev) also add a Server-Timing header (D1 time and total, in the browser’s Network panel) and show the development error page: a 500 page with the internal message, the request’s details (secrets filtered) and the D1 statements it ran, or error.detail in a JSON error. Release builds (ocre deploy) never show internal details.

§Errors

Returns a [worker::Error] only when the response cannot be converted to a JavaScript Response. Handler errors are responses (an HTML page or JSON), not Err.

§Free plan

One call per Worker request (100,000 a day); the middleware itself reads no D1 rows and no KV keys: sessions live in the cookie.

§Examples

src/lib.rs of a generated app:

use axum::{Router, routing::get};
use ocre::Ctx;

fn routes() -> Router<Ctx> {
    Router::new().route("/up", get(|| async { "OK" }))
}

#[worker::event(fetch)]
async fn fetch(
    req: worker::HttpRequest,
    env: worker::Env,
    _ctx: worker::Context,
) -> worker::Result<worker::web_sys::Response> {
    ocre::serve(routes(), req, env).await
}