// flashd — the Flash language server: the stdio driver. // // stdin and stdout ARE the protocol channel — nothing else may ever be // written to stdout, or the client's framing is destroyed; anything // human-readable goes to stderr. The loop is: block on one byte, drain // whatever else the reader already buffered, then hand every complete // frame to the server core. Each message is handled inside its own // arena, so a session that runs for hours holds onto nothing but the // document store (and the decoder's one-frame buffer), both backed by // the general-purpose allocator. // // A framing error is fatal by design: without a parseable Content-Length // the byte stream can never resynchronize, so the only honest move is to // report on stderr and exit non-zero. EOF on stdin — the client went // away without the shutdown/exit handshake — also exits non-zero, per // the protocol's guidance for orphaned servers. use std use core use build_options use "transport" use "server" pub fn main(init std.process.Init) !void { io := init.io gpa := init.gpa var stdout_buf [4096]u8 = undefined var stdout_obj = std.Io.File.stdout().writer(io, &stdout_buf) out := &stdout_obj.interface var stderr_buf [256]u8 = undefined var stderr_obj = std.Io.File.stderr().writer(io, &stderr_buf) err_out := &stderr_obj.interface var stdin_buf [65536]u8 = undefined var stdin_obj = std.Io.File.stdin().reader(io, &stdin_buf) rdr := &stdin_obj.interface var srv = server.Server.init(gpa, build_options.version) defer srv.deinit() var dec transport.Decoder = .empty defer dec.deinit(gpa) while true { // Block for at least one byte, then drain the reader's buffer in // one feed — per-byte only for the first byte of a chunk. b := rdr.takeByte() catch break first := [1]u8{b} try dec.feed(gpa, first[0..]) rest := rdr.buffered() if rest.len > 0 { try dec.feed(gpa, rest) rdr.toss(rest.len) } while true { body_or_null := dec.next() catch { err_out.writeAll("flashd: unrecoverable framing error on stdin\n") catch {} err_out.flush() catch {} std.process.exit(1) } body := body_or_null orelse break var msg_arena = core.arena.ArenaAllocator.init(gpa) defer msg_arena.deinit() outcome := try srv.handle(msg_arena.allocator(), body) if outcome.response |resp| { framed := try transport.frame(msg_arena.allocator(), resp) try out.writeAll(framed) try out.flush() } if outcome.exit |code| { out.flush() catch {} std.process.exit(code) } } } // EOF without the exit notification: the client vanished. err_out.writeAll("flashd: stdin closed without an exit notification\n") catch {} err_out.flush() catch {} std.process.exit(1) } test "the driver surface is reachable" { _ = &main }