All pastes #8j7gcYzjf8 Raw Edit

hew paste

public hew v1 · immutable
#8j7gcYzjf8 ·published 2026-06-04 05:29 UTC ·by slepp
rendered paste body
// demo-tcp-http-proxy.hew//// Architecture: TCP->HTTP transform proxy with per-connection actors.////   main             - binds :7878, loops accept(); spawns one ProxyConn per client//   ProxyConn actor  - active-mode connection handler (reactor delivers on_data/on_close)//                      on_data: interprets each inbound chunk as a URL, asks the//                      HttpFetcher sub-actor, transforms the result, writes back to client//   HttpFetcher actor - sub-actor owned per-ProxyConn; calls http_client.get_string and//                      returns Result<string, string> via the await/ask pattern//// Transform: prepend "PROXY> " to every non-empty line of the fetched body.//// Verified: compiles AND runs end-to-end on the v0.5 trunk. A TCP client sent// "http://127.0.0.1:8099/data.txt"; the proxy fetched it through the per-connection// HttpFetcher sub-actor, prefixed each line, and the client received://     PROXY> alpha//     PROXY> beta//     PROXY> gamma//// The reader lives on the runtime reactor (on_data), the HTTP fetch lives on the// HttpFetcher sub-actor (off the connection's worker), and the writer returns from// the same handler (conn.send_string) -- so read, fetch, and write never block one// another. status: compiles + runs on v0.5 trunk.import std::net;import std::net::http::http_client;// ---- HttpFetcher sub-actor --------------------------------------------------//// One instance per ProxyConn. Handles `fetch(url)` asks and returns the body// or an error string. Isolated so the blocking HTTP call never ties up the// same worker thread as the TCP connection handler.actor HttpFetcher {    receive fn fetch(url: string) -> Result<string, string> {        match http_client.get_string(url) {            Some(body) => Ok(body),            // NOTE: a literal message here, not http_client.last_error(): the            // latter currently trips a codegen-front fail-closed (BUG-NET-7) when            // its string result feeds an Err() inside an await-suspended actor            // handler. Tracked separately; the proxy's error text is unaffected.            None => Err("http fetch failed"),        }    }}// ---- ProxyConn per-connection actor ----------------------------------------//// Spawned with the accepted Connection and a pre-spawned HttpFetcher.// Registered as the active-mode handler via conn.attach(handler) in main.// The runtime reactor delivers on_data / on_close without blocking any worker.actor ProxyConn {    let conn: Connection;    let fetcher: LocalPid<HttpFetcher>;    // Called by the reactor for each inbound chunk.    receive fn on_data(chunk: bytes) {        // bytes.to_string() decodes UTF-8 (std::io impl on bytes).        let raw = chunk.to_string();        let url = raw.trim();        // Skip empty lines (keep-alive pings, blank lines, etc.)        if url.len() == 0 {            return;        }        // Ask the sub-actor to fetch the URL.        // await suspends this handler; the worker is freed until the reply arrives.        let ask_result = await fetcher.fetch(url);        match ask_result {            Ok(inner) => {                match inner {                    Ok(body) => {                        let transformed = transform_body(body);                        conn.send_string(transformed);                    },                    Err(e) => {                        // HTTP fetch failed; send the error back as a plain line.                        let msg = "ERROR: " + e + "\n";                        conn.send_string(msg);                    },                }            },            Err(_) => {                // AskError: fetcher actor unreachable (crashed or closed).                conn.send_string("ERROR: fetcher unavailable\n");            },        }    }    // Called once when the client disconnects or the socket errors.    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 = "";    for i in 0 .. lines.len() {        let line = lines.get(i);        if line.len() > 0 {            out = out + "PROXY> " + line + "\n";        } else {            out = out + "\n";        }    }    out}// ---- Entry point ------------------------------------------------------------fn main() {    let listener = net.listen(":7878");    println("TCP->HTTP proxy listening on :7878");    // Accept loop: each accepted connection gets its own actor pair.    loop {        let conn = listener.accept();        let fetcher = spawn HttpFetcher;        let handler = spawn ProxyConn(conn: conn, fetcher: fetcher);        conn.attach(handler); // reactor now drives on_data/on_close for this connection    }}