@zudojs/http 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -3
- package/dist/httpAdapter/http.adapter.d.ts +34 -3
- package/dist/httpAdapter/http.adapter.js +22 -6
- package/dist/httpAdapter/http.adapters.d.ts +24 -3
- package/dist/httpAdapter/http.adapters.js +24 -45
- package/dist/httpAdapter/httpAdapter.errorResponse.d.ts +2 -2
- package/dist/httpAdapter/httpAdapter.errorResponse.js +26 -9
- package/dist/httpAdapter/httpAdapter.logger.d.ts +19 -0
- package/dist/httpAdapter/httpAdapter.logger.js +21 -0
- package/dist/httpAdapter/node/httpNode.adapter.d.ts +12 -1
- package/dist/httpAdapter/node/httpNode.adapter.js +28 -14
- package/dist/httpAdapter/node/httpNode.request.d.ts +8 -7
- package/dist/httpAdapter/node/httpNode.request.js +17 -27
- package/dist/httpAdapter/node/httpNode.server.d.ts +6 -0
- package/dist/httpAdapter/node/httpNode.server.js +6 -0
- package/dist/httpAdapter/node/httpNode.type.d.ts +2 -1
- package/dist/httpAgent/http.agent.d.ts +22 -3
- package/dist/httpAgent/http.agent.js +52 -14
- package/dist/httpCacheControl/httpCacheControl.freshness.d.ts +7 -1
- package/dist/httpCacheControl/httpCacheControl.freshness.js +30 -3
- package/dist/httpCookies/http.cookies.d.ts +29 -2
- package/dist/httpCookies/http.cookies.js +44 -24
- package/dist/httpCookies/httpCookies.defaults.d.ts +40 -0
- package/dist/httpCookies/httpCookies.defaults.js +42 -0
- package/dist/httpCookies/index.d.ts +1 -0
- package/dist/httpCookies/index.js +1 -0
- package/dist/httpKeepAlive/httpKeepAlive.core.js +14 -2
- package/dist/httpMiddleware/builtin/conditional/httpMiddleware.conditional.d.ts +22 -1
- package/dist/httpMiddleware/builtin/conditional/httpMiddleware.conditional.js +19 -4
- package/dist/httpMiddleware/builtin/conditional/index.d.ts +1 -0
- package/dist/httpMiddleware/builtin/cors/httpMiddleware.cors.d.ts +8 -0
- package/dist/httpMiddleware/builtin/cors/httpMiddleware.cors.js +25 -22
- package/dist/httpMiddleware/builtin/helpers/httpMiddleware.accessor.js +2 -6
- package/dist/httpMiddleware/builtin/helpers/httpMiddleware.media.d.ts +33 -0
- package/dist/httpMiddleware/builtin/helpers/httpMiddleware.media.js +59 -0
- package/dist/httpMiddleware/builtin/helpers/index.d.ts +1 -0
- package/dist/httpMiddleware/builtin/helpers/index.js +1 -0
- package/dist/httpMiddleware/builtin/image/httpMiddleware.image.d.ts +13 -0
- package/dist/httpMiddleware/builtin/image/httpMiddleware.image.js +31 -53
- package/dist/httpMiddleware/builtin/index.d.ts +1 -0
- package/dist/httpMiddleware/builtin/index.js +1 -0
- package/dist/httpMiddleware/builtin/logging/httpMiddleware.logging.d.ts +18 -0
- package/dist/httpMiddleware/builtin/logging/httpMiddleware.logging.js +17 -1
- package/dist/httpMiddleware/builtin/rateLimit/httpMiddleware.rateLimit.d.ts +47 -0
- package/dist/httpMiddleware/builtin/rateLimit/httpMiddleware.rateLimit.js +61 -0
- package/dist/httpMiddleware/builtin/rateLimit/index.d.ts +7 -0
- package/dist/httpMiddleware/builtin/rateLimit/index.js +7 -0
- package/dist/httpMiddleware/builtin/security/httpMiddleware.security.d.ts +11 -0
- package/dist/httpMiddleware/builtin/security/httpMiddleware.security.js +25 -12
- package/dist/httpMiddleware/builtin/static/httpMiddleware.static.js +18 -4
- package/dist/httpMiddleware/builtin/video/httpMiddleware.video.d.ts +19 -1
- package/dist/httpMiddleware/builtin/video/httpMiddleware.video.js +38 -59
- package/dist/httpMiddleware/builtin/video/httpMiddleware.video.options.d.ts +12 -0
- package/dist/httpMiddleware/builtin/video/httpMiddleware.video.options.js +31 -0
- package/dist/httpMiddleware/httpMiddleware.error.d.ts +5 -31
- package/dist/httpMiddleware/httpMiddleware.error.js +5 -53
- package/dist/httpNegotiation/httpNegotiation.core.d.ts +13 -0
- package/dist/httpNegotiation/httpNegotiation.core.js +60 -8
- package/dist/httpProxy/http.proxy.d.ts +16 -0
- package/dist/httpProxy/http.proxy.js +44 -3
- package/dist/httpProxy/httpProxy.pathGuard.d.ts +23 -0
- package/dist/httpProxy/httpProxy.pathGuard.js +43 -0
- package/dist/httpProxy/index.d.ts +1 -0
- package/dist/httpProxy/index.js +1 -0
- package/dist/httpQuery/index.d.ts +11 -2
- package/dist/httpQuery/index.js +11 -2
- package/dist/httpQuery/queryParse/index.d.ts +10 -0
- package/dist/httpQuery/queryParse/index.js +10 -0
- package/dist/httpQuery/queryParse/queryParse.flat.d.ts +13 -0
- package/dist/httpQuery/queryParse/queryParse.flat.js +44 -0
- package/dist/httpQuery/queryParse/queryParse.nested.d.ts +25 -0
- package/dist/httpQuery/queryParse/queryParse.nested.js +112 -0
- package/dist/httpQuery/queryParse/queryParse.tokenizer.d.ts +37 -0
- package/dist/httpQuery/queryParse/queryParse.tokenizer.js +95 -0
- package/dist/httpQuery/queryRequest/index.d.ts +9 -0
- package/dist/httpQuery/queryRequest/index.js +9 -0
- package/dist/httpQuery/queryRequest/query.request.d.ts +43 -0
- package/dist/httpQuery/queryRequest/query.request.js +96 -0
- package/dist/httpQuery/querySerialize/index.d.ts +10 -0
- package/dist/httpQuery/querySerialize/index.js +10 -0
- package/dist/httpQuery/querySerialize/query.util.d.ts +21 -0
- package/dist/httpQuery/querySerialize/query.util.js +67 -0
- package/dist/httpQuery/querySerialize/querySerialize.core.d.ts +12 -0
- package/dist/httpQuery/querySerialize/querySerialize.core.js +97 -0
- package/dist/httpQuery/queryTypes/index.d.ts +11 -0
- package/dist/httpQuery/queryTypes/index.js +9 -0
- package/dist/httpQuery/queryTypes/query.container.d.ts +16 -0
- package/dist/httpQuery/queryTypes/query.container.js +51 -0
- package/dist/httpQuery/queryTypes/query.limit.d.ts +25 -0
- package/dist/httpQuery/queryTypes/query.limit.js +32 -0
- package/dist/httpQuery/queryTypes/query.type.d.ts +62 -0
- package/dist/httpQuery/queryTypes/query.type.js +2 -0
- package/dist/httpRedirect/http.redirect.d.ts +6 -0
- package/dist/httpRedirect/http.redirect.js +53 -2
- package/dist/httpRequest/http.request.d.ts +61 -2
- package/dist/httpRequest/http.request.js +86 -35
- package/dist/httpRequest/httpRequest.context.d.ts +7 -0
- package/dist/httpRequest/httpRequest.context.js +26 -34
- package/dist/httpRequest/index.d.ts +1 -0
- package/dist/httpRequest/index.js +1 -0
- package/dist/httpRequest/target/httpRequest.target.d.ts +45 -0
- package/dist/httpRequest/target/httpRequest.target.js +111 -0
- package/dist/httpRequest/target/index.d.ts +9 -0
- package/dist/httpRequest/target/index.js +9 -0
- package/dist/httpResponse/httpResponse.helper.js +2 -1
- package/dist/httpRouter/core/factory/httpRoute.factory.base.d.ts +18 -1
- package/dist/httpRouter/core/factory/httpRoute.factory.base.js +49 -6
- package/dist/httpRouter/core/factory/httpRoute.factory.js +3 -3
- package/dist/httpRouter/core/register/httpRouter.register.js +16 -21
- package/dist/httpRouter/core/types/httpRouter.type.d.ts +6 -0
- package/dist/httpRouter/core/util/httpRoute.util.d.ts +61 -0
- package/dist/httpRouter/core/util/httpRoute.util.js +102 -17
- package/dist/httpRouter/dispatch/httpRoute.dispatcher.d.ts +11 -0
- package/dist/httpRouter/dispatch/httpRoute.dispatcher.js +5 -3
- package/dist/httpRouter/matching/httpRoute.matcher.core.js +51 -6
- package/dist/httpRouter/matching/httpRoute.matcher.d.ts +0 -1
- package/dist/httpRouter/matching/httpRoute.matcher.js +64 -26
- package/dist/httpRouter/pattern/httpRoute.pattern.parse.d.ts +16 -0
- package/dist/httpRouter/pattern/httpRoute.pattern.parse.js +40 -11
- package/dist/httpRouter/pattern/index.d.ts +1 -1
- package/dist/httpRouter/pattern/index.js +1 -1
- package/dist/httpSecurity/httpSecurity.config.d.ts +6 -0
- package/dist/httpSecurity/httpSecurity.config.js +1 -0
- package/dist/httpSecurity/httpSecurity.guard.d.ts +7 -9
- package/dist/httpSecurity/httpSecurity.guard.js +7 -14
- package/dist/httpSecurity/httpSecurity.nodeGuard.d.ts +48 -0
- package/dist/httpSecurity/httpSecurity.nodeGuard.js +32 -0
- package/dist/httpSecurity/httpSecurity.validator.js +20 -9
- package/dist/httpSecurity/index.d.ts +2 -0
- package/dist/httpSecurity/index.js +1 -0
- package/dist/httpServer/core/httpServer.core.d.ts +11 -0
- package/dist/httpServer/core/httpServer.core.js +30 -4
- package/dist/httpTrustProxy/httpTrustProxy.compilation.d.ts +5 -2
- package/dist/httpTrustProxy/httpTrustProxy.compilation.js +19 -2
- package/dist/httpTrustProxy/httpTrustProxy.type.d.ts +3 -1
- package/dist/index.js +0 -1
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
HTTP primitives, request handling, routing, middleware, and server infrastructure for Zudojs applications.
|
|
4
4
|
|
|
5
|
+
<!-- zudo-docs:start -->
|
|
6
|
+
|
|
7
|
+
**Documentation:** [zudojs.oyinlola.site/docs/packages-http](https://zudojs.oyinlola.site/docs/packages-http) · **For AI agents:** [Markdown version](https://zudojs.oyinlola.site/docs/packages-http.md), [llms.txt](https://zudojs.oyinlola.site/llms.txt)
|
|
8
|
+
|
|
9
|
+
<!-- zudo-docs:end -->
|
|
10
|
+
|
|
5
11
|
## Installation
|
|
6
12
|
|
|
7
13
|
```bash
|
|
@@ -28,9 +34,53 @@ await server.start();
|
|
|
28
34
|
```
|
|
29
35
|
|
|
30
36
|
A handler receives an `HttpRequestContext` and may return an
|
|
31
|
-
`HttpResponseContext
|
|
32
|
-
|
|
33
|
-
`
|
|
37
|
+
`HttpResponseContext` or any JSON value. **Every value that is not an
|
|
38
|
+
`HttpResponseContext` is data**: a plain object such as `{ status: "ok" }` is
|
|
39
|
+
sent as `application/json`, whatever its keys. To choose the status, headers
|
|
40
|
+
or raw body, return `createResponseContext({ status, headers, body })`.
|
|
41
|
+
Throwing an `HttpError` created by `notFound()`, `unauthorized()` and friends
|
|
42
|
+
answers with that error's status. When errors are wrapped, the **outermost**
|
|
43
|
+
error that carries a status wins; only the middleware pipeline's own
|
|
44
|
+
wrappers are looked through, so `new HttpError(502, "Bad Gateway", { cause })`
|
|
45
|
+
answers 502 and never exposes the cause.
|
|
46
|
+
|
|
47
|
+
## Secure defaults
|
|
48
|
+
|
|
49
|
+
- **Request guard.** The Node adapter runs `guardRequest` on every request
|
|
50
|
+
before the body is read and answers `400` when it refuses: Host,
|
|
51
|
+
`X-Request-Id` format, header count and size, URL and query length, and
|
|
52
|
+
`Transfer-Encoding`/`Content-Length` smuggling. Tune it with
|
|
53
|
+
`createNodeHttpAdapter({ security: { allowedHosts: ["api.example.com"] } })`
|
|
54
|
+
or turn it off with `security: false`. HTTP/1.0 requests may omit Host, and
|
|
55
|
+
oversized bodies still get `413` from `maxBodySize`.
|
|
56
|
+
- **Canonical request targets.** Request targets with `.`/`..` segments
|
|
57
|
+
(including `%2e%2e`), backslashes or a non-origin form are refused with
|
|
58
|
+
`400`. `request.path`, the router and `createPathMiddleware` all read the
|
|
59
|
+
path with one parser, and `//host/admin` is a path, never an authority.
|
|
60
|
+
- **Path-scoped middleware** matches the way the router does: case-insensitive
|
|
61
|
+
and ignoring repeated or trailing slashes (`{ caseSensitive: true }` to opt
|
|
62
|
+
out).
|
|
63
|
+
- **Query strings** are parsed once: a repeated name is an array in both
|
|
64
|
+
`request.query`/`getQuery()` and the router's `ctx.query`.
|
|
65
|
+
- **Cookies** default to `Path=/; HttpOnly; Secure; SameSite=Lax`; override
|
|
66
|
+
any attribute explicitly. Signed cookies are bound to their name: read them
|
|
67
|
+
with `parseSignedCookie(value, secret, name)`.
|
|
68
|
+
- **`trustProxy`** accepts a hop count (`trustProxy: 1` trusts one proxy).
|
|
69
|
+
- **CORS** origin matching is `@zudojs/security`'s `isOriginAllowed`, and a
|
|
70
|
+
wildcard origin with `credentials: true` throws when the middleware is
|
|
71
|
+
created. **Rate limiting**: `createRateLimitMiddleware({ max, windowMs })`
|
|
72
|
+
wraps `@zudojs/security`'s `createRateLimiter`. Requests with no usable
|
|
73
|
+
client address share one bucket (`UNKNOWN_CLIENT_RATE_LIMIT_IP`,
|
|
74
|
+
`0.0.0.0`): they are limited together, never unlimited and never a 500.
|
|
75
|
+
- Signed-cookie signatures are compared with `@zudojs/crypto`'s constant-time
|
|
76
|
+
`timingSafeEqualString`.
|
|
77
|
+
- Contexts built by the stock adapters log through a `@zudojs/logger` console
|
|
78
|
+
logger named `http`, which redacts secret metadata fields (`authorization`,
|
|
79
|
+
`password`, `apiKey`, …).
|
|
80
|
+
- `HttpRequestGuardError`, `HttpMiddlewareError` and
|
|
81
|
+
`HttpMiddlewarePipelineError` are the `@zudojs/errors` classes, re-exported.
|
|
82
|
+
- `HttpServer.stop()` gives in-flight requests the full
|
|
83
|
+
`gracefulShutdownTimeout`.
|
|
34
84
|
|
|
35
85
|
## Features
|
|
36
86
|
|
|
@@ -64,7 +64,12 @@ export interface HttpAdapter {
|
|
|
64
64
|
*/
|
|
65
65
|
setErrorHandler?(handler: HttpErrorHandler): void;
|
|
66
66
|
start?(): void | Promise<void>;
|
|
67
|
-
|
|
67
|
+
/**
|
|
68
|
+
* Stops the adapter. `options.graceMs` bounds how long in-flight requests
|
|
69
|
+
* may run before their connections are closed; `HttpServer.stop()` passes
|
|
70
|
+
* its `gracefulShutdownTimeout` here.
|
|
71
|
+
*/
|
|
72
|
+
stop?(options?: HttpAdapterStopOptions): void | Promise<void>;
|
|
68
73
|
}
|
|
69
74
|
import { HttpAdapterError } from "@zudojs/errors";
|
|
70
75
|
export { HttpAdapterError };
|
|
@@ -96,7 +101,7 @@ export declare abstract class BaseHttpAdapter implements HttpAdapter {
|
|
|
96
101
|
protected handleError(error: unknown, request: HttpRequestContext, input: unknown, response: HttpResponseContext): Promise<void>;
|
|
97
102
|
protected write(input: unknown, context: HttpResponseContext): Promise<void>;
|
|
98
103
|
start(): Promise<void>;
|
|
99
|
-
stop(): Promise<void>;
|
|
104
|
+
stop(_options?: HttpAdapterStopOptions): Promise<void>;
|
|
100
105
|
get isStarted(): boolean;
|
|
101
106
|
}
|
|
102
107
|
export interface GenericAdapterOptions extends HttpAdapterOptions {
|
|
@@ -128,9 +133,35 @@ export declare class HttpAdapterRegistry {
|
|
|
128
133
|
clear(): void;
|
|
129
134
|
}
|
|
130
135
|
export declare function startAdapter(adapter: HttpAdapter): Promise<void>;
|
|
131
|
-
|
|
136
|
+
/**
|
|
137
|
+
* Options passed to {@link HttpAdapter.stop}.
|
|
138
|
+
*/
|
|
139
|
+
export interface HttpAdapterStopOptions {
|
|
140
|
+
/** Milliseconds in-flight requests may run before connections are closed. */
|
|
141
|
+
readonly graceMs?: number;
|
|
142
|
+
}
|
|
143
|
+
export declare function stopAdapter(adapter: HttpAdapter, options?: HttpAdapterStopOptions): Promise<void>;
|
|
132
144
|
export declare function isHttpAdapter(value: unknown): value is HttpAdapter;
|
|
145
|
+
/**
|
|
146
|
+
* Turns a handler's return value into a response.
|
|
147
|
+
*
|
|
148
|
+
* Only an `HttpResponseContext` is treated as a response; `undefined`/`null`
|
|
149
|
+
* is an empty 200. **Every other value, including any plain object, is
|
|
150
|
+
* data** and is sent as `application/json`. To answer with a status, headers
|
|
151
|
+
* or a raw body, return `createResponseContext({ status, headers, body })`.
|
|
152
|
+
*
|
|
153
|
+
* A plain object used to count as a response init whenever it had any of
|
|
154
|
+
* eight common keys, so `{ status: "ok" }` became a 500 and a row with a
|
|
155
|
+
* `body` or `metadata` column was silently emptied or truncated.
|
|
156
|
+
*/
|
|
133
157
|
export declare function normalizeHandlerResult(result: HttpHandlerResult): HttpResponseContext;
|
|
158
|
+
/**
|
|
159
|
+
* Whether a value has the shape of a response init.
|
|
160
|
+
*
|
|
161
|
+
* @deprecated No longer used to interpret handler results: a plain object
|
|
162
|
+
* returned from a handler is always sent as JSON (see
|
|
163
|
+
* {@link normalizeHandlerResult}). Kept for API compatibility.
|
|
164
|
+
*/
|
|
134
165
|
export declare function isResponseContextInit(value: unknown): value is ResponseContextInit;
|
|
135
166
|
export declare function mergeResponseContext(target: HttpResponseContext, source: HttpResponseContext): HttpResponseContext;
|
|
136
167
|
//# sourceMappingURL=http.adapter.d.ts.map
|
|
@@ -110,7 +110,7 @@ export class BaseHttpAdapter {
|
|
|
110
110
|
}
|
|
111
111
|
this.started = true;
|
|
112
112
|
}
|
|
113
|
-
async stop() {
|
|
113
|
+
async stop(_options) {
|
|
114
114
|
if (!this.started) {
|
|
115
115
|
return;
|
|
116
116
|
}
|
|
@@ -222,9 +222,9 @@ export async function startAdapter(adapter) {
|
|
|
222
222
|
await adapter.start();
|
|
223
223
|
}
|
|
224
224
|
}
|
|
225
|
-
export async function stopAdapter(adapter) {
|
|
225
|
+
export async function stopAdapter(adapter, options) {
|
|
226
226
|
if (adapter.stop) {
|
|
227
|
-
await adapter.stop();
|
|
227
|
+
await adapter.stop(options);
|
|
228
228
|
}
|
|
229
229
|
}
|
|
230
230
|
/* -------------------------------------------------------------------------- */
|
|
@@ -244,6 +244,18 @@ export function isHttpAdapter(value) {
|
|
|
244
244
|
/* -------------------------------------------------------------------------- */
|
|
245
245
|
/* Result Normalization */
|
|
246
246
|
/* -------------------------------------------------------------------------- */
|
|
247
|
+
/**
|
|
248
|
+
* Turns a handler's return value into a response.
|
|
249
|
+
*
|
|
250
|
+
* Only an `HttpResponseContext` is treated as a response; `undefined`/`null`
|
|
251
|
+
* is an empty 200. **Every other value, including any plain object, is
|
|
252
|
+
* data** and is sent as `application/json`. To answer with a status, headers
|
|
253
|
+
* or a raw body, return `createResponseContext({ status, headers, body })`.
|
|
254
|
+
*
|
|
255
|
+
* A plain object used to count as a response init whenever it had any of
|
|
256
|
+
* eight common keys, so `{ status: "ok" }` became a 500 and a row with a
|
|
257
|
+
* `body` or `metadata` column was silently emptied or truncated.
|
|
258
|
+
*/
|
|
247
259
|
export function normalizeHandlerResult(result) {
|
|
248
260
|
if (result instanceof HttpResponseContext) {
|
|
249
261
|
return result;
|
|
@@ -251,11 +263,15 @@ export function normalizeHandlerResult(result) {
|
|
|
251
263
|
if (result === undefined || result === null) {
|
|
252
264
|
return createResponseContext();
|
|
253
265
|
}
|
|
254
|
-
if (isResponseContextInit(result)) {
|
|
255
|
-
return createResponseContext(result);
|
|
256
|
-
}
|
|
257
266
|
return createResponseContext().json(result);
|
|
258
267
|
}
|
|
268
|
+
/**
|
|
269
|
+
* Whether a value has the shape of a response init.
|
|
270
|
+
*
|
|
271
|
+
* @deprecated No longer used to interpret handler results: a plain object
|
|
272
|
+
* returned from a handler is always sent as JSON (see
|
|
273
|
+
* {@link normalizeHandlerResult}). Kept for API compatibility.
|
|
274
|
+
*/
|
|
259
275
|
export function isResponseContextInit(value) {
|
|
260
276
|
if (value === null || typeof value !== "object") {
|
|
261
277
|
return false;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
2
|
import type { HTTPContext, HTTPHandler, HTTPMiddleware, HTTPRequest, HTTPResponse, HTTPRouteMatch, HTTPState } from "../httpTypes/http.types.js";
|
|
3
|
+
import type { TrustProxy } from "../httpTrustProxy/httpTrustProxy.type.js";
|
|
3
4
|
export interface HTTPAdapter<State extends HTTPState = HTTPState> {
|
|
4
5
|
readonly name: string;
|
|
5
6
|
createRequest(request: IncomingMessage): HTTPRequest;
|
|
@@ -9,9 +10,21 @@ export interface HTTPAdapter<State extends HTTPState = HTTPState> {
|
|
|
9
10
|
readonly signal?: AbortSignal;
|
|
10
11
|
}): HTTPContext<State>;
|
|
11
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* Options every Node request built by this module accepts.
|
|
15
|
+
*
|
|
16
|
+
* `trustProxy` defaults to `false`, so `X-Forwarded-For` and
|
|
17
|
+
* `X-Forwarded-Proto` are ignored and the socket peer decides `request.ip`,
|
|
18
|
+
* `request.protocol` and `request.secure`.
|
|
19
|
+
*/
|
|
20
|
+
export interface NodeRequestTrustOptions {
|
|
21
|
+
readonly trustProxy?: TrustProxy;
|
|
22
|
+
}
|
|
12
23
|
export declare class NodeHTTPAdapter<State extends HTTPState = HTTPState> implements HTTPAdapter<State> {
|
|
13
24
|
readonly name = "node";
|
|
14
|
-
|
|
25
|
+
private readonly trustProxy;
|
|
26
|
+
constructor(options?: NodeRequestTrustOptions);
|
|
27
|
+
createRequest(request: IncomingMessage, options?: NodeRequestTrustOptions): HTTPRequest;
|
|
15
28
|
createResponse(response: ServerResponse): HTTPResponse;
|
|
16
29
|
createContext(request: HTTPRequest, response: HTTPResponse, options?: {
|
|
17
30
|
readonly state?: State;
|
|
@@ -20,12 +33,19 @@ export declare class NodeHTTPAdapter<State extends HTTPState = HTTPState> implem
|
|
|
20
33
|
}
|
|
21
34
|
export interface HTTPAdapterOptions<State extends HTTPState = HTTPState> {
|
|
22
35
|
readonly adapter?: HTTPAdapter<State>;
|
|
36
|
+
/**
|
|
37
|
+
* Which socket peers may speak for a client through `X-Forwarded-*`.
|
|
38
|
+
* Defaults to `false` — forwarded headers are ignored.
|
|
39
|
+
*/
|
|
40
|
+
readonly trustProxy?: TrustProxy;
|
|
23
41
|
}
|
|
24
|
-
export declare function createHTTPAdapter<State extends HTTPState = HTTPState>(): NodeHTTPAdapter<State>;
|
|
42
|
+
export declare function createHTTPAdapter<State extends HTTPState = HTTPState>(options?: NodeRequestTrustOptions): NodeHTTPAdapter<State>;
|
|
25
43
|
export interface RequestAdapter {
|
|
26
44
|
toHTTPRequest(request: IncomingMessage): HTTPRequest;
|
|
27
45
|
}
|
|
28
46
|
export declare class NodeRequestAdapter implements RequestAdapter {
|
|
47
|
+
private readonly trustProxy;
|
|
48
|
+
constructor(options?: NodeRequestTrustOptions);
|
|
29
49
|
toHTTPRequest(request: IncomingMessage): HTTPRequest;
|
|
30
50
|
}
|
|
31
51
|
export interface ResponseAdapter {
|
|
@@ -61,11 +81,12 @@ export declare class DefaultHTTPMiddlewareAdapter<State extends HTTPState = HTTP
|
|
|
61
81
|
export interface HTTPRouteAdapter<State extends HTTPState = HTTPState> {
|
|
62
82
|
match(method: string, path: string): HTTPRouteMatch<State> | undefined;
|
|
63
83
|
}
|
|
64
|
-
export declare function adaptNodeRequest(request: IncomingMessage): HTTPRequest;
|
|
84
|
+
export declare function adaptNodeRequest(request: IncomingMessage, options?: NodeRequestTrustOptions): HTTPRequest;
|
|
65
85
|
export declare function adaptNodeResponse(response: ServerResponse): HTTPResponse;
|
|
66
86
|
export declare function adaptNodeContext<State extends HTTPState = HTTPState>(request: IncomingMessage, response: ServerResponse, options?: {
|
|
67
87
|
readonly state?: State;
|
|
68
88
|
readonly signal?: AbortSignal;
|
|
89
|
+
readonly trustProxy?: TrustProxy;
|
|
69
90
|
}): HTTPContext<State>;
|
|
70
91
|
export declare function isHTTPAdapter(value: unknown): value is HTTPAdapter;
|
|
71
92
|
//# sourceMappingURL=http.adapters.d.ts.map
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import { createHTTPContext } from "../httpContext/http.context.js";
|
|
2
2
|
import { createHTTPRequest } from "../httpRequest/http.request.js";
|
|
3
3
|
import { createHTTPResponse } from "../httpResponse/http.response.js";
|
|
4
|
-
|
|
5
|
-
/* Node Adapter */
|
|
6
|
-
/* -------------------------------------------------------------------------- */
|
|
4
|
+
import { createDefaultContextLogger } from "./httpAdapter.logger.js";
|
|
7
5
|
export class NodeHTTPAdapter {
|
|
8
6
|
name = "node";
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
trustProxy;
|
|
8
|
+
constructor(options = {}) {
|
|
9
|
+
this.trustProxy = options.trustProxy ?? false;
|
|
10
|
+
}
|
|
11
|
+
createRequest(request, options = {}) {
|
|
12
|
+
return createHTTPRequest(request, {
|
|
13
|
+
trustProxy: options.trustProxy ?? this.trustProxy,
|
|
14
|
+
});
|
|
11
15
|
}
|
|
12
16
|
createResponse(response) {
|
|
13
17
|
return createHTTPResponse(response);
|
|
@@ -18,19 +22,23 @@ export class NodeHTTPAdapter {
|
|
|
18
22
|
response,
|
|
19
23
|
state: options.state ?? {},
|
|
20
24
|
signal: options.signal,
|
|
21
|
-
logger:
|
|
25
|
+
logger: createDefaultContextLogger(),
|
|
22
26
|
});
|
|
23
27
|
}
|
|
24
28
|
}
|
|
25
29
|
/* -------------------------------------------------------------------------- */
|
|
26
30
|
/* Adapter Factory */
|
|
27
31
|
/* -------------------------------------------------------------------------- */
|
|
28
|
-
export function createHTTPAdapter() {
|
|
29
|
-
return new NodeHTTPAdapter();
|
|
32
|
+
export function createHTTPAdapter(options = {}) {
|
|
33
|
+
return new NodeHTTPAdapter(options);
|
|
30
34
|
}
|
|
31
35
|
export class NodeRequestAdapter {
|
|
36
|
+
trustProxy;
|
|
37
|
+
constructor(options = {}) {
|
|
38
|
+
this.trustProxy = options.trustProxy ?? false;
|
|
39
|
+
}
|
|
32
40
|
toHTTPRequest(request) {
|
|
33
|
-
return createHTTPRequest(request);
|
|
41
|
+
return createHTTPRequest(request, { trustProxy: this.trustProxy });
|
|
34
42
|
}
|
|
35
43
|
}
|
|
36
44
|
export class NodeResponseAdapter {
|
|
@@ -45,7 +53,7 @@ export class NodeContextAdapter {
|
|
|
45
53
|
response,
|
|
46
54
|
state: options.state ?? {},
|
|
47
55
|
signal: options.signal,
|
|
48
|
-
logger:
|
|
56
|
+
logger: createDefaultContextLogger(),
|
|
49
57
|
});
|
|
50
58
|
}
|
|
51
59
|
}
|
|
@@ -75,19 +83,21 @@ export class DefaultHTTPMiddlewareAdapter {
|
|
|
75
83
|
/* -------------------------------------------------------------------------- */
|
|
76
84
|
/* Request Conversion Helpers */
|
|
77
85
|
/* -------------------------------------------------------------------------- */
|
|
78
|
-
export function adaptNodeRequest(request) {
|
|
79
|
-
return createHTTPRequest(request
|
|
86
|
+
export function adaptNodeRequest(request, options = {}) {
|
|
87
|
+
return createHTTPRequest(request, {
|
|
88
|
+
trustProxy: options.trustProxy ?? false,
|
|
89
|
+
});
|
|
80
90
|
}
|
|
81
91
|
export function adaptNodeResponse(response) {
|
|
82
92
|
return createHTTPResponse(response);
|
|
83
93
|
}
|
|
84
94
|
export function adaptNodeContext(request, response, options = {}) {
|
|
85
95
|
return createHTTPContext({
|
|
86
|
-
request: adaptNodeRequest(request),
|
|
96
|
+
request: adaptNodeRequest(request, { trustProxy: options.trustProxy }),
|
|
87
97
|
response: adaptNodeResponse(response),
|
|
88
98
|
state: options.state ?? {},
|
|
89
99
|
signal: options.signal,
|
|
90
|
-
logger:
|
|
100
|
+
logger: createDefaultContextLogger(),
|
|
91
101
|
});
|
|
92
102
|
}
|
|
93
103
|
/* -------------------------------------------------------------------------- */
|
|
@@ -103,35 +113,4 @@ export function isHTTPAdapter(value) {
|
|
|
103
113
|
typeof candidate.createResponse === "function" &&
|
|
104
114
|
typeof candidate.createContext === "function");
|
|
105
115
|
}
|
|
106
|
-
/* -------------------------------------------------------------------------- */
|
|
107
|
-
/* Fallback Logger */
|
|
108
|
-
/* -------------------------------------------------------------------------- */
|
|
109
|
-
function createFallbackLogger() {
|
|
110
|
-
return {
|
|
111
|
-
info(message, metadata) {
|
|
112
|
-
if (metadata !== undefined) {
|
|
113
|
-
console.info(message, metadata);
|
|
114
|
-
}
|
|
115
|
-
else {
|
|
116
|
-
console.info(message);
|
|
117
|
-
}
|
|
118
|
-
},
|
|
119
|
-
warn(message, metadata) {
|
|
120
|
-
if (metadata !== undefined) {
|
|
121
|
-
console.warn(message, metadata);
|
|
122
|
-
}
|
|
123
|
-
else {
|
|
124
|
-
console.warn(message);
|
|
125
|
-
}
|
|
126
|
-
},
|
|
127
|
-
error(message, metadata) {
|
|
128
|
-
if (metadata !== undefined) {
|
|
129
|
-
console.error(message, metadata);
|
|
130
|
-
}
|
|
131
|
-
else {
|
|
132
|
-
console.error(message);
|
|
133
|
-
}
|
|
134
|
-
},
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
116
|
//# sourceMappingURL=http.adapters.js.map
|
|
@@ -23,8 +23,8 @@ export interface ResolvedErrorResponse {
|
|
|
23
23
|
/**
|
|
24
24
|
* Builds the default response for an unhandled error.
|
|
25
25
|
*
|
|
26
|
-
* -
|
|
27
|
-
*
|
|
26
|
+
* - The outermost error with a 4xx/5xx `statusCode` (looking through the
|
|
27
|
+
* pipeline's own wrappers only) is answered with that status.
|
|
28
28
|
* - Its `message` and `code` are included only when the error opts in with
|
|
29
29
|
* `expose: true` (the `@zudojs/errors` default for 4xx); otherwise the
|
|
30
30
|
* body carries the status text alone, so a 5xx never leaks internals.
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
import { getStatusText } from "../httpResponse/core/httpResponse.statusText.js";
|
|
19
19
|
import { isValidHeaderFieldValue } from "../httpHeaders/security/index.js";
|
|
20
|
+
import { HttpMiddlewareError, HttpMiddlewarePipelineError, } from "../httpMiddleware/httpMiddleware.error.js";
|
|
20
21
|
/* -------------------------------------------------------------------------- */
|
|
21
22
|
/* Constants */
|
|
22
23
|
/* -------------------------------------------------------------------------- */
|
|
@@ -30,11 +31,25 @@ const MAX_UNWRAP_DEPTH = 8;
|
|
|
30
31
|
/* Resolution */
|
|
31
32
|
/* -------------------------------------------------------------------------- */
|
|
32
33
|
/**
|
|
33
|
-
*
|
|
34
|
+
* Whether `error` is one of the pipeline's own wrappers. These are added by
|
|
35
|
+
* the framework on the way out and always report 500, so they are looked
|
|
36
|
+
* through; nothing else is.
|
|
37
|
+
*/
|
|
38
|
+
function isFrameworkWrapper(error) {
|
|
39
|
+
return (error instanceof HttpMiddlewareError ||
|
|
40
|
+
error instanceof HttpMiddlewarePipelineError);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Locates the error whose status answers the request.
|
|
34
44
|
*
|
|
35
|
-
* The
|
|
36
|
-
*
|
|
37
|
-
*
|
|
45
|
+
* The **outermost** error that carries a status wins. Only the framework's
|
|
46
|
+
* own wrappers (`HttpMiddlewareError`, `HttpMiddlewarePipelineError`) are
|
|
47
|
+
* unwrapped. An application that throws
|
|
48
|
+
* `new HttpError(502, "Bad Gateway", { cause: upstream401 })` is mapping an
|
|
49
|
+
* upstream failure on purpose; taking the innermost status sent the client
|
|
50
|
+
* the upstream's 401, its message and its `WWW-Authenticate` header.
|
|
51
|
+
* An error that is neither a wrapper nor carries a status ends the search
|
|
52
|
+
* (generic 500), so a `cause` the application attached is never exposed.
|
|
38
53
|
*/
|
|
39
54
|
function findStatusError(error, depth, seen) {
|
|
40
55
|
if (error === null || typeof error !== "object" || depth > MAX_UNWRAP_DEPTH) {
|
|
@@ -45,6 +60,11 @@ function findStatusError(error, depth, seen) {
|
|
|
45
60
|
}
|
|
46
61
|
seen.add(error);
|
|
47
62
|
const candidate = error;
|
|
63
|
+
if (!isFrameworkWrapper(error)) {
|
|
64
|
+
return isHttpStatus(candidate.statusCode)
|
|
65
|
+
? candidate
|
|
66
|
+
: undefined;
|
|
67
|
+
}
|
|
48
68
|
const nested = [];
|
|
49
69
|
if (Array.isArray(candidate.errors)) {
|
|
50
70
|
nested.push(...candidate.errors);
|
|
@@ -58,9 +78,6 @@ function findStatusError(error, depth, seen) {
|
|
|
58
78
|
return found;
|
|
59
79
|
}
|
|
60
80
|
}
|
|
61
|
-
if (isHttpStatus(candidate.statusCode)) {
|
|
62
|
-
return candidate;
|
|
63
|
-
}
|
|
64
81
|
return undefined;
|
|
65
82
|
}
|
|
66
83
|
function isHttpStatus(value) {
|
|
@@ -72,8 +89,8 @@ function isHttpStatus(value) {
|
|
|
72
89
|
/**
|
|
73
90
|
* Builds the default response for an unhandled error.
|
|
74
91
|
*
|
|
75
|
-
* -
|
|
76
|
-
*
|
|
92
|
+
* - The outermost error with a 4xx/5xx `statusCode` (looking through the
|
|
93
|
+
* pipeline's own wrappers only) is answered with that status.
|
|
77
94
|
* - Its `message` and `code` are included only when the error opts in with
|
|
78
95
|
* `expose: true` (the `@zudojs/errors` default for 4xx); otherwise the
|
|
79
96
|
* body carries the status text alone, so a 5xx never leaks internals.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default logger for contexts built by the stock adapters.
|
|
3
|
+
*
|
|
4
|
+
* @module httpAdapter/httpAdapter.logger
|
|
5
|
+
*/
|
|
6
|
+
import { type Logger } from "@zudojs/logger";
|
|
7
|
+
/**
|
|
8
|
+
* Creates the logger a stock adapter gives each `HTTPContext` when the
|
|
9
|
+
* caller supplies none: a `@zudojs/logger` console logger named `http`.
|
|
10
|
+
*
|
|
11
|
+
* It goes through the logger's secret-field redaction, so metadata such as
|
|
12
|
+
* `authorization`, `password` or `apiKey` passed to `ctx.log()` is printed
|
|
13
|
+
* as `[REDACTED]`. The earlier fallback called `console.*` with the raw
|
|
14
|
+
* metadata object. One logger per context (construction costs a few
|
|
15
|
+
* microseconds), so `setLevel()` / `disable()` / `close()` on one context's
|
|
16
|
+
* logger cannot silence another's.
|
|
17
|
+
*/
|
|
18
|
+
export declare function createDefaultContextLogger(): Logger;
|
|
19
|
+
//# sourceMappingURL=httpAdapter.logger.d.ts.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default logger for contexts built by the stock adapters.
|
|
3
|
+
*
|
|
4
|
+
* @module httpAdapter/httpAdapter.logger
|
|
5
|
+
*/
|
|
6
|
+
import { createDefaultLogger } from "@zudojs/logger";
|
|
7
|
+
/**
|
|
8
|
+
* Creates the logger a stock adapter gives each `HTTPContext` when the
|
|
9
|
+
* caller supplies none: a `@zudojs/logger` console logger named `http`.
|
|
10
|
+
*
|
|
11
|
+
* It goes through the logger's secret-field redaction, so metadata such as
|
|
12
|
+
* `authorization`, `password` or `apiKey` passed to `ctx.log()` is printed
|
|
13
|
+
* as `[REDACTED]`. The earlier fallback called `console.*` with the raw
|
|
14
|
+
* metadata object. One logger per context (construction costs a few
|
|
15
|
+
* microseconds), so `setLevel()` / `disable()` / `close()` on one context's
|
|
16
|
+
* logger cannot silence another's.
|
|
17
|
+
*/
|
|
18
|
+
export function createDefaultContextLogger() {
|
|
19
|
+
return createDefaultLogger("http");
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=httpAdapter.logger.js.map
|
|
@@ -10,6 +10,7 @@ import { Server } from "node:http";
|
|
|
10
10
|
import { HttpRequestContext } from "../../httpRequest/httpRequest.context.js";
|
|
11
11
|
import { HttpResponseContext } from "../../httpResponse/httpResponse.context.js";
|
|
12
12
|
import { BaseHttpAdapter } from "../http.adapter.js";
|
|
13
|
+
import type { HttpAdapterStopOptions } from "../http.adapter.js";
|
|
13
14
|
import type { HttpResponseWriter } from "../../httpResponse/httpResponse.writer.js";
|
|
14
15
|
import type { NodeAdapterOptions, NodeServerAddress } from "./httpNode.type.js";
|
|
15
16
|
export declare class NodeHttpAdapter extends BaseHttpAdapter {
|
|
@@ -25,6 +26,7 @@ export declare class NodeHttpAdapter extends BaseHttpAdapter {
|
|
|
25
26
|
private readonly connectionsCheckingInterval;
|
|
26
27
|
private readonly shutdownGraceMs;
|
|
27
28
|
private readonly events;
|
|
29
|
+
private readonly requestGuard;
|
|
28
30
|
private server;
|
|
29
31
|
private ownsServer;
|
|
30
32
|
/**
|
|
@@ -50,6 +52,10 @@ export declare class NodeHttpAdapter extends BaseHttpAdapter {
|
|
|
50
52
|
*/
|
|
51
53
|
private attachNodeBody;
|
|
52
54
|
private executeNodeHandler;
|
|
55
|
+
/**
|
|
56
|
+
* Plain objects are data and are sent as JSON; see
|
|
57
|
+
* `normalizeHandlerResult`.
|
|
58
|
+
*/
|
|
53
59
|
private normalizeResult;
|
|
54
60
|
private handleNodeError;
|
|
55
61
|
private writeBadRequest;
|
|
@@ -65,7 +71,12 @@ export declare class NodeHttpAdapter extends BaseHttpAdapter {
|
|
|
65
71
|
private dispatchNodeRequest;
|
|
66
72
|
private emitAdapterError;
|
|
67
73
|
start(): Promise<void>;
|
|
68
|
-
|
|
74
|
+
/**
|
|
75
|
+
* Closes the server. The grace period is `options.graceMs` (the server's
|
|
76
|
+
* `gracefulShutdownTimeout`), capped by an explicit `shutdownGraceMs`;
|
|
77
|
+
* with neither it is 10 s.
|
|
78
|
+
*/
|
|
79
|
+
stop(options?: HttpAdapterStopOptions): Promise<void>;
|
|
69
80
|
}
|
|
70
81
|
export declare function createNodeHttpAdapter(options?: NodeAdapterOptions): NodeHttpAdapter;
|
|
71
82
|
//# sourceMappingURL=httpNode.adapter.d.ts.map
|
|
@@ -9,13 +9,15 @@
|
|
|
9
9
|
import { IncomingMessage, Server, ServerResponse, createServer, } from "node:http";
|
|
10
10
|
import { HttpRequestContext } from "../../httpRequest/httpRequest.context.js";
|
|
11
11
|
import { HttpResponseContext, createResponseContext, } from "../../httpResponse/httpResponse.context.js";
|
|
12
|
-
import { BaseHttpAdapter } from "../http.adapter.js";
|
|
12
|
+
import { BaseHttpAdapter, normalizeHandlerResult } from "../http.adapter.js";
|
|
13
13
|
import { writeResponse } from "../../httpResponse/httpResponse.writer.js";
|
|
14
14
|
import { DEFAULT_HOST, DEFAULT_PORT, DEFAULT_MAX_BODY_SIZE, NODE_DEFAULT_HEADERS_TIMEOUT, NODE_DEFAULT_REQUEST_TIMEOUT, NODE_DEFAULT_KEEP_ALIVE_TIMEOUT, validatePort, validateMaxBodySize, } from "./httpNode.type.js";
|
|
15
15
|
import { NodeResponseWriter } from "./httpNode.response.js";
|
|
16
|
+
import { compileTrustProxy } from "../../httpTrustProxy/httpTrustProxy.compilation.js";
|
|
17
|
+
import { createNodeRequestGuard } from "../../httpSecurity/httpSecurity.nodeGuard.js";
|
|
16
18
|
import { createNodeRequestContext } from "./httpNode.request.js";
|
|
17
19
|
import { resolveErrorResponse } from "../httpAdapter.errorResponse.js";
|
|
18
|
-
import { isIncomingMessage, isServerResponse, isNodeRequestResponsePair, configureServer, listen, closeServer, readNodeRequestBody, NodeRequestBodyTooLargeError,
|
|
20
|
+
import { isIncomingMessage, isServerResponse, isNodeRequestResponsePair, configureServer, listen, closeServer, readNodeRequestBody, NodeRequestBodyTooLargeError, } from "./httpNode.server.js";
|
|
19
21
|
/* -------------------------------------------------------------------------- */
|
|
20
22
|
/* Node HTTP Adapter */
|
|
21
23
|
/* -------------------------------------------------------------------------- */
|
|
@@ -32,6 +34,7 @@ export class NodeHttpAdapter extends BaseHttpAdapter {
|
|
|
32
34
|
connectionsCheckingInterval;
|
|
33
35
|
shutdownGraceMs;
|
|
34
36
|
events;
|
|
37
|
+
requestGuard;
|
|
35
38
|
server;
|
|
36
39
|
ownsServer = false;
|
|
37
40
|
/**
|
|
@@ -73,7 +76,11 @@ export class NodeHttpAdapter extends BaseHttpAdapter {
|
|
|
73
76
|
Math.min(30_000, this.headersTimeout);
|
|
74
77
|
this.shutdownGraceMs = options.shutdownGraceMs;
|
|
75
78
|
this.trustProxy = options.trustProxy;
|
|
79
|
+
if (options.trustProxy !== undefined) {
|
|
80
|
+
compileTrustProxy(options.trustProxy);
|
|
81
|
+
}
|
|
76
82
|
this.events = options.events ?? {};
|
|
83
|
+
this.requestGuard = createNodeRequestGuard(options.security);
|
|
77
84
|
this.server = options.server;
|
|
78
85
|
this.ownsServer = !options.server;
|
|
79
86
|
}
|
|
@@ -132,6 +139,10 @@ export class NodeHttpAdapter extends BaseHttpAdapter {
|
|
|
132
139
|
}
|
|
133
140
|
const request = input.request;
|
|
134
141
|
const response = input.response;
|
|
142
|
+
if (this.requestGuard && !this.requestGuard(request).allowed) {
|
|
143
|
+
await this.writeBadRequest(response);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
135
146
|
let context;
|
|
136
147
|
try {
|
|
137
148
|
context = this.createRequest(request);
|
|
@@ -183,17 +194,12 @@ export class NodeHttpAdapter extends BaseHttpAdapter {
|
|
|
183
194
|
}
|
|
184
195
|
return this.handler(request);
|
|
185
196
|
}
|
|
197
|
+
/**
|
|
198
|
+
* Plain objects are data and are sent as JSON; see
|
|
199
|
+
* `normalizeHandlerResult`.
|
|
200
|
+
*/
|
|
186
201
|
normalizeResult(result) {
|
|
187
|
-
|
|
188
|
-
return result;
|
|
189
|
-
}
|
|
190
|
-
if (result === undefined || result === null) {
|
|
191
|
-
return createResponseContext();
|
|
192
|
-
}
|
|
193
|
-
if (isResponseContextLike(result)) {
|
|
194
|
-
return createResponseContext(result);
|
|
195
|
-
}
|
|
196
|
-
return createResponseContext().json(result);
|
|
202
|
+
return normalizeHandlerResult(result);
|
|
197
203
|
}
|
|
198
204
|
async handleNodeError(error, request, response) {
|
|
199
205
|
if (response.headersSent) {
|
|
@@ -326,12 +332,20 @@ export class NodeHttpAdapter extends BaseHttpAdapter {
|
|
|
326
332
|
}
|
|
327
333
|
await super.start();
|
|
328
334
|
}
|
|
329
|
-
|
|
335
|
+
/**
|
|
336
|
+
* Closes the server. The grace period is `options.graceMs` (the server's
|
|
337
|
+
* `gracefulShutdownTimeout`), capped by an explicit `shutdownGraceMs`;
|
|
338
|
+
* with neither it is 10 s.
|
|
339
|
+
*/
|
|
340
|
+
async stop(options = {}) {
|
|
330
341
|
if (!this.server || !this.server.listening) {
|
|
331
342
|
await super.stop();
|
|
332
343
|
return;
|
|
333
344
|
}
|
|
334
|
-
|
|
345
|
+
const graceMs = options.graceMs === undefined
|
|
346
|
+
? this.shutdownGraceMs
|
|
347
|
+
: Math.min(options.graceMs, this.shutdownGraceMs ?? Infinity);
|
|
348
|
+
await closeServer(this.server, { graceMs });
|
|
335
349
|
if (this.clientErrorListener) {
|
|
336
350
|
this.server.off("clientError", this.clientErrorListener);
|
|
337
351
|
this.clientErrorListener = undefined;
|
|
@@ -21,13 +21,14 @@ export declare function getNodeRemoteAddress(request: IncomingMessage, options?:
|
|
|
21
21
|
/**
|
|
22
22
|
* Parses the request-target's query string into a flat record.
|
|
23
23
|
*
|
|
24
|
-
* Every value is attacker-controlled
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
24
|
+
* Every value is attacker-controlled, so decoding never throws (a malformed
|
|
25
|
+
* `%E0` is kept raw) and `+` has its form-encoding meaning. This uses the same
|
|
26
|
+
* parser as the router's `ctx.query` (`parseQueryString`), so both agree: a
|
|
27
|
+
* repeated name is an array (`?role=user&role=admin` gives
|
|
28
|
+
* `["user", "admin"]`), the record has a `null` prototype, and
|
|
29
|
+
* `__proto__` / `constructor` / `prototype` are dropped. Previously the last
|
|
30
|
+
* value silently won here while the router returned the array.
|
|
30
31
|
*/
|
|
31
|
-
export declare function parseNodeQuery(request: IncomingMessage): Readonly<Record<string, string>>;
|
|
32
|
+
export declare function parseNodeQuery(request: IncomingMessage): Readonly<Record<string, string | readonly string[]>>;
|
|
32
33
|
export declare function createNodeRequestContext(request: IncomingMessage, options?: NodeRequestOptions): HttpRequestContext;
|
|
33
34
|
//# sourceMappingURL=httpNode.request.d.ts.map
|