@nifrajs/proxy 2.14.1 → 3.1.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/dist/index.d.ts +128 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +579 -46
- package/dist/index.js.map +1 -1
- package/dist/node-stream.d.ts +61 -0
- package/dist/node-stream.d.ts.map +1 -0
- package/dist/node-stream.js +135 -0
- package/dist/node-stream.js.map +1 -0
- package/dist/undici.d.ts +50 -0
- package/dist/undici.d.ts.map +1 -0
- package/dist/undici.js +138 -0
- package/dist/undici.js.map +1 -0
- package/package.json +17 -2
- package/src/index.ts +792 -50
- package/src/node-stream.ts +174 -0
- package/src/undici.ts +164 -0
package/dist/index.d.ts
CHANGED
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
* by a `Connection` header, and `Proxy-*` headers are dropped from the forwarded request and from
|
|
11
11
|
* the relayed response (the Connection-nominated leak is the @fastify/http-proxy CVE-2026-33805 /
|
|
12
12
|
* Hono proxy CVE-2026-71849 class). Forwarding metadata (`Forwarded`, `X-Forwarded-*`) is stripped
|
|
13
|
-
* unless `forwardClientIp: true`, in which case the caller
|
|
13
|
+
* unless `forwardClientIp: true`, in which case only the observed caller IP is emitted by default.
|
|
14
|
+
* An inbound chain is preserved only with the explicit `trustForwardedFor: true` declaration. Upstream
|
|
14
15
|
* redirects are never followed (`redirect: "manual"`) - following one would let the upstream steer
|
|
15
16
|
* the proxy anywhere. TLS verification is always on; there is no knob to disable it.
|
|
16
17
|
*/
|
|
@@ -20,6 +21,84 @@ export interface ProxyContext {
|
|
|
20
21
|
readonly clientIp?: string | undefined;
|
|
21
22
|
readonly signal?: AbortSignal | undefined;
|
|
22
23
|
}
|
|
24
|
+
/** The forwarded request, after hygiene, as handed to a {@link ProxyTransport}. */
|
|
25
|
+
export interface ProxyUpstreamRequest {
|
|
26
|
+
readonly method: string;
|
|
27
|
+
/** Already sanitised: hop-by-hop, Connection-nominated, `Proxy-*`, `host`, and (unless opted in)
|
|
28
|
+
* forwarding headers are gone, and static `headers` have been applied. Send them as given. */
|
|
29
|
+
readonly headers: Headers;
|
|
30
|
+
readonly body: ReadableStream<Uint8Array> | null;
|
|
31
|
+
/** Aborts on the deadline or on caller disconnect. A transport MUST honour it. */
|
|
32
|
+
readonly signal: AbortSignal;
|
|
33
|
+
}
|
|
34
|
+
/** What a {@link ProxyTransport} returns. Header hygiene on the way back is the proxy's job. */
|
|
35
|
+
export interface ProxyUpstreamResponse {
|
|
36
|
+
readonly status: number;
|
|
37
|
+
/** Empty string is fine - HTTP/2 has no reason phrase, and nothing downstream depends on it. */
|
|
38
|
+
readonly statusText: string;
|
|
39
|
+
readonly headers: Headers;
|
|
40
|
+
readonly body: ReadableStream<Uint8Array> | null;
|
|
41
|
+
/**
|
|
42
|
+
* Whether `body` is still exactly the bytes the upstream sent, `Content-Encoding` intact. `fetch`
|
|
43
|
+
* transparently decodes a compressed response, so its body is identity and the stored
|
|
44
|
+
* `content-encoding`/`content-length` no longer describe it - both are dropped on relay (the
|
|
45
|
+
* default when this is omitted). A transport that does NOT decode (undici's `request` does not)
|
|
46
|
+
* MUST set this `true` so the encoding and length are relayed instead, letting the client decode
|
|
47
|
+
* the bytes it actually receives. Getting this wrong ships a gzip body labelled identity.
|
|
48
|
+
*/
|
|
49
|
+
readonly bodyEncoded?: boolean;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* How the forwarded request reaches the upstream. Defaults to the undici transport on Node when
|
|
53
|
+
* `undici` is installed (substantially faster there), and to `fetch` everywhere else.
|
|
54
|
+
*
|
|
55
|
+
* A transport is a security boundary, and swapping it moves three of this package's guarantees into
|
|
56
|
+
* your implementation. It MUST dial exactly `target` and nothing else, MUST NOT follow redirects
|
|
57
|
+
* (relay the 3xx as-is), and MUST leave TLS verification on. It MUST NOT add, drop, or rewrite the
|
|
58
|
+
* headers it is handed - they have already been sanitised, and re-adding `host` or a forwarding
|
|
59
|
+
* header undoes that work. If it does not decode `Content-Encoding`, it MUST set
|
|
60
|
+
* {@link ProxyUpstreamResponse.bodyEncoded} so the encoding is relayed rather than stripped.
|
|
61
|
+
*
|
|
62
|
+
* `@nifrajs/proxy/undici` ships the one selected by default on Node.
|
|
63
|
+
*/
|
|
64
|
+
export type ProxyTransport = (target: URL, request: ProxyUpstreamRequest) => Promise<ProxyUpstreamResponse>;
|
|
65
|
+
/** Structural request view used only by the Node-native mount seam. The raw body stays the
|
|
66
|
+
* IncomingMessage; no Web Request, Headers, or Web stream is created before the transport sees it. */
|
|
67
|
+
export interface NodeNativeProxyRequest {
|
|
68
|
+
readonly method: string;
|
|
69
|
+
/** Absolute URL assembled by the Node adapter from its trusted protocol/host policy. */
|
|
70
|
+
readonly url: string;
|
|
71
|
+
readonly headers: Readonly<Record<string, string | readonly string[] | undefined>>;
|
|
72
|
+
readonly raw: unknown;
|
|
73
|
+
}
|
|
74
|
+
/** Structural response view used by the Node-native mount seam. */
|
|
75
|
+
export interface NodeNativeProxyResponse {
|
|
76
|
+
readonly destroyed?: boolean;
|
|
77
|
+
readonly writableEnded?: boolean;
|
|
78
|
+
readonly writable?: boolean;
|
|
79
|
+
readonly headersSent?: boolean;
|
|
80
|
+
setHeader(name: string, value: string | readonly string[]): void;
|
|
81
|
+
writeHead(status: number): void;
|
|
82
|
+
end(body?: string | Uint8Array): void;
|
|
83
|
+
once(event: string, listener: (...args: unknown[]) => void): unknown;
|
|
84
|
+
removeListener(event: string, listener: (...args: unknown[]) => void): unknown;
|
|
85
|
+
destroy(error?: unknown): unknown;
|
|
86
|
+
}
|
|
87
|
+
export interface NativeProxyTransportRequest {
|
|
88
|
+
readonly method: string;
|
|
89
|
+
/** Flat name/value pairs already sanitized by the proxy. */
|
|
90
|
+
readonly headers: readonly string[];
|
|
91
|
+
readonly body: unknown;
|
|
92
|
+
readonly signal: AbortSignal;
|
|
93
|
+
readonly headersTimeout: number;
|
|
94
|
+
}
|
|
95
|
+
export interface NativeProxyUpstreamResponse {
|
|
96
|
+
readonly status: number;
|
|
97
|
+
readonly headers: Readonly<Record<string, string | readonly string[] | undefined>>;
|
|
98
|
+
readonly body: unknown;
|
|
99
|
+
dump(): Promise<void>;
|
|
100
|
+
}
|
|
101
|
+
export type NativeProxyTransport = (target: URL, request: NativeProxyTransportRequest) => Promise<NativeProxyUpstreamResponse>;
|
|
23
102
|
export interface ProxyOptions {
|
|
24
103
|
/**
|
|
25
104
|
* The upstream to forward every request to, as a **bare origin** (`https://api.internal:8443`).
|
|
@@ -32,19 +111,62 @@ export interface ProxyOptions {
|
|
|
32
111
|
readonly stripPrefix?: string;
|
|
33
112
|
/**
|
|
34
113
|
* Forward caller metadata upstream. Default **false**: `Forwarded` and `X-Forwarded-*` headers
|
|
35
|
-
* are stripped, so a client-forged chain never reaches the upstream. When true, the
|
|
36
|
-
* `X-Forwarded-For`
|
|
37
|
-
*
|
|
38
|
-
*
|
|
114
|
+
* are stripped, so a client-forged chain never reaches the upstream. When true, the observed caller
|
|
115
|
+
* IP replaces inbound `X-Forwarded-For` by default (pass a `ProxyContext` so `c.clientIp` - already
|
|
116
|
+
* filtered by the app's trust declaration - is the value emitted). With a bare `Request`, forwarding
|
|
117
|
+
* metadata is suppressed rather than passed through.
|
|
39
118
|
*/
|
|
40
119
|
readonly forwardClientIp?: boolean;
|
|
41
|
-
/**
|
|
120
|
+
/**
|
|
121
|
+
* Preserve an inbound `X-Forwarded-For` chain before appending the observed caller IP. Set this only
|
|
122
|
+
* when a proxy you operate has already stripped and rebuilt the inbound chain; otherwise a directly
|
|
123
|
+
* reachable caller can forge the leading entries. Requires `forwardClientIp: true`.
|
|
124
|
+
*/
|
|
125
|
+
readonly trustForwardedFor?: boolean;
|
|
126
|
+
/**
|
|
127
|
+
* Deadline in milliseconds for the upstream to *begin* answering. Default `30_000`; expiry
|
|
128
|
+
* answers `504`. It covers up to the response headers, which is the only window in which a `504`
|
|
129
|
+
* is still sendable - once the status has been relayed the exchange cannot be turned into one. A
|
|
130
|
+
* body that starts and then stalls is the transport's timeout to enforce; both built-in transports
|
|
131
|
+
* cap the idle gap between chunks at 30s (undici's `bodyTimeout`, and an equivalent per-chunk timer
|
|
132
|
+
* on the portable one) and error the relayed stream past it. A custom transport owes the same
|
|
133
|
+
* bound. Caller disconnect still tears the upstream down at any point.
|
|
134
|
+
*/
|
|
42
135
|
readonly timeoutMs?: number;
|
|
43
136
|
/** Static headers to set on every forwarded request (after hygiene, so they always win). */
|
|
44
137
|
readonly headers?: Readonly<Record<string, string>>;
|
|
138
|
+
/**
|
|
139
|
+
* How to reach the upstream. Omit to use the default: the undici transport on Node when `undici`
|
|
140
|
+
* is installed (the fast path there, resolved lazily so the base package stays dependency-free),
|
|
141
|
+
* `fetch` on every other runtime and when `undici` is absent. See {@link ProxyTransport} - a
|
|
142
|
+
* transport carries security obligations. Pass one explicitly to pin the choice or tune it (e.g.
|
|
143
|
+
* `undiciTransport({ dispatcher })`).
|
|
144
|
+
*/
|
|
145
|
+
readonly transport?: ProxyTransport;
|
|
45
146
|
}
|
|
46
147
|
/** Forward a request (or a nifra context) to the configured upstream. */
|
|
47
148
|
export type ProxyHandler = (input: Request | ProxyContext) => Promise<Response>;
|
|
149
|
+
/** Options for {@link fetchTransport}. */
|
|
150
|
+
export interface FetchTransportOptions {
|
|
151
|
+
/**
|
|
152
|
+
* Milliseconds of silence tolerated *within* the response body before the relayed stream is
|
|
153
|
+
* errored and the upstream read cancelled. `createProxy`'s own `timeoutMs` only covers the wait
|
|
154
|
+
* for response headers - after the status is relayed a `504` is no longer sendable - so this is
|
|
155
|
+
* what protects against a body that starts and then stalls. Default `30_000` (undici's
|
|
156
|
+
* `bodyTimeout` default, so the choice of transport does not change the bound); `0` disables it.
|
|
157
|
+
*/
|
|
158
|
+
readonly bodyTimeoutMs?: number;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Create the portable `fetch`-backed {@link ProxyTransport} - the default off Node, and the fallback
|
|
162
|
+
* everywhere the undici transport cannot be used. `redirect: "manual"` is not a preference: following
|
|
163
|
+
* an upstream redirect would let the upstream choose the proxy's next destination, so it is pinned
|
|
164
|
+
* here and a 3xx is relayed to the caller untouched.
|
|
165
|
+
*
|
|
166
|
+
* Pass it explicitly only to tune {@link FetchTransportOptions.bodyTimeoutMs} or to pin the choice
|
|
167
|
+
* on Node; leaving `transport` unset picks the right one per runtime.
|
|
168
|
+
*/
|
|
169
|
+
export declare function fetchTransport(options?: FetchTransportOptions): ProxyTransport;
|
|
48
170
|
/**
|
|
49
171
|
* Create a proxy handler bound to one upstream origin.
|
|
50
172
|
*
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|