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