Public: anyone can read this; the crew can change it.

A test pins Server-Timing: a phase name never carries text from the request, and the header stays one valid line #6919

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

I added five tests to `test/route-timing.test.ts` that pin Server-Timing, and the tests found two holes in `src/core/route_timing.ts`, which I fixed. `bun x tsc --noEmit` is clean and the file's 10 tests pass. The settings-timing, server-timing-sweep and route-timing-coverage tests also pass. I did not run the whole suite.

shipped production is serving it deploy 9725453

opened this

I added five tests to test/route-timing.test.ts that pin Server-Timing, and the tests found two holes in src/core/route_timing.ts, which I fixed. bun x tsc --noEmit is clean and the file's 10 tests pass. The settings-timing, server-timing-sweep and route-timing-coverage tests also pass. I did not run the whole suite.

Tests

  • Phase order: two phases timed on one request (one sync, one async) appear after app in order, each with dur.
  • Throw: a throw inside timePhase or timePhaseAsync still records the phase and rethrows the original error.
  • Bad names: a phase name with a comma, semicolon, quote, CR, LF, space, capital or nothing at all is refused. That holds for both notePhase and timePhase, and the resulting header (including the desc="hit" form) stays on one line.
  • Frames: nameFrame and namesFrame name route and flag the request, with no leak to other requests.
  • Once, then nothing: takeDbTime and takeSessionTime give their value once and then nothing, and one request's time never shows up on another.

Holes fixed

  • Throw in a timed phase: if the work threw, timePhase and timePhaseAsync recorded nothing. They now record the phase and rethrow the original error.
  • takeSessionTime: it never cleared its value, so it could be read twice. It now clears it, like takeDbTime. The page callers that take it (commits, edit, settings and others) take it once. Where nameFrame is also used, it runs before the take, so route still subtracts the session time.

Not done: the name check itself was already sound, since notePhase only accepts ^[a-z][a-z0-9-]{0,15}$, so I changed nothing there.

I extended the existing test/route-timing.test.ts instead of making a new file, as the issue asked.

Closes #6910

commented

I added five tests to test/route-timing.test.ts that pin Server-Timing, and the tests found two holes in src/core/route_timing.ts, which I fixed. bun x tsc --noEmit is clean and the file's 10 tests pass. The settings-timing, server-timing-sweep and route-timing-coverage tests also pass. I did not run the whole suite.

Tests

  • Phase order: two phases timed on one request (one sync, one async) appear after app in order, each with dur.
  • Throw: a throw inside timePhase or timePhaseAsync still records the phase and rethrows the original error.
  • Bad names: a phase name with a comma, semicolon, quote, CR, LF, space, capital or nothing at all is refused. That holds for both notePhase and timePhase, and the resulting header (including the desc="hit" form) stays on one line.
  • Frames: nameFrame and namesFrame name route and flag the request, with no leak to other requests.
  • Once, then nothing: takeDbTime and takeSessionTime give their value once and then nothing, and one request's time never shows up on another.

Holes fixed

  • Throw in a timed phase: if the work threw, timePhase and timePhaseAsync recorded nothing. They now record the phase and rethrow the original error.
  • takeSessionTime: it never cleared its value, so it could be read twice. It now clears it, like takeDbTime. The page callers that take it (commits, edit, settings and others) take it once. Where nameFrame is also used, it runs before the take, so route still subtracts the session time.

Not done: the name check itself was already sound, since notePhase only accepts ^[a-z][a-z0-9-]{0,15}$, so I changed nothing there.

I extended the existing test/route-timing.test.ts instead of making a new file, as the issue asked.

Approved

Approved. The tests pin the phase-name token rule, the one-line header (CR/LF/separator/quote refused), phase order, once-only takes and no cross-request leak. Both source fixes are small and right: a throwing phase is still recorded and the original error rethrown, and takeSessionTime now clears like takeDbTime. I checked the callers: nameFrame reads the session time before the handlers take it (settings.ts:119/211), so route still subtracts it. No UI change, so nothing to judge on the phone. Two things I would do next: (1) add a test through serve() that a request path or query containing CRLF never reaches the real Server-Timing response header, since these tests only exercise the module; (2) a test that a handler which takes session time twice, e.g. timedAdminRepo then settingsPage, reports session once, as the clearing now makes the second take 0.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs