Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ this project aims to follow [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- feat(stdlib/Http): RSR rewire — surface `hpm-http-rsr` Zig FFI (10 server-side externs: listen / port / free / accept / method / path / header / body / respond / request-free) + opaque `HpmHttpServer` + `HpmHttpRequest` types; native-only (#425)
- feat(stdlib/json): v0.3 — RSR rewire to `hpm-json-rsr` Zig FFI (11 externs + opaque `HpmJsonValue` + `parse` / `to_json`), Deno-ESM lowering via `__as_hpmJson*` shims (#421)
- feat(parser): trailing-comma in fn params and expr lists (Refs gitbot-fleet#148) (#370)
- feat(lexer): underscore-prefix idents `_key`/`_unused` (Refs gitbot-fleet#148) (#373)
Expand Down
82 changes: 82 additions & 0 deletions stdlib/Http.affine
Original file line number Diff line number Diff line change
Expand Up @@ -169,3 +169,85 @@ pub fn readResponse(t: Thenable) -> Response {
}
#{ status: responseStatus(t), headers: headers, body: responseBody(t) }
}

// ── HTTP server primitives (hpm-http-rsr Zig FFI surface) ────────────────────
//
// The server-side counterpart to `http_request`. Backed by the 10
// `hpm_http_*` exports at
// `hyperpolymath/http-capability-gateway/ffi/zig/src/main.zig`.
//
// Designed for the OikosBot webhook receiver path: bind once, accept in a
// loop, inspect the request, reply, free. No keep-alive (every response
// closes the connection). No TLS — designed to sit behind a reverse proxy
// (Caddy / nginx).
//
// **Native-only.** These externs have no Deno-ESM lowering and will fail
// at runtime there (consistent with the existing `http_request_thenable`
// + `response*` wasm-path externs above). The natural deployment is the
// WasmGC / native-C path linking libhttp_capability_gateway alongside
// libhpm_crypto.
//
// Native lowering adapts the Zig size-query convention (`isize`, NULL/0
// → required size, then re-call to write into a caller-provided buffer)
// into AS-friendly `String` / `Option<String>` returns; AS callers never
// see the two-step protocol.

pub extern type HpmHttpServer;
pub extern type HpmHttpRequest;

/// Bind a TCP listener on `host:port`. Host is an IPv4/IPv6 string
/// (e.g. `"0.0.0.0"`, `"127.0.0.1"`, `"::1"`). Pass `port = 0` to let
/// the kernel pick a free port (then read it back with
/// `hpm_http_server_port`). Returns `None` on host-parse / bind /
/// allocator failure. Pair every `Some(s)` with `hpm_http_server_free(s)`.
pub extern fn hpm_http_server_listen(host: String, port: Int) -> Option<HpmHttpServer> / { Net };

/// The bound port (useful when `listen` was called with `port: 0`).
/// Returns 0 on a null handle.
pub extern fn hpm_http_server_port(server: HpmHttpServer) -> Int / { Net };

/// Close the listener and free the handle. Does NOT affect requests
/// already returned by `accept` — those must be freed independently
/// with `hpm_http_request_free`. Returns 0.
pub extern fn hpm_http_server_free(server: HpmHttpServer) -> Int / { Net };

/// Block until a request arrives, parse its head, return a request
/// handle. Returns `None` if accept failed, the client sent a
/// malformed head, or the allocator failed. The TCP connection is
/// closed automatically on failure. Pair every `Some(r)` with
/// `hpm_http_request_free(r)`.
pub extern fn hpm_http_server_accept(server: HpmHttpServer) -> Option<HpmHttpRequest> / { Net, Async };

/// Request method ordinal, matching `std.http.Method`:
/// `0=GET 1=HEAD 2=POST 3=PUT 4=DELETE 5=CONNECT 6=OPTIONS 7=TRACE 8=PATCH`.
/// Returns -1 on a null handle.
pub extern fn hpm_http_request_method(req: HpmHttpRequest) -> Int / { Net };

/// Request target (URI path + query). Returns the empty string on a
/// null handle.
pub extern fn hpm_http_request_path(req: HpmHttpRequest) -> String / { Net };

/// Look up a request header by case-insensitive name. Returns `None`
/// if the header is absent. The native lowering hides the Zig
/// size-query convention — AS callers see the decoded value
/// directly.
pub extern fn hpm_http_request_header(req: HpmHttpRequest, name: String) -> Option<String> / { Net };

/// Read the entire request body. May only be called once per request
/// — subsequent calls return the empty string. Native lowering caps
/// the body at the Zig-side `HTTP_MAX_BODY_BYTES` (1 MiB); over-cap
/// requests return the empty string.
pub extern fn hpm_http_request_body(req: HpmHttpRequest) -> String / { Net };

/// Send a complete HTTP response. `status` is the numeric status
/// code (e.g. `200`, `404`, `500`). `headers` is a
/// `"Name: Value\r\nName: Value"`-formatted extra-headers buffer
/// (max 16 entries; pass the empty string for none). Connection is
/// always closed after (no keep-alive). Returns 0 on success, -1 on
/// error (already-responded / over-16-headers / IO failure).
pub extern fn hpm_http_request_respond(req: HpmHttpRequest, status: Int, headers: String, body: String) -> Int / { Net };

/// Close the TCP connection and free the request handle. Must be
/// called exactly once for every `Some(r)` returned by
/// `hpm_http_server_accept`. Returns 0.
pub extern fn hpm_http_request_free(req: HpmHttpRequest) -> Int / { Net };
Loading