writeonce/docs/examples/tls-client/main.wo
shoney.arickathil 72ed35773d test(tls): live acceptance gate for net.connect_tls (rv2 9 F3c-net phase 4)
- docs/examples/tls-client/main.wo: an outbound HTTPS client in .wo —
  net.connect_tls, write_tls a request, read_tls to EOF, print; connect
  failure caught with try/catch and reported (never a silent downgrade)
- scripts/tls-accept.sh + `just tls`: dials a local TLS 1.3 stub (python
  ssl, TLS1.3-only) with a generated test CA — proves the hand-rolled
  handshake + chain/host validation + an app round-trip end to end from
  .wo through the compiler, and refuses the untrusted-chain and
  hostname-mismatch negatives. No live network; log /tmp/tls.log
- gate: 5 checks, 0 failures

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit 3d8bb140ec478167a4e660adf2caa964ff410ff1)
2026-09-15 01:15:52 +02:00

50 lines
1.7 KiB
Text

-- tls-client — runtime-v2 9 F3c-net's acceptance workload. An outbound HTTPS
-- client, written end to end in .wo: it dials a TLS 1.3 server with
-- `net.connect_tls`, which runs the hand-rolled handshake (X25519 + AES-GCM /
-- ChaCha20-Poly1305 + the RFC 8446 key schedule), validates the certificate
-- chain to a trust anchor and matches the hostname (SAN), then carries
-- application bytes over `net.write_tls` / `net.read_tls`.
--
-- woc --emit main.wo -o tls-client.wob
-- WO_CA_BUNDLE=ca.pem wovm tls-client.wob localhost 18443
--
-- A connection whose chain does not chain to a trusted anchor, whose SAN does
-- not match the host, or that is expired, is refused loudly (the connect traps,
-- caught here). The gate (scripts/tls-accept.sh, `just tls`) proves the happy
-- path and those negatives against a local stub — no live network.
use net
fn main(args: multi Text) -> Int {
if len(args) < 2 {
print_err("usage: tls-client <host> <port>");
return 2;
}
let host = args[0];
let port = parse_int(args[1]);
if port == nil {
print_err("tls-client: <port> must be a number");
return 2;
}
-- connect_tls traps on DNS/connect/handshake/chain/hostname failure — a
-- secure connection is never silently downgraded, so we catch and report.
let fd = try net.connect_tls(host, port) catch (e) -1;
if fd < 0 {
print("tls: refused (handshake, chain, or hostname)");
return 1;
}
net.write_tls(fd, "GET / HTTP/1.0\r\nHost: ${host}\r\n\r\n");
let acc = "";
while true {
let chunk = try net.read_tls(fd, 4096) catch (e) "";
if len(chunk) == 0 { break; }
acc = acc .. chunk;
}
net.close(fd);
print("recv ${len(acc)} bytes");
print(acc);
return 0;
}