Zero-Copy & Servers

Network servers are where Lumina's design comes together: stackful coroutines for one-task-per-connection code without callback hell, capabilities so the server's I/O is visible in its signature, and zero-copy primitives so bytes do not bounce through user space when they don't need to.

An HTTP server in a screenful

function response(body: String): String =
    `HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: {body.length()}\r\nConnection: close\r\n\r\n{body}`

function main(net: Network, console: Console): Void {
    port = 18093
    match net.listen("127.0.0.1", port) {
        Ok(listener) => {
            console.println(`listening on http://127.0.0.1:{port}`)
            for await conn in listener.incoming() {
                spawn(() => {
                    request = conn.read(8192)
                    if request.length() > 0 {
                        conn.send(response("Hello from Lumina!\n"))
                    }
                    conn.close()
                    return 0
                })
            }
        },
        Err(e) => console.println(`listen failed ({e.kind()}): {e.message}`)
    }
}
$ lumina run server.lumina &
listening on http://127.0.0.1:18093
$ curl http://127.0.0.1:18093/
Hello from Lumina!

Every connection gets its own task (spawn). The handler is straight-line code: read, respond, close. When it waits on the socket, the task suspends and the scheduler runs other connections. This is the Go model, with no async coloring needed in the handler.

The idiomatic shape: capabilities + streams

The repository's benchmark server, examples/http_server.lumina, shows the production shape:

function main(net: Network, console: Console): Integer {
    port = resolvePort(18081)
    console.println(`Lumina HTTP server listening on 0.0.0.0:{port}`)

    match net.listen("0.0.0.0", port) {
        Ok(listener) => {
            for await client in listener.incoming() {
                spawn(() => handleClient(client))
            }
            return 0
        },
        Err(e) => {
            console.println(`listen failed ({e.kind()}): {e.message}`)
            return 1
        }
    }
}

lumina run examples/http_server.lumina serves Hello, World! on port 18081. tools/http_bench_wrk.sh benchmarks it against a Tokio + hyper reference server in tools/tokio_http_reference.

Zero-copy: kernel splice

Proxies and gateways mostly move bytes from one socket to another. spliceTo does that inside the kernel, so the payload never enters Lumina's memory:

async function relay(src: TcpSocket, dst: TcpSocket): Integer {
    return src.spliceTo(dst, 0, 5)      // kernel splice: 5 bytes src -> dst
}

src and dst are TcpSocket values from net.connect(host, port) (TcpSocket!NetworkError) or from listener.incoming(). Tests for this live in tests/spec/19_stdlib_net/05_tcp_splice_loopback.lumina.

Zero-copy: peek before you read

sock.peek(maxLen) looks at incoming bytes with MSG_PEEK without consuming them. A server can inspect the first bytes ("is this TLS? HTTP/1? HTTP/2?") and then hand the untouched stream to the right handler, or splice it onward. tests/spec/19_stdlib_net/04_tcp_peek_loopback.lumina shows it end to end.

Zero-copy inside the program

Zero-copy is not only about sockets:

Under the hood

The layered design

Language_ZeroCopy.md describes zero-copy as a stack of layers, from the kernel up:

On top sits an HTTP framework with router, middleware and TLS in the standard library designed. The examples above speak raw HTTP/1.1, and stdlib/net/ has WebSocket and JSON-RPC modules.

Next: Interop & Stdlib