// 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 }}