| /** | ||
| * The canonical error thrown when a request body exceeds `maxRequestBodySize`. | ||
| * | ||
| * It carries a stable, documented shape so any layer can map it to an HTTP | ||
| * `413 Payload Too Large` response without string matching. | ||
| * | ||
| * @see https://srvx.h3.dev/guide/body-limit | ||
| */ | ||
| interface BodyTooLargeError extends Error { | ||
| /** Stable machine-readable code. */ | ||
| code: "ERR_BODY_TOO_LARGE"; | ||
| /** HTTP status to respond with. */ | ||
| statusCode: 413; | ||
| /** Alias of {@link statusCode}. */ | ||
| status: 413; | ||
| } | ||
| /** | ||
| * Creates the canonical {@link BodyTooLargeError | `413 Payload Too Large` error} | ||
| * used across srvx when a request body exceeds the configured `maxRequestBodySize`. | ||
| * | ||
| * @see https://srvx.h3.dev/guide/body-limit | ||
| */ | ||
| declare function createBodyTooLargeError(maxRequestBodySize: number): BodyTooLargeError; | ||
| /** | ||
| * Wraps a body `ReadableStream` so the total number of bytes read cannot exceed | ||
| * `maxRequestBodySize`. | ||
| * | ||
| * The wrapper is **pull-based**: it reads from the upstream stream only when the | ||
| * consumer pulls, so it preserves backpressure and never buffers the whole body. | ||
| * As soon as the accumulated size passes the limit, the wrapped stream errors | ||
| * with the {@link createBodyTooLargeError | `413`-style error} and the upstream | ||
| * stream is cancelled with that same error (so the underlying source can stop | ||
| * producing / release the socket). Cancelling the wrapped stream propagates to | ||
| * the upstream stream. | ||
| * | ||
| * @see https://srvx.h3.dev/guide/body-limit | ||
| */ | ||
| declare function limitBodyStream(stream: ReadableStream<Uint8Array>, maxRequestBodySize: number): ReadableStream<Uint8Array>; | ||
| /** | ||
| * Returns a `Request` whose body is size-limited to `maxRequestBodySize`. | ||
| * | ||
| * If the request has no body it is returned unchanged; otherwise it is rebuilt | ||
| * with a size-limited body stream (method, url, headers and signal are | ||
| * preserved). Used for runtimes that expose a native `Request` but no body-size | ||
| * option (e.g. Deno). | ||
| * | ||
| * When the request declares a `Content-Length` that already exceeds the limit, | ||
| * the body is rejected early: the original body is cancelled without being read | ||
| * and the returned request's body errors immediately with the | ||
| * {@link createBodyTooLargeError | `413`-style error}. `Content-Length` is only | ||
| * a fast path — it may be absent (chunked transfer encoding) or understated, so | ||
| * the streaming limit is always enforced regardless. A request that overstates | ||
| * its `Content-Length` is rejected on the declared length (a malformed request, | ||
| * matching how e.g. Bun and nginx enforce limits). The error still surfaces when | ||
| * the body is consumed (`request.text()` / `.json()` / `.arrayBuffer()` / | ||
| * `.body`), matching the streamed-limit behaviour. | ||
| * | ||
| * @see https://srvx.h3.dev/guide/body-limit | ||
| */ | ||
| declare function limitRequestBody(request: Request, maxRequestBodySize: number): Request; | ||
| export { BodyTooLargeError, createBodyTooLargeError, limitBodyStream, limitRequestBody }; |
| function createBodyTooLargeError(maxRequestBodySize) { | ||
| return Object.assign(/* @__PURE__ */ new Error(`Request body exceeds the maximum allowed size of ${maxRequestBodySize} bytes.`), { | ||
| code: "ERR_BODY_TOO_LARGE", | ||
| statusCode: 413, | ||
| status: 413 | ||
| }); | ||
| } | ||
| function limitBodyStream(stream, maxRequestBodySize) { | ||
| const reader = stream.getReader(); | ||
| let size = 0; | ||
| return new ReadableStream({ | ||
| async pull(controller) { | ||
| const { done, value } = await reader.read(); | ||
| if (done) { | ||
| controller.close(); | ||
| return; | ||
| } | ||
| size += value.byteLength; | ||
| if (size > maxRequestBodySize) { | ||
| const error = createBodyTooLargeError(maxRequestBodySize); | ||
| reader.cancel(error).catch(() => {}); | ||
| controller.error(error); | ||
| return; | ||
| } | ||
| controller.enqueue(value); | ||
| }, | ||
| cancel(reason) { | ||
| return reader.cancel(reason); | ||
| } | ||
| }); | ||
| } | ||
| function limitRequestBody(request, maxRequestBodySize) { | ||
| if (!request.body) return request; | ||
| const contentLengthHeader = request.headers.get("content-length"); | ||
| if ((contentLengthHeader && /^\d+$/.test(contentLengthHeader) ? Number(contentLengthHeader) : NaN) > maxRequestBodySize) { | ||
| const error = createBodyTooLargeError(maxRequestBodySize); | ||
| request.body.cancel(error).catch(() => {}); | ||
| return new Request(request, { | ||
| body: new ReadableStream({ start(controller) { | ||
| controller.error(error); | ||
| } }), | ||
| duplex: "half" | ||
| }); | ||
| } | ||
| return new Request(request, { | ||
| body: limitBodyStream(request.body, maxRequestBodySize), | ||
| duplex: "half" | ||
| }); | ||
| } | ||
| export { createBodyTooLargeError, limitBodyStream, limitRequestBody }; |
| import { FastURL } from "../_chunks/_url.mjs"; | ||
| import { createWaitUntil, fmtURL, printListening, resolvePortAndHost, resolveTLSOptions, toNativeResponse } from "../_chunks/_utils2.mjs"; | ||
| import { limitRequestBody } from "../body-limit.mjs"; | ||
| import { gracefulShutdownPlugin, wrapFetch } from "../_chunks/_plugins.mjs"; | ||
| import { trustProxyPlugin } from "../_chunks/_trust-proxy.mjs"; | ||
| import { limitRequestBody } from "../_chunks/_body-limit.mjs"; | ||
| const FastResponse = Response; | ||
@@ -7,0 +7,0 @@ function serve(options) { |
| import { FastURL, lazyInherit } from "../_chunks/_url.mjs"; | ||
| import { createWaitUntil, fmtURL, printListening, resolvePortAndHost, resolveTLSOptions } from "../_chunks/_utils2.mjs"; | ||
| import { createBodyTooLargeError, limitBodyStream } from "../body-limit.mjs"; | ||
| import { errorPlugin, gracefulShutdownPlugin, wrapFetch } from "../_chunks/_plugins.mjs"; | ||
| import { HOST_RE, forwardedHopValue, resolveClientIP, trustedHops } from "../_chunks/_trust-proxy.mjs"; | ||
| import { createBodyTooLargeError, limitBodyStream } from "../_chunks/_body-limit.mjs"; | ||
| import nodeHTTP, { IncomingMessage, ServerResponse } from "node:http"; | ||
@@ -7,0 +7,0 @@ import { Duplex, PassThrough, Readable, addAbortSignal } from "node:stream"; |
+1
-1
@@ -208,3 +208,3 @@ import { bold, cyan, gray, green, magenta, red, url, yellow } from "./_chunks/_utils.mjs"; | ||
| name: "srvx", | ||
| version: "0.12.0", | ||
| version: "0.12.1", | ||
| description: "Universal Server." | ||
@@ -211,0 +211,0 @@ }; |
+2
-1
| { | ||
| "name": "srvx", | ||
| "version": "0.12.1", | ||
| "version": "0.12.2", | ||
| "description": "Universal Server.", | ||
@@ -30,2 +30,3 @@ "homepage": "https://srvx.h3.dev", | ||
| "./static": "./dist/static.mjs", | ||
| "./body-limit": "./dist/body-limit.mjs", | ||
| "./log": "./dist/log.mjs", | ||
@@ -32,0 +33,0 @@ "./tracing": "./dist/tracing.mjs", |
| function createBodyTooLargeError(maxRequestBodySize) { | ||
| return Object.assign(/* @__PURE__ */ new Error(`Request body exceeds the maximum allowed size of ${maxRequestBodySize} bytes.`), { | ||
| code: "ERR_BODY_TOO_LARGE", | ||
| statusCode: 413, | ||
| status: 413 | ||
| }); | ||
| } | ||
| function limitBodyStream(stream, maxRequestBodySize) { | ||
| const reader = stream.getReader(); | ||
| let size = 0; | ||
| return new ReadableStream({ | ||
| async pull(controller) { | ||
| const { done, value } = await reader.read(); | ||
| if (done) { | ||
| controller.close(); | ||
| return; | ||
| } | ||
| size += value.byteLength; | ||
| if (size > maxRequestBodySize) { | ||
| const error = createBodyTooLargeError(maxRequestBodySize); | ||
| reader.cancel(error).catch(() => {}); | ||
| controller.error(error); | ||
| return; | ||
| } | ||
| controller.enqueue(value); | ||
| }, | ||
| cancel(reason) { | ||
| return reader.cancel(reason); | ||
| } | ||
| }); | ||
| } | ||
| function limitRequestBody(request, maxRequestBodySize) { | ||
| if (!request.body) return request; | ||
| return new Request(request, { | ||
| body: limitBodyStream(request.body, maxRequestBodySize), | ||
| duplex: "half" | ||
| }); | ||
| } | ||
| export { createBodyTooLargeError, limitBodyStream, limitRequestBody }; |
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
165258
2.11%43
2.38%3500
0.32%