testForwardDoesntWaitOnAnAppThatDoesntAnswer

tcmts.cli.sync.test.testForwardDoesntWaitOnAnAppThatDoesntAnswer · #zrv7bnch · term

A proxied app that doesn't begin to answer a page in patience doesn't hold its caller: a GET gets a 503 saying it's busy, at once, and slow is told (the web UI stops one stuck so); an action (a POST) waits for its answer.

import { forward } from "#codebase/tcmts/cli/sync";
import assert from "node:assert/strict";
import http from "node:http";

export const testForwardDoesntWaitOnAnAppThatDoesntAnswer = async () => {
  // an app that never answers a GET, and answers a POST after 400ms
  const app = http.createServer((q, r) => {
    if (q.method === "POST") setTimeout(() => r.end("done"), 400);
  });
  let slow = 0;
  const last: { at: number; when?: number } = { at: 0 };
  const front = http.createServer((q, r) => {
    const { port } = app.address() as { port: number };
    forward(q, r, `http://127.0.0.1:${port}`, last, { patience: 200, slow: () => slow++ });
  });
  const listen = (s: http.Server) => new Promise<void>((r) => s.listen(0, "127.0.0.1", r));
  await listen(app);
  await listen(front);
  const url = `http://127.0.0.1:${(front.address() as { port: number }).port}/x`;
  try {
    const t = performance.now();
    const page = await fetch(url, { headers: { accept: "text/html" } });
    assert.equal(page.status, 503);
    assert.equal(page.headers.get("retry-after"), "5");
    assert.match(await page.text(), /busy/);
    assert.ok(performance.now() - t < 3000, "answered as busy without waiting on");
    assert.equal(slow, 1);
    assert.equal(last.when, undefined);
    const posted = await fetch(url, { method: "POST", body: "x" });
    assert.equal(posted.status, 200);
    assert.equal(await posted.text(), "done");
    assert.equal(slow, 1);
    assert.ok(last.when);
  } finally {
    for (const s of [front, app]) {
      s.closeAllConnections();
      s.close();
    }
  }
}

uses