Skip to content

limitRequestBody: preserve ServerRequest (throws / drops augmentation on the Node request) #281

Description

@pi0x

Context

srvx/body-limit's limitRequestBody(request, max) rebuilds the request via new Request(request, { body, duplex }):

https://github.com/h3js/srvx/blob/main/src/body-limit.ts

That's fine for a plain web Request, but it doesn't work on srvx's own ServerRequest — which is exactly the object srvx hands to handlers (event.req in h3). We hit this while wiring per-handler body limits in h3 (h3js/h3#1500).

Problem 1 — throws on the Node ServerRequest

Passing srvx's Node ServerRequest into new Request(request, ...) throws:

TypeError: Cannot read private member #state from an object whose class did not declare it

i.e. the Node adapter's request isn't a native Request under the hood, so new Request(serverRequest, init) can't re-wrap it. So limitRequestBody currently can't be applied to event.req on Node at all.

Problem 2 — drops runtime augmentation

Even where the rebuild succeeds (web/Deno), the result is a plain Request — it loses srvx's augmentation (runtime, waitUntil, ip, context). A downstream layer that swaps the request back in then has to re-copy those props one-by-one, which is fragile (a newly-added augmentation prop silently drops off).

Repro

import { limitRequestBody } from "srvx/body-limit";

export default {
  fetch(req) {                       // req is srvx ServerRequest
    const limited = limitRequestBody(req, 1024); // throws on Node
    return new Response("ok");
  },
};

Proposed fix

Make limitRequestBody accept and return a ServerRequest intact. Two options:

  1. Proxy-wrap instead of rebuild — route body reads (body / text / json / formData / arrayBuffer / blob / bytes) through a single lazy limitBodyStream, and pass everything else through with Reflect.get. No new Request, no duplex hack, and augmentation is preserved for free. This is what we ended up doing in h3 as a local workaround:

    https://github.com/h3js/h3/blob/perf/body-limit/src/utils/internal/body.ts (limitRequestBody)

  2. Reconstruct through srvx's ServerRequest constructor, carrying augmentation, so the Node #state path is handled internally.

If limitRequestBody preserved ServerRequest, h3 could drop its copy and just call the srvx export directly. Happy to send a PR (option 1) if you'd like.

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