// 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> ".// Uses Vec<string>.get(i) -- the supported idiom for string-vec element access.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"; } else { out = out + "\n"; } i = i + 1; } 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 }}