Skip to content

Node: req.headers identity and mutation visibility across _request materialization #245

Description

@pi0x

Context

On the Node adapter, ServerRequest lazily materializes a native (undici) Request, exposed as req._request. The native constructor copies the headers into its own Headers instance, so after materialization two header stores exist: the srvx-side NodeRequestHeaders wrapper (#headers) and the native request's private copy.

Whichever one get headers() returns, the other can go stale:

  • Current behavior (main, after the revert): once _request materializes, get headers() switches to #request.headers. A reference taken earlier becomes detached — req.headers !== h, and mutations through h are silently invisible:
    const h = req.headers;
    void req._request; // any native method (clone/blob/bytes/formData) triggers this too
    h.set("x-foo", "1"); // lost
  • Approach tried in fix(node): Node adapter correctness fixes (statusText, empty body, HEAD streaming, send errors, sync bridge) #243 (reverted in 1754b02): keep #headers canonical forever. This fixes identity/liveness of early references, but freezes req._request.headers at the materialization-time snapshot — mutations made after materialization show up in req.headers but not in _request.headers, and therefore not in clone() or formData() (which sniffs content-type from the native request).

Either way one view diverges; the question is which contract to commit to for v1, since _request is supported API.

Options

  1. Keep current behavior and document that header references must be re-read after _request materialization (mutate-after-materialize stays broken for early refs).
  2. srvx-side canonical (the reverted approach) and document that _request.headers / clone() are a snapshot as of materialization.
  3. Write-through: keep #headers canonical for reads/identity, but mirror set/append/delete into #request.headers once materialized. Keeps both views consistent at the cost of a dual-write on mutation paths (and needs care around undici's guard semantics).

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions