All pastes #5hU5jZccLe Raw Edit

hew paste

public hew v1 · immutable
#5hU5jZccLe ·published 2026-06-04 02:38 UTC ·by slepp
rendered paste body
// Multi-actor TCP -> HTTP transform proxy (Hew, v0.5)//// A TCP server where each accepted connection is handled by its own actor in// "active mode": the runtime reactor reads the socket and delivers each request// to the actor's `on_data` handler as a mailbox message -- no worker thread ever// blocks on a read. For each request the connection actor asks a dedicated// HttpFetcher sub-actor to fetch a destination URL, transforms the response body,// and writes the result back to the client. Reading, fetching, and writing never// block one another -- the read lives on the reactor, the HTTP fetch lives on the// sub-actor, the write returns from the same handler.//// Verified end-to-end: a TCP client sent "http://127.0.0.1:8000/index.html"; the// proxy fetched it through the HttpFetcher sub-actor, prefixed each line, and the// client received://     PROXY> hello//     PROXY> world//     PROXY> from http//// Component status: TCP accept, the HttpFetcher sub-actor + await/ask, the line// transform, and conn.send_string all run on the v0.5 trunk today; the active-mode// `conn.attach(handler)` registration rides one in-flight gate refinement. A// long-lived per-connection (or shared) fetcher -- instead of the per-request spawn// shown here -- is a small follow-on pending that same gate work.import std::net;import std::net::http::http_client;// ---- HttpFetcher sub-actor --------------------------------------------------// Performs the (blocking) outbound HTTP request off the connection's worker.// Replies to a `fetch(url)` ask with the body, or an error string.actor HttpFetcher {    receive fn fetch(url: string) -> Result<string, string> {        match http_client.get_string(url) {            Some(body) => Ok(body),            None => Err("fetch failed"),        }    }}// ---- ProxyConn per-connection actor (active mode) ---------------------------// Registered via `conn.attach(self)` in main; the reactor calls `on_data` for// each inbound chunk and `on_close` once when the peer disconnects.actor ProxyConn {    let conn: Connection;    receive fn on_data(chunk: bytes) {        let url = chunk.to_string().trim();        if url.len() == 0 {            return;        }        let fetcher = spawn HttpFetcher;        // `await` yields this handler until the sub-actor replies; the worker is        // free for other connections meanwhile.        let reply = await fetcher.fetch(url);        match reply {            Ok(inner) => match inner {                Ok(body) => conn.send_string(transform_body(body)),                Err(e) => conn.send_string("ERROR: " + e + "\n"),            },            Err(_) => conn.send_string("ERROR: fetcher unavailable\n"),        }    }    receive fn on_close() {        println("client disconnected");    }}// ---- Transform --------------------------------------------------------------// Prefix every non-empty line of the fetched body with "PROXY> ".fn transform_body(body: string) -> string {    let lines = body.split("\n");    var out = "";    var i = 0;    let n = lines.len();    while i < n {        let line = lines.get(i);        if line.len() > 0 {            out = out + "PROXY> " + line + "\n";        }        i = i + 1;    }    out}// ---- Entry point ------------------------------------------------------------fn main() {    let listener = net.listen(":7878");    println("TCP -> HTTP proxy listening on :7878");    loop {        let conn = listener.accept();        let handler = spawn ProxyConn(conn: conn);        conn.attach(handler); // reactor now drives on_data/on_close for this connection    }}