distd serves through std.http — the readiness loop lands (PLAN-HTTPZ A1)
The hand-rolled sequential accept loop, its SO_RCVTIMEO band-aid, and the whole src/server/http.sx module are retired: distd is now a std.http handler. Server.init gets the store directory through the ctx word; route() fills a Response instead of writing to a socket; every handler ports mechanically (respond_error/load_or_503/respond_render take *Response; bodies allocate from the per-request arena, never the stack, since serialization happens after the handler returns). Downloads keep X-Checksum-SHA256 via extra_headers; auth takes the extracted Authorization value; the 411 contract (POST/PUT must declare Content-Length) moves into the handler, pinned as before. Config: 512 MiB read cap (whole-body artifact uploads), 120s request deadline, 5s keepalive, 200 requests per connection. Idle connections now cost nothing — timeouts evict, never block. http_client gains its own 10s read timeout (the old shared helper's secs->ms change had silently shrunk it to 10ms). tests/server_http.sx pins the architecture: a request answers within 1s while SIX idle preconnects are held open (the retired loop paid 250ms-2s per idle socket serially), and two requests ride one keep-alive connection. make test 24/24 green.
This commit is contained in:
@@ -183,17 +183,28 @@ main :: () -> i32 {
|
||||
process.assert(get_str(get_obj(bad, "error"), "code") == "download.unknown_object", "unknown digest names download.unknown_object");
|
||||
print(" download ok\n");
|
||||
|
||||
// ── idle preconnect must not wedge the accept loop ────────────────
|
||||
// Hold a connection open that never sends bytes (what a browser's
|
||||
// speculative preconnect does) and require a real request to still be
|
||||
// answered: the 2s read timeout must free the loop well inside curl's
|
||||
// 5s budget. Pre-fix (no SO_RCVTIMEO) this curl times out with 000.
|
||||
process.run("sh -c '(sleep 6 | nc 127.0.0.1 18792 > /dev/null 2>&1) &'");
|
||||
// ── idle preconnects cost nothing (PLAN-HTTPZ A1) ─────────────────
|
||||
// Hold SIX connections open that never send bytes (browser-style
|
||||
// speculative preconnects) and require a real request to answer
|
||||
// within 1s. The retired sequential loop paid its read timeout per
|
||||
// idle socket serially (6 x 250ms band-aid = 1.5s; 6 x 2s = 12s
|
||||
// before that), so this pins the readiness architecture, not a
|
||||
// tuned timeout.
|
||||
process.run("sh -c 'for i in 1 2 3 4 5 6; do (sleep 8 | nc 127.0.0.1 18792 > /dev/null 2>&1) & done'");
|
||||
process.run("sleep 0.3");
|
||||
wc := process.run(concat(concat("curl -s -m 5 -o /dev/null -w '%{http_code}' ", BASE), "/healthz"));
|
||||
wc := process.run(concat(concat("curl -s -m 1 -o /dev/null -w '%{http_code}' ", BASE), "/healthz"));
|
||||
process.assert(wc != null, "curl spawn failed (idle-conn case)");
|
||||
process.assert(wc!.stdout == "200", "request must be served while an idle connection is held open");
|
||||
print(" idle connection cannot wedge the loop\n");
|
||||
process.assert(wc!.stdout == "200", "request must answer within 1s while 6 idle connections are held open");
|
||||
print(" 6 idle preconnects: served within 1s\n");
|
||||
|
||||
// ── keep-alive: two requests ride one connection ──────────────────
|
||||
// curl reuses its connection for consecutive URLs; both responses
|
||||
// must arrive (the retired loop closed after every response).
|
||||
ka := process.run(concat(concat(concat(concat("curl -s -m 2 ", BASE), "/healthz "), BASE), "/healthz"));
|
||||
process.assert(ka != null, "curl spawn failed (keep-alive case)");
|
||||
process.assert(ka!.stdout == "{\"status\":\"ok\"}{\"status\":\"ok\"}",
|
||||
"two requests on one connection both answer");
|
||||
print(" keep-alive reuse ok\n");
|
||||
|
||||
// ── freshness: publish B while the server runs ────────────────────
|
||||
rb := process.run(publish_cmd(path_join(MDIR, "b.json")));
|
||||
|
||||
Reference in New Issue
Block a user