@nodefony/http 10.0.0-beta.4 → 10.0.0-beta.6
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 +24 -0
- package/dist/_virtual/{_@oxc-project_runtime@0.155.0 → _@oxc-project_runtime@0.157.0}/helpers/esm/decorate.js +1 -1
- package/dist/_virtual/{_@oxc-project_runtime@0.155.0 → _@oxc-project_runtime@0.157.0}/helpers/esm/decorateParam.js +1 -1
- package/dist/index.js +29 -25
- package/dist/nodefony/command/proxyGenerateCommand.js +2 -2
- package/dist/nodefony/config/config.js +6 -3
- package/dist/nodefony/interfaces/IRequestTrace.js +1 -0
- package/dist/nodefony/service/ProfilerAdminApi.js +17 -3
- package/dist/nodefony/service/audit-logger.js +3 -17
- package/dist/nodefony/service/error-renderer.js +6 -4
- package/dist/nodefony/service/http-kernel.js +191 -40
- package/dist/nodefony/service/pretty-request-logger.js +11 -10
- package/dist/nodefony/service/request-logger.js +1 -1
- package/dist/nodefony/service/reverse-proxy.js +7 -2
- package/dist/nodefony/service/servers/clientError.js +28 -4
- package/dist/nodefony/service/servers/server-h2c.js +5 -5
- package/dist/nodefony/service/servers/server-http.js +5 -3
- package/dist/nodefony/service/servers/server-https.js +12 -5
- package/dist/nodefony/service/servers/server-static.js +1 -6
- package/dist/nodefony/service/servers/server-websocket-secure.js +6 -10
- package/dist/nodefony/service/servers/server-websocket.js +6 -10
- package/dist/nodefony/service/servers/serverShutdown.js +63 -4
- package/dist/nodefony/service/sessions/sessions-service.js +3 -3
- package/dist/nodefony/service/upload/upload-service.js +2 -2
- package/dist/nodefony/src/assets/prebuiltUi.js +23 -5
- package/dist/nodefony/src/context/Context.js +56 -13
- package/dist/nodefony/src/context/http/ByteStream.js +359 -0
- package/dist/nodefony/src/context/http/HttpContext.js +75 -25
- package/dist/nodefony/src/context/http/Request.js +15 -6
- package/dist/nodefony/src/context/http/Response.js +354 -35
- package/dist/nodefony/src/context/http/SseStream.js +81 -146
- package/dist/nodefony/src/context/http/StreamLifecycle.js +273 -0
- package/dist/nodefony/src/context/http/urlFastPath.js +80 -1
- package/dist/nodefony/src/context/http2/Request.js +1 -1
- package/dist/nodefony/src/context/http2/Response.js +28 -5
- package/dist/nodefony/src/context/requestTrace.js +104 -0
- package/dist/nodefony/src/context/timingMode.js +21 -0
- package/dist/nodefony/src/context/transport.js +20 -1
- package/dist/nodefony/src/context/unsentResponse.js +30 -0
- package/dist/nodefony/src/context/websocket/Response.js +4 -2
- package/dist/nodefony/src/context/websocket/WebsocketContext.js +81 -20
- package/dist/nodefony/src/context/websocket/websocketUrl.js +7 -0
- package/dist/nodefony/src/context/websocket/wsBackpressure.js +15 -15
- package/dist/nodefony/src/context/websocket/wsFrameRate.js +71 -0
- package/dist/nodefony/src/context/websocket/wsLogContent.js +55 -17
- package/dist/nodefony/src/cookies/cookie.js +54 -13
- package/dist/nodefony/src/profiler/FrameProfile.js +22 -0
- package/dist/nodefony/src/profiler/Profiler.js +25 -22
- package/dist/nodefony/src/profiler/aggregate.js +26 -6
- package/dist/nodefony/src/proxy/forward.js +109 -9
- package/dist/nodefony/src/proxy/generateProxyConfig.js +6 -2
- package/dist/nodefony/src/proxy/rules.js +29 -14
- package/dist/nodefony/src/servers/upgradeDispatch.js +62 -4
- package/dist/types/index.d.ts +8 -0
- package/dist/types/nodefony/config/config.d.ts +8 -0
- package/dist/types/nodefony/interfaces/IContext.d.ts +9 -0
- package/dist/types/nodefony/interfaces/IRequestTrace.d.ts +56 -0
- package/dist/types/nodefony/interfaces/IReverseProxy.d.ts +8 -0
- package/dist/types/nodefony/interfaces/IRouting.d.ts +16 -0
- package/dist/types/nodefony/interfaces/ISecurity.d.ts +27 -2
- package/dist/types/nodefony/interfaces/index.d.ts +1 -0
- package/dist/types/nodefony/service/ProfilerAdminApi.d.ts +5 -1
- package/dist/types/nodefony/service/audit-logger.d.ts +3 -11
- package/dist/types/nodefony/service/error-renderer.d.ts +5 -4
- package/dist/types/nodefony/service/http-kernel.d.ts +65 -2
- package/dist/types/nodefony/service/servers/clientError.d.ts +3 -2
- package/dist/types/nodefony/service/servers/serverShutdown.d.ts +5 -3
- package/dist/types/nodefony/src/assets/prebuiltUi.d.ts +9 -4
- package/dist/types/nodefony/src/context/Context.d.ts +25 -1
- package/dist/types/nodefony/src/context/http/ByteStream.d.ts +109 -0
- package/dist/types/nodefony/src/context/http/HttpContext.d.ts +60 -4
- package/dist/types/nodefony/src/context/http/Request.d.ts +8 -1
- package/dist/types/nodefony/src/context/http/Response.d.ts +134 -8
- package/dist/types/nodefony/src/context/http/SseStream.d.ts +32 -7
- package/dist/types/nodefony/src/context/http/StreamLifecycle.d.ts +53 -0
- package/dist/types/nodefony/src/context/http/urlFastPath.d.ts +40 -0
- package/dist/types/nodefony/src/context/http2/Response.d.ts +17 -3
- package/dist/types/nodefony/src/context/requestTrace.d.ts +64 -0
- package/dist/types/nodefony/src/context/timingMode.d.ts +23 -0
- package/dist/types/nodefony/src/context/transport.d.ts +17 -0
- package/dist/types/nodefony/src/context/unsentResponse.d.ts +16 -0
- package/dist/types/nodefony/src/context/websocket/WebsocketContext.d.ts +41 -1
- package/dist/types/nodefony/src/context/websocket/websocketUrl.d.ts +4 -0
- package/dist/types/nodefony/src/context/websocket/wsBackpressure.d.ts +12 -2
- package/dist/types/nodefony/src/context/websocket/wsFrameRate.d.ts +69 -0
- package/dist/types/nodefony/src/context/websocket/wsLogContent.d.ts +16 -8
- package/dist/types/nodefony/src/cookies/cookie.d.ts +20 -2
- package/dist/types/nodefony/src/profiler/FrameProfile.d.ts +15 -0
- package/dist/types/nodefony/src/profiler/Profiler.d.ts +27 -102
- package/dist/types/nodefony/src/profiler/aggregate.d.ts +23 -106
- package/dist/types/nodefony/src/proxy/forward.d.ts +30 -2
- package/dist/types/nodefony/src/proxy/rules.d.ts +20 -0
- package/dist/types/nodefony/src/servers/upgradeDispatch.d.ts +41 -1
- package/docs/byte-stream.md +371 -0
- package/docs/cookies.md +13 -3
- package/docs/diagnostics-channel.md +303 -0
- package/docs/index.md +14 -1
- package/docs/observabilite.md +94 -16
- package/docs/reverse-proxy.md +33 -19
- package/docs/servers.md +26 -15
- package/docs/session.md +3 -3
- package/docs/sse.md +27 -20
- package/package.json +16 -16
|
@@ -1,31 +1,41 @@
|
|
|
1
1
|
import HttpError from "../src/errors/httpError.js";
|
|
2
|
+
import { isUnsupportedTransferCoding } from "../src/proxy/rules.js";
|
|
3
|
+
import { hasConflictingAuthority } from "../src/context/transport.js";
|
|
2
4
|
import { buildTrustProxy } from "../src/context/trustProxy.js";
|
|
3
5
|
import { resolveForwarded } from "../src/context/forwarded.js";
|
|
4
6
|
import { MemoryRateLimitStore } from "../src/rateLimit/MemoryRateLimitStore.js";
|
|
5
7
|
import { WsConnectionCounter } from "../src/rateLimit/WsConnectionCounter.js";
|
|
6
8
|
import { compileDomainPatterns, compileTrustedHosts, isDomainAllowed } from "../src/context/domainMatcher.js";
|
|
9
|
+
import { isProfiledUrl } from "../src/profiler/aggregate.js";
|
|
7
10
|
import { responseEnded } from "../src/context/responseEnded.js";
|
|
8
|
-
import {
|
|
11
|
+
import { unsentResponseWarning } from "../src/context/unsentResponse.js";
|
|
12
|
+
import { closeRequestTrace, hasRequestTraceSubscribers, openRequestTrace, runTraced } from "../src/context/requestTrace.js";
|
|
9
13
|
import WebsocketContext, { toHandshakeStatus } from "../src/context/websocket/WebsocketContext.js";
|
|
10
14
|
import { trackPong } from "./servers/wsHeartbeat.js";
|
|
15
|
+
import { parseWebsocketUrl } from "../src/context/websocket/websocketUrl.js";
|
|
11
16
|
import HttpContext from "../src/context/http/HttpContext.js";
|
|
12
17
|
import DefaultErrorRenderer from "./error-renderer.js";
|
|
13
|
-
import JsonAuditLogger from "./audit-logger.js";
|
|
14
18
|
import DefaultRequestLogger from "./request-logger.js";
|
|
15
19
|
import PrettyRequestLogger from "./pretty-request-logger.js";
|
|
20
|
+
import JsonAuditLogger from "./audit-logger.js";
|
|
16
21
|
import { resolveTraceparent } from "./trace.js";
|
|
17
22
|
import { asServersConfig, configuredServerPort } from "../src/servers/kernelServers.js";
|
|
18
|
-
import __decorate from "../../_virtual/_@oxc-project_runtime@0.
|
|
19
|
-
import { GcScheduler, IDENTITY_CHANGED_EVENT, RequestContext, RevocationWatch, Service, finallyMaybe, injectable, isPromise, logColor, nodefonyError, servedUrl, thenMaybe, writeRuntimeState } from "nodefony";
|
|
23
|
+
import __decorate from "../../_virtual/_@oxc-project_runtime@0.157.0/helpers/esm/decorate.js";
|
|
24
|
+
import { BootConfigurationError, GcScheduler, IDENTITY_CHANGED_EVENT, RequestContext, RevocationWatch, Service, finallyMaybe, injectable, isPromise, logColor, nodefonyError, servedUrl, thenMaybe, writeRuntimeState } from "nodefony";
|
|
20
25
|
import cluster from "node:cluster";
|
|
21
26
|
import http from "node:http";
|
|
22
27
|
// nodefony/service/http-kernel.ts
|
|
23
|
-
const HEALTH_HEADERS =
|
|
24
|
-
"content-type"
|
|
25
|
-
"
|
|
26
|
-
|
|
28
|
+
const HEALTH_HEADERS = [
|
|
29
|
+
"content-type",
|
|
30
|
+
"application/json",
|
|
31
|
+
"cache-control",
|
|
32
|
+
"no-store"
|
|
33
|
+
];
|
|
27
34
|
const HEALTH_BODY_OK = "{\"status\":\"ok\"}";
|
|
28
35
|
const HEALTH_BODY_UNREADY = "{\"status\":\"unready\"}";
|
|
36
|
+
/** Corps du 429 du limiteur de débit — figé, rien à sérialiser par refus. */
|
|
37
|
+
const TOO_MANY_REQUESTS_BODY = "{\"code\":429,\"message\":\"Too Many Requests\"}";
|
|
38
|
+
const TOO_MANY_REQUESTS_LENGTH = Buffer.byteLength(TOO_MANY_REQUESTS_BODY);
|
|
29
39
|
const perfProbe = process.env.NF_PERF_PROBE === "1" ? {
|
|
30
40
|
count: 0,
|
|
31
41
|
enterScopeNs: 0,
|
|
@@ -104,12 +114,28 @@ let HttpKernel = class HttpKernel extends Service {
|
|
|
104
114
|
secContentTypeOptions = null;
|
|
105
115
|
secFrameOptions = null;
|
|
106
116
|
secHsts = null;
|
|
117
|
+
/**
|
|
118
|
+
* En-têtes du transport, tableau plat `[nom, valeur, …]` aux noms en
|
|
119
|
+
* minuscules, par famille de serveur (HSTS sur TLS seulement). Figés et
|
|
120
|
+
* VALIDÉS au calcul : chaque réponse les recopie sans les revalider.
|
|
121
|
+
*/
|
|
122
|
+
#transportHeaders = {
|
|
123
|
+
http: [],
|
|
124
|
+
https: []
|
|
125
|
+
};
|
|
126
|
+
/** Réponse d'une sonde de santé : le transport, puis type et cache. */
|
|
127
|
+
#healthHeaders = {
|
|
128
|
+
http: [],
|
|
129
|
+
https: []
|
|
130
|
+
};
|
|
107
131
|
sessionService;
|
|
108
132
|
router;
|
|
109
133
|
firewall;
|
|
110
134
|
errorRenderer = new DefaultErrorRenderer();
|
|
111
135
|
requestLogger = new DefaultRequestLogger();
|
|
112
136
|
profiler = null;
|
|
137
|
+
/** `timing.tooling` : profiler aussi l'outillage (lu une fois, à onReady). */
|
|
138
|
+
profileTooling = false;
|
|
113
139
|
_trustProxyChecker = null;
|
|
114
140
|
_wsOriginPolicy = Object.create(null);
|
|
115
141
|
rateLimiter = null;
|
|
@@ -152,12 +178,49 @@ let HttpKernel = class HttpKernel extends Service {
|
|
|
152
178
|
if (hsts.preload) v += "; preload";
|
|
153
179
|
this.secHsts = v;
|
|
154
180
|
} else this.secHsts = null;
|
|
181
|
+
const headerServer = this.options.headerServer;
|
|
182
|
+
const http1 = [];
|
|
183
|
+
if (headerServer != null && headerServer !== "") http1.push("server", headerServer);
|
|
184
|
+
if (this.secContentTypeOptions !== null) http1.push("x-content-type-options", this.secContentTypeOptions);
|
|
185
|
+
if (this.secFrameOptions !== null) http1.push("x-frame-options", this.secFrameOptions);
|
|
186
|
+
const tls = this.secHsts === null ? http1 : [
|
|
187
|
+
...http1,
|
|
188
|
+
"strict-transport-security",
|
|
189
|
+
this.secHsts
|
|
190
|
+
];
|
|
191
|
+
for (let i = 0; i + 1 < tls.length; i += 2) {
|
|
192
|
+
const name = tls[i] ?? "";
|
|
193
|
+
try {
|
|
194
|
+
http.validateHeaderValue(name, tls[i + 1] ?? "");
|
|
195
|
+
} catch (error) {
|
|
196
|
+
throw new BootConfigurationError(`[@nodefony/http] en-tête « ${name} » refusé : ${error instanceof Error ? error.message : String(error)}`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
this.#transportHeaders = {
|
|
200
|
+
http: http1,
|
|
201
|
+
https: tls
|
|
202
|
+
};
|
|
203
|
+
this.#healthHeaders = {
|
|
204
|
+
http: [...http1, ...HEALTH_HEADERS],
|
|
205
|
+
https: [...tls, ...HEALTH_HEADERS]
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* En-têtes du transport d'une réponse, figés au démarrage — ce que
|
|
210
|
+
* `HttpResponse` place en tête de sa réserve.
|
|
211
|
+
*
|
|
212
|
+
* @param type - famille du serveur qui a reçu la requête.
|
|
213
|
+
* @returns la liste plate `[nom, valeur, …]`, noms en minuscules.
|
|
214
|
+
*/
|
|
215
|
+
transportHeaders(type) {
|
|
216
|
+
return type === "https" ? this.#transportHeaders.https : this.#transportHeaders.http;
|
|
155
217
|
}
|
|
156
218
|
/**
|
|
157
219
|
* Ce que ce kernel ÉMET réellement comme en-têtes de sécurité transport.
|
|
158
220
|
*
|
|
159
221
|
* Ces trois en-têtes (HSTS, X-Frame-Options, X-Content-Type-Options) sont posés
|
|
160
|
-
*
|
|
222
|
+
* par `@nodefony/http` sur TOUTE réponse (en tête de la réserve de `HttpResponse`),
|
|
223
|
+
* statiques et pages d'erreur comprises
|
|
161
224
|
* — pas par `@nodefony/security`, dont les clés homonymes sont inertes. Toute
|
|
162
225
|
* console d'administration doit donc lire CE point, sinon elle affiche une
|
|
163
226
|
* intention de configuration au lieu de l'état appliqué.
|
|
@@ -184,6 +247,7 @@ let HttpKernel = class HttpKernel extends Service {
|
|
|
184
247
|
*/
|
|
185
248
|
onConfigChanged() {
|
|
186
249
|
this.computeSecurityHeaderCaches();
|
|
250
|
+
this.firewall?.onTransportHeadersChanged?.();
|
|
187
251
|
this._trustProxyChecker = null;
|
|
188
252
|
this._wsOriginPolicy = Object.create(null);
|
|
189
253
|
this.trustedHosts = this.options.trustedHosts;
|
|
@@ -207,6 +271,36 @@ let HttpKernel = class HttpKernel extends Service {
|
|
|
207
271
|
get rateLimitStore() {
|
|
208
272
|
return this.rateLimiter;
|
|
209
273
|
}
|
|
274
|
+
/**
|
|
275
|
+
* Cette requête — ou cette frame du pont — se profile-t-elle ? Jamais sans
|
|
276
|
+
* profileur (production). L'outillage (console, profileur, journaux, Vite :
|
|
277
|
+
* `isProfiledUrl`, sur la règle `isConsolePath` du cœur) seulement sous
|
|
278
|
+
* `timing.tooling` — on ne
|
|
279
|
+
* mesure pas la façon dont on mesure : ni tampon SQL, ni instantané, ni place
|
|
280
|
+
* dans les anneaux du profileur.
|
|
281
|
+
*
|
|
282
|
+
* @param url - URL ou chemin de la requête
|
|
283
|
+
* @returns `true` quand le profil doit être pris
|
|
284
|
+
*/
|
|
285
|
+
shouldProfile(url) {
|
|
286
|
+
return this.profiler !== null && isProfiledUrl(url, this.profileTooling);
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* En développement, signale une action qui a rendu une valeur que le
|
|
290
|
+
* Resolver ne sait pas envoyer (`null`, `undefined`, instance de classe)
|
|
291
|
+
* et n'a rien envoyé elle-même : la requête attend son délai d'expiration.
|
|
292
|
+
* Le message nomme l'action, ce qu'elle a rendu et le geste qui convient
|
|
293
|
+
* (`unsentResponseWarning`).
|
|
294
|
+
*
|
|
295
|
+
* Appelé au timeout, AVANT que le 504/408 parte — après, `sended` est vrai et
|
|
296
|
+
* plus rien ne se signalerait — et au démontage, pour un client parti avant.
|
|
297
|
+
* Gratuit en production : rien n'est lu au-delà de `waitAsync`.
|
|
298
|
+
*
|
|
299
|
+
* @param context - le contexte HTTP de la requête
|
|
300
|
+
*/
|
|
301
|
+
warnUnsentResponse(context) {
|
|
302
|
+
if (context.waitAsync && !context.sended && this.kernel?.environment === "development") this.log(unsentResponseWarning(context.resolver, context.url), "WARNING");
|
|
303
|
+
}
|
|
210
304
|
configureRateLimit() {
|
|
211
305
|
this.rateLimitGc?.stop();
|
|
212
306
|
this.rateLimitGc = null;
|
|
@@ -261,9 +355,9 @@ let HttpKernel = class HttpKernel extends Service {
|
|
|
261
355
|
* @param response - réponse serveur brute (HTTP/1 ou H2 compat)
|
|
262
356
|
* @param ok - `true` → 200, `false` → 503 (readiness pendant boot/shutdown)
|
|
263
357
|
*/
|
|
264
|
-
#respondHealth(response, ok) {
|
|
358
|
+
#respondHealth(response, ok, type) {
|
|
265
359
|
const res = response;
|
|
266
|
-
res.writeHead(ok ? 200 : 503,
|
|
360
|
+
res.writeHead(ok ? 200 : 503, type === "https" ? this.#healthHeaders.https : this.#healthHeaders.http);
|
|
267
361
|
res.end(ok ? HEALTH_BODY_OK : HEALTH_BODY_UNREADY);
|
|
268
362
|
}
|
|
269
363
|
async init() {
|
|
@@ -277,6 +371,8 @@ let HttpKernel = class HttpKernel extends Service {
|
|
|
277
371
|
this.regAlias = this.compileAlias();
|
|
278
372
|
this.sessionService = this.get("sessions");
|
|
279
373
|
this.profiler = this.get("profiler");
|
|
374
|
+
const timing = this.kernel?.options.timing;
|
|
375
|
+
this.profileTooling = typeof timing === "object" && timing !== null && "tooling" in timing && timing.tooling === true;
|
|
280
376
|
this.configureRateLimit();
|
|
281
377
|
this.configureWsConnectionLimit();
|
|
282
378
|
this.configureHealth();
|
|
@@ -571,7 +667,7 @@ let HttpKernel = class HttpKernel extends Service {
|
|
|
571
667
|
if (!context.response.isHeaderSent()) {
|
|
572
668
|
if (context.isRedirect) {
|
|
573
669
|
context.isRedirect = false;
|
|
574
|
-
context.response.
|
|
670
|
+
context.response.removeHeader("Location");
|
|
575
671
|
}
|
|
576
672
|
try {
|
|
577
673
|
await context.render(result.body);
|
|
@@ -641,19 +737,14 @@ let HttpKernel = class HttpKernel extends Service {
|
|
|
641
737
|
* pipeline.
|
|
642
738
|
*/
|
|
643
739
|
dispatchHttpRequest(request, response, type) {
|
|
644
|
-
const headerServer = this.options.headerServer;
|
|
645
|
-
if (headerServer != null && headerServer !== "") response.setHeader("Server", headerServer);
|
|
646
|
-
if (this.secContentTypeOptions !== null) response.setHeader("X-Content-Type-Options", this.secContentTypeOptions);
|
|
647
|
-
if (this.secFrameOptions !== null) response.setHeader("X-Frame-Options", this.secFrameOptions);
|
|
648
|
-
if (this.secHsts !== null && type === "https") response.setHeader("Strict-Transport-Security", this.secHsts);
|
|
649
740
|
if (this.#healthPaths !== null) {
|
|
650
741
|
const url = request.url;
|
|
651
742
|
if (url === this.#healthPaths.liveness) {
|
|
652
|
-
this.#respondHealth(response, true);
|
|
743
|
+
this.#respondHealth(response, true, type);
|
|
653
744
|
return;
|
|
654
745
|
}
|
|
655
746
|
if (url === this.#healthPaths.readiness) {
|
|
656
|
-
this.#respondHealth(response, !this.#terminating && this.kernel?.servable === true);
|
|
747
|
+
this.#respondHealth(response, !this.#terminating && this.kernel?.servable === true, type);
|
|
657
748
|
return;
|
|
658
749
|
}
|
|
659
750
|
}
|
|
@@ -667,13 +758,19 @@ let HttpKernel = class HttpKernel extends Service {
|
|
|
667
758
|
if (verdict.limited) {
|
|
668
759
|
response.setHeader("Retry-After", verdict.retryAfterS);
|
|
669
760
|
const res = response;
|
|
670
|
-
|
|
671
|
-
|
|
761
|
+
response.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
762
|
+
response.setHeader("Content-Length", TOO_MANY_REQUESTS_LENGTH);
|
|
763
|
+
res.writeHead(429, type === "https" ? this.#transportHeaders.https : this.#transportHeaders.http);
|
|
764
|
+
res.end(TOO_MANY_REQUESTS_BODY);
|
|
672
765
|
return;
|
|
673
766
|
}
|
|
674
767
|
}
|
|
675
768
|
}
|
|
676
|
-
if (this.listenerCount("onServerRequest"))
|
|
769
|
+
if (this.listenerCount("onServerRequest")) {
|
|
770
|
+
const transport = type === "https" ? this.#transportHeaders.https : this.#transportHeaders.http;
|
|
771
|
+
for (let i = 0; i + 1 < transport.length; i += 2) response.setHeader(transport[i] ?? "", transport[i + 1] ?? "");
|
|
772
|
+
return this.fireAsync("onServerRequest", request, response, type).then(() => this.handle(request, response, type));
|
|
773
|
+
}
|
|
677
774
|
return this.handle(request, response, type);
|
|
678
775
|
}
|
|
679
776
|
async initServers() {
|
|
@@ -816,10 +913,12 @@ let HttpKernel = class HttpKernel extends Service {
|
|
|
816
913
|
teardownHttp(context, scope) {
|
|
817
914
|
if (context.finished) return void 0;
|
|
818
915
|
try {
|
|
819
|
-
|
|
916
|
+
context.stopClock();
|
|
917
|
+
if (context.waitAsync) this.warnUnsentResponse(context);
|
|
820
918
|
context.logRequest();
|
|
919
|
+
if (context.requestTrace !== null) closeRequestTrace(context, context.response.statusCode, context.error);
|
|
821
920
|
context.logPhasesVerbose();
|
|
822
|
-
this.profiler?.collect(context);
|
|
921
|
+
if (this.shouldProfile(context.url)) this.profiler?.collect(context);
|
|
823
922
|
const afterResponse = context._runAfterResponse();
|
|
824
923
|
if (afterResponse !== void 0 || context.listenerCount("onFinish")) return this.finishTeardownHttp(context, scope, afterResponse);
|
|
825
924
|
this.releaseHttpContext(context, scope);
|
|
@@ -903,20 +1002,34 @@ let HttpKernel = class HttpKernel extends Service {
|
|
|
903
1002
|
try {
|
|
904
1003
|
context = this.createHttpContext(scope, request, response, type);
|
|
905
1004
|
const created = context;
|
|
906
|
-
const served =
|
|
1005
|
+
const served = hasRequestTraceSubscribers() ? runTraced(openRequestTrace(created, "http"), this.dispatchHttpContext, this, created, scope, request, response) : this.dispatchHttpContext(created, scope, request, response);
|
|
907
1006
|
if (!isPromise(served)) return served;
|
|
908
1007
|
return served.catch((e) => this.failHttp(e, created, scope));
|
|
909
1008
|
} catch (e) {
|
|
910
|
-
return this.failHttp(e, context, scope);
|
|
1009
|
+
return this.failHttp(e, context, scope, response);
|
|
911
1010
|
}
|
|
912
1011
|
}
|
|
913
1012
|
/**
|
|
1013
|
+
* Sert un contexte HTTP fraîchement créé : le crochet `onCreateContext`
|
|
1014
|
+
* s'il a des écouteurs, puis le pipeline.
|
|
1015
|
+
*
|
|
1016
|
+
* Hot path : `fireAsync` est une fonction async → 1 Promise + 1 microtask
|
|
1017
|
+
* MÊME à 0 listener. En prod sans @nodefony/security ces seams
|
|
1018
|
+
* (onCreateContext/beforeResolve/afterAuth) n'ont aucun listener → garde
|
|
1019
|
+
* `listenerCount` (O(1), 0 alloc) pour ne RIEN planifier.
|
|
1020
|
+
*
|
|
1021
|
+
* @returns le contexte servi, ou sa promesse
|
|
1022
|
+
*/
|
|
1023
|
+
dispatchHttpContext(context, scope, request, response) {
|
|
1024
|
+
return this.listenerCount("onCreateContext") ? this.fireAsync("onCreateContext", context).then(() => this.serveHttpContext(context, scope, request, response)) : this.serveHttpContext(context, scope, request, response);
|
|
1025
|
+
}
|
|
1026
|
+
/**
|
|
914
1027
|
* Traceparent, tampon du profileur, puis le pipeline dans la bulle ALS de la
|
|
915
1028
|
* requête (`RequestContext.run`).
|
|
916
1029
|
*/
|
|
917
1030
|
serveHttpContext(context, scope, request, response) {
|
|
918
1031
|
context.traceparent = resolveTraceparent(request.headers.traceparent);
|
|
919
|
-
const profilerQueries = this.
|
|
1032
|
+
const profilerQueries = this.shouldProfile(context.url) ? [] : null;
|
|
920
1033
|
context.profilerQueries = profilerQueries;
|
|
921
1034
|
context.profiling = profilerQueries !== null;
|
|
922
1035
|
const store = {
|
|
@@ -933,9 +1046,19 @@ let HttpKernel = class HttpKernel extends Service {
|
|
|
933
1046
|
/**
|
|
934
1047
|
* Chemin d'erreur de {@link handleHttp} : libère le scope d'un contexte qui
|
|
935
1048
|
* n'a pas pu se construire, puis rend l'erreur au client (`onError`).
|
|
1049
|
+
*
|
|
1050
|
+
* Sans contexte, `onError` n'a rien sur quoi écrire : la réponse BRUTE
|
|
1051
|
+
* reçoit alors un 500 nu. Sinon HTTP/1.1 coupait la connexion sans un octet
|
|
1052
|
+
* et HTTP/2 laissait le flux pendu.
|
|
936
1053
|
*/
|
|
937
|
-
failHttp(e, context, scope) {
|
|
938
|
-
if (context === null)
|
|
1054
|
+
failHttp(e, context, scope, response) {
|
|
1055
|
+
if (context === null) {
|
|
1056
|
+
this.container?.leaveScope(scope);
|
|
1057
|
+
if (response && !response.headersSent) {
|
|
1058
|
+
response.statusCode = 500;
|
|
1059
|
+
response.end();
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
939
1062
|
return this.onError(e, context);
|
|
940
1063
|
}
|
|
941
1064
|
/**
|
|
@@ -967,16 +1090,24 @@ let HttpKernel = class HttpKernel extends Service {
|
|
|
967
1090
|
*/
|
|
968
1091
|
routeHttpRequest(httpContext, request, response) {
|
|
969
1092
|
if (this.reverseProxy !== null && this.reverseProxy.mounts !== null && (httpContext.validDomain || !this.domainCheck)) {
|
|
1093
|
+
if (this.reverseProxy.match(request.url) !== void 0) httpContext.response.commitHeaders();
|
|
970
1094
|
const relayed = this.reverseProxy.forward(request, response, httpContext.scheme);
|
|
971
|
-
if (relayed !== void 0)
|
|
1095
|
+
if (relayed !== void 0) {
|
|
1096
|
+
httpContext.relayAccessLog = this.reverseProxy.match(request.url)?.accessLog ?? "all";
|
|
1097
|
+
return relayed.then(() => httpContext);
|
|
1098
|
+
}
|
|
972
1099
|
}
|
|
973
1100
|
httpContext.phaseStart("resolve");
|
|
974
1101
|
httpContext.resolver = this.router ? this.router.resolve(httpContext) : null;
|
|
975
1102
|
httpContext.phaseEnd("resolve");
|
|
976
|
-
this.firewall
|
|
1103
|
+
if (this.firewall) httpContext.response.headerPolicy = this.firewall;
|
|
977
1104
|
if (this.serverStatic && httpContext.resolver?.resolve !== true && !httpContext.resolver?.exception && (asServersConfig(this.kernel?.options.servers)?.statics || this.kernel?.options.statics || this.serverStatic.hasMounts())) {
|
|
978
|
-
response.removeHeader("Content-Type");
|
|
979
|
-
|
|
1105
|
+
httpContext.response.removeHeader("Content-Type");
|
|
1106
|
+
httpContext.response.commitBeforeNativeWrite();
|
|
1107
|
+
return this.serverStatic.handle(request, response).catch(() => void 0).then(() => {
|
|
1108
|
+
httpContext.response.disarmNativeWrite();
|
|
1109
|
+
return this.serveHttpRequest(httpContext);
|
|
1110
|
+
});
|
|
980
1111
|
}
|
|
981
1112
|
return this.serveHttpRequest(httpContext);
|
|
982
1113
|
}
|
|
@@ -985,6 +1116,9 @@ let HttpKernel = class HttpKernel extends Service {
|
|
|
985
1116
|
* ({@link onRequestEnd}) et l'action. Chacun n'est attendu que s'il attend.
|
|
986
1117
|
*/
|
|
987
1118
|
serveHttpRequest(httpContext) {
|
|
1119
|
+
const headers = httpContext.request.headers;
|
|
1120
|
+
if (isUnsupportedTransferCoding(headers["transfer-encoding"])) return Promise.reject(new HttpError("Transfer-Encoding not implemented", 501, httpContext));
|
|
1121
|
+
if (hasConflictingAuthority(headers)) return Promise.reject(new HttpError("Host differs from :authority", 400, httpContext));
|
|
988
1122
|
const streamBody = httpContext.resolver?.resolve === true && httpContext.resolver.route?.bodyStream === true;
|
|
989
1123
|
httpContext.phaseStart("parse");
|
|
990
1124
|
const parsed = finallyMaybe(() => streamBody ? void 0 : httpContext.request.initialize(), () => httpContext.phaseEnd("parse"));
|
|
@@ -1042,8 +1176,8 @@ let HttpKernel = class HttpKernel extends Service {
|
|
|
1042
1176
|
if (this.listenerCount("afterAuth")) await this.fireAsync("afterAuth", context);
|
|
1043
1177
|
} catch (authError) {
|
|
1044
1178
|
if (context.resolver?.exception) {
|
|
1045
|
-
context.response.
|
|
1046
|
-
context.response.
|
|
1179
|
+
context.response.removeHeader("Upgrade");
|
|
1180
|
+
context.response.removeHeader("Connection");
|
|
1047
1181
|
}
|
|
1048
1182
|
await this.fireAsync("onAuthFailure", context, authError).catch((e) => this.log(e, "ERROR", "onAuthFailure"));
|
|
1049
1183
|
throw authError;
|
|
@@ -1146,7 +1280,7 @@ let HttpKernel = class HttpKernel extends Service {
|
|
|
1146
1280
|
scope
|
|
1147
1281
|
};
|
|
1148
1282
|
context?.holdRequestStore(store);
|
|
1149
|
-
|
|
1283
|
+
const pipeline = async () => {
|
|
1150
1284
|
await this.onConnect(context, error);
|
|
1151
1285
|
if (this.firewall && context?.secure) {
|
|
1152
1286
|
context.phaseStart("firewall");
|
|
@@ -1168,6 +1302,7 @@ let HttpKernel = class HttpKernel extends Service {
|
|
|
1168
1302
|
const accepted = admission.accept();
|
|
1169
1303
|
const ws = accepted instanceof Promise ? await accepted : accepted;
|
|
1170
1304
|
if (ws === null) {
|
|
1305
|
+
if (context.requestTrace !== null) closeRequestTrace(context, null, null);
|
|
1171
1306
|
this.releaseOrphanWsScope(scope, context);
|
|
1172
1307
|
return;
|
|
1173
1308
|
}
|
|
@@ -1180,20 +1315,36 @@ let HttpKernel = class HttpKernel extends Service {
|
|
|
1180
1315
|
} finally {
|
|
1181
1316
|
context.phaseEnd("action");
|
|
1182
1317
|
}
|
|
1183
|
-
}
|
|
1318
|
+
};
|
|
1319
|
+
return await (context !== null && hasRequestTraceSubscribers() ? runTraced(openRequestTrace(context, "websocket"), RequestContext.run, RequestContext, store, pipeline) : RequestContext.run(store, pipeline));
|
|
1184
1320
|
} catch (e) {
|
|
1185
1321
|
try {
|
|
1186
1322
|
await this.onError(e, context);
|
|
1187
1323
|
} catch (errorHandlingError) {
|
|
1188
1324
|
admission.refuse(500);
|
|
1325
|
+
this.closeFailedWebsocketTrace(context, e);
|
|
1189
1326
|
this.releaseOrphanWsScope(scope, context);
|
|
1190
1327
|
throw errorHandlingError;
|
|
1191
1328
|
}
|
|
1192
1329
|
admission.refuse(500);
|
|
1330
|
+
this.closeFailedWebsocketTrace(context, e);
|
|
1193
1331
|
this.releaseOrphanWsScope(scope, context);
|
|
1194
1332
|
throw e;
|
|
1195
1333
|
}
|
|
1196
1334
|
}
|
|
1335
|
+
/**
|
|
1336
|
+
* Fin du canal `nodefony.http.request` pour un handshake interrompu par une
|
|
1337
|
+
* exception — sans effet si un refus l'a déjà publiée (`refuseHandshake`).
|
|
1338
|
+
* Le statut est un fait, pas une déduction : une socket rattachée dit que
|
|
1339
|
+
* le `101` est parti ; sinon c'est le `500` du filet qui a été rendu.
|
|
1340
|
+
*
|
|
1341
|
+
* @param context - le contexte du handshake, `null` s'il n'a pu être construit
|
|
1342
|
+
* @param error - l'exception qui a interrompu le pipeline
|
|
1343
|
+
*/
|
|
1344
|
+
closeFailedWebsocketTrace(context, error) {
|
|
1345
|
+
if (!context?.requestTrace) return;
|
|
1346
|
+
closeRequestTrace(context, context.connection !== null ? 101 : 500, error);
|
|
1347
|
+
}
|
|
1197
1348
|
releaseOrphanWsScope(scope, context) {
|
|
1198
1349
|
if (!context) {
|
|
1199
1350
|
this.container?.leaveScope(scope);
|
|
@@ -1351,12 +1502,12 @@ let HttpKernel = class HttpKernel extends Service {
|
|
|
1351
1502
|
return true;
|
|
1352
1503
|
}
|
|
1353
1504
|
/**
|
|
1354
|
-
* Inscrit un flux
|
|
1505
|
+
* Inscrit un flux (événements ou octets) à identité révocable : il sera fermé dans la fenêtre de
|
|
1355
1506
|
* re-validation qui suit la mort de son identité (déconnexion, session
|
|
1356
1507
|
* révoquée, jeton expiré ou annulé) — le même registre et la même règle que
|
|
1357
1508
|
* les sockets WebSocket.
|
|
1358
1509
|
*
|
|
1359
|
-
* @internal — appelé par le flux
|
|
1510
|
+
* @internal — appelé par le cycle de vie du flux (`StreamLifecycle`).
|
|
1360
1511
|
*/
|
|
1361
1512
|
watchStream(stream) {
|
|
1362
1513
|
(this.#streams ??= new RevocationWatch({
|
|
@@ -1370,7 +1521,7 @@ let HttpKernel = class HttpKernel extends Service {
|
|
|
1370
1521
|
this.#streams?.unregister(stream);
|
|
1371
1522
|
}
|
|
1372
1523
|
/**
|
|
1373
|
-
* Re-valide tous les flux
|
|
1524
|
+
* Re-valide tous les flux en cours et ferme ceux dont l'identité est morte.
|
|
1374
1525
|
* Publique pour qu'un banc la déclenche sans attendre le tick.
|
|
1375
1526
|
*/
|
|
1376
1527
|
revalidateStreams() {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { severityFromStatus, statusOf } from "./audit-logger.js";
|
|
1
2
|
import { handshakeRefusalStatus } from "../src/context/websocket/WebsocketContext.js";
|
|
2
|
-
import {
|
|
3
|
-
import { logColor } from "nodefony";
|
|
3
|
+
import { accessStatusFamily, logColor } from "nodefony";
|
|
4
4
|
// nodefony/service/pretty-request-logger.ts
|
|
5
5
|
/**
|
|
6
6
|
* Pretty single-line logger for dev (P3.2) — the biggest gain for humans.
|
|
@@ -24,9 +24,9 @@ var PrettyRequestLogger = class {
|
|
|
24
24
|
renderHttp(context, error) {
|
|
25
25
|
const ctx = context;
|
|
26
26
|
const err = error ?? ctx.error ?? null;
|
|
27
|
-
const status = ctx
|
|
27
|
+
const status = statusOf(ctx) ?? (err ? 500 : null);
|
|
28
28
|
const method = ctx.method ?? "?";
|
|
29
|
-
const duration =
|
|
29
|
+
const duration = ctx.stopClock();
|
|
30
30
|
return {
|
|
31
31
|
text: `${logColor.cyan(method.padEnd(4))} ${colorizeStatus(status)} ${ctx.url} ${logColor.blackBright(formatDuration(duration))} ${logColor.blackBright(ctx.remoteAddress ?? "-")} ` + logColor.magenta(`[${shortId(ctx.requestId)}]`) + (err ? ` ${logColor.red(err.message)}` : ""),
|
|
32
32
|
severity: severityFromStatus(status),
|
|
@@ -36,8 +36,8 @@ var PrettyRequestLogger = class {
|
|
|
36
36
|
renderWebsocket(context, error, acceptedProtocol) {
|
|
37
37
|
const ctx = context;
|
|
38
38
|
const refused = error ? handshakeRefusalStatus(context, error) : null;
|
|
39
|
-
const status = refused ?? ctx
|
|
40
|
-
const duration =
|
|
39
|
+
const status = refused ?? statusOf(ctx) ?? (error ? 500 : null);
|
|
40
|
+
const duration = ctx.stopClock();
|
|
41
41
|
const proto = acceptedProtocol ? `[${acceptedProtocol}]` : "";
|
|
42
42
|
return {
|
|
43
43
|
text: `${logColor.cyan("WS ")} ${colorizeStatus(status)} ${ctx.url} ${logColor.blackBright(proto)} ${logColor.blackBright(formatDuration(duration))} ${logColor.blackBright(ctx.remoteAddress ?? "-")} ` + logColor.magenta(`[${shortId(ctx.requestId)}]`) + (error ? ` ${logColor.red(error.message)}` : ""),
|
|
@@ -47,11 +47,12 @@ var PrettyRequestLogger = class {
|
|
|
47
47
|
}
|
|
48
48
|
};
|
|
49
49
|
function colorizeStatus(status) {
|
|
50
|
-
|
|
50
|
+
const family = accessStatusFamily(status);
|
|
51
|
+
if (status === null || family === null) return logColor.blackBright("---");
|
|
51
52
|
const s = String(status);
|
|
52
|
-
if (
|
|
53
|
-
if (
|
|
54
|
-
if (
|
|
53
|
+
if (family >= 5) return logColor.red(s);
|
|
54
|
+
if (family === 4) return logColor.yellowBold(s);
|
|
55
|
+
if (family === 3) return logColor.yellow(s);
|
|
55
56
|
return logColor.green(s);
|
|
56
57
|
}
|
|
57
58
|
function formatDuration(ms) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { handshakeRefusalStatus } from "../src/context/websocket/WebsocketContext.js";
|
|
2
1
|
import { severityFromStatus } from "./audit-logger.js";
|
|
2
|
+
import { handshakeRefusalStatus } from "../src/context/websocket/WebsocketContext.js";
|
|
3
3
|
import { logColor } from "nodefony";
|
|
4
4
|
// nodefony/service/request-logger.ts
|
|
5
5
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { forwardRequest, forwardUpgrade, refuseMalformedHandshake, refuseUpgrade } from "../src/proxy/forward.js";
|
|
1
2
|
import { isAmbiguousPath, normalizePrefix, parseProxyOrigin, proxyMountProblems, websocketHandshakeProblem } from "../src/proxy/rules.js";
|
|
2
3
|
import { requestAuthority } from "../src/context/transport.js";
|
|
3
|
-
import { forwardRequest, forwardUpgrade, refuseUpgrade } from "../src/proxy/forward.js";
|
|
4
4
|
import { Service } from "nodefony";
|
|
5
5
|
import http from "node:http";
|
|
6
6
|
import { randomBytes } from "node:crypto";
|
|
@@ -275,10 +275,15 @@ var ReverseProxy = class extends Service {
|
|
|
275
275
|
refuseUpgrade(socket, 421, "Misdirected Request");
|
|
276
276
|
return true;
|
|
277
277
|
}
|
|
278
|
-
if (hostname === null || isAmbiguousPath(req.url ?? "/")
|
|
278
|
+
if (hostname === null || isAmbiguousPath(req.url ?? "/")) {
|
|
279
279
|
refuseUpgrade(socket, 400, "Bad Request");
|
|
280
280
|
return true;
|
|
281
281
|
}
|
|
282
|
+
const problem = websocketHandshakeProblem(req.method, req.headers);
|
|
283
|
+
if (problem !== null) {
|
|
284
|
+
refuseMalformedHandshake(socket, problem);
|
|
285
|
+
return true;
|
|
286
|
+
}
|
|
282
287
|
const secure = req.socket instanceof TLSSocket;
|
|
283
288
|
if (rules !== void 0 && !rules.isWebsocketOriginAllowed(req.headers.origin, hostname, secure)) {
|
|
284
289
|
refuseUpgrade(socket, 403, "Forbidden");
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { isPeerGoneError } from "../../src/context/websocket/Response.js";
|
|
2
2
|
// nodefony/service/servers/clientError.ts
|
|
3
3
|
/**
|
|
4
|
-
* Gère l'event `clientError` d'un `http.Server` / `https.Server
|
|
4
|
+
* Gère l'event `clientError` d'un `http.Server` / `https.Server`, et des connexions
|
|
5
|
+
* HTTP/1.1 d'un serveur HTTP/2 à `allowHTTP1`.
|
|
5
6
|
*
|
|
6
7
|
* ⚠️ Doc Node (`http.Server` event `'clientError'`) : dès qu'un listener
|
|
7
8
|
* `clientError` est attaché, Node **cesse de fermer le socket automatiquement**
|
|
@@ -13,9 +14,32 @@ import { isPeerGoneError } from "../../src/context/websocket/Response.js";
|
|
|
13
14
|
* @param socket - flux client brut (l'event `clientError` fournit un `Duplex`).
|
|
14
15
|
*/
|
|
15
16
|
function handleClientError(error, socket) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
const code = "code" in error ? error.code : void 0;
|
|
18
|
+
if (code === "ECONNRESET" || !socket.writable) return;
|
|
19
|
+
const statusLine = code === "HPE_HEADER_OVERFLOW" ? overflowStatusLine(error) : "400 Bad Request";
|
|
20
|
+
socket.end(`HTTP/1.1 ${statusLine}\r\nConnection: close\r\nContent-Length: 0\r\n\r\n`);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Le statut d'un `HPE_HEADER_OVERFLOW` : Node lève la même erreur pour une
|
|
24
|
+
* cible trop longue et pour des en-têtes trop longs, alors que la norme les
|
|
25
|
+
* distingue — 414 pour la cible (RFC 9112 §3), 431 pour les en-têtes
|
|
26
|
+
* (RFC 6585 §5).
|
|
27
|
+
*
|
|
28
|
+
* Le parseur ne constate le débordement qu'à la FIN d'un élément (la cible,
|
|
29
|
+
* une valeur d'en-tête), au point `bytesParsed` du paquet reçu (`rawPacket`).
|
|
30
|
+
* Si la ligne de requête était finie, son `CRLF` — ou celui d'un en-tête —
|
|
31
|
+
* précède ce point : ce sont les en-têtes qui débordent. Sinon, c'est la cible.
|
|
32
|
+
* Constaté sur Node 26, en un paquet comme en plusieurs. Faute de ces deux
|
|
33
|
+
* champs, 431 : le statut d'avant.
|
|
34
|
+
*/
|
|
35
|
+
function overflowStatusLine(error) {
|
|
36
|
+
const raw = "rawPacket" in error ? error.rawPacket : void 0;
|
|
37
|
+
const parsed = "bytesParsed" in error ? error.bytesParsed : void 0;
|
|
38
|
+
if (Buffer.isBuffer(raw) && typeof parsed === "number") {
|
|
39
|
+
const eol = raw.indexOf("\r\n");
|
|
40
|
+
if (eol === -1 || eol >= parsed) return "414 URI Too Long";
|
|
41
|
+
}
|
|
42
|
+
return "431 Request Header Fields Too Large";
|
|
19
43
|
}
|
|
20
44
|
/**
|
|
21
45
|
* Gère l'event `sessionError` d'un serveur HTTP/2 (h2c ou TLS) : un pair PARTI
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { asServersConfig, configuredServerPort, isH2cEnabled } from "../../src/servers/kernelServers.js";
|
|
2
|
-
import __decorate from "../../../_virtual/_@oxc-project_runtime@0.155.0/helpers/esm/decorate.js";
|
|
3
|
-
import { handleSessionError } from "./clientError.js";
|
|
4
1
|
import { createDrainTerminator } from "./serverShutdown.js";
|
|
5
|
-
import {
|
|
6
|
-
import __decorateParam from "../../../_virtual/_@oxc-project_runtime@0.155.0/helpers/esm/decorateParam.js";
|
|
2
|
+
import { handleSessionError } from "./clientError.js";
|
|
7
3
|
import { http2ServerLimits, setHttp2SessionTimeout } from "../../src/servers/http2Limits.js";
|
|
4
|
+
import { bindWithFallback, buildBindPlan, h2cPortConflict } from "../../src/servers/portBinder.js";
|
|
5
|
+
import { asServersConfig, configuredServerPort, isH2cEnabled } from "../../src/servers/kernelServers.js";
|
|
6
|
+
import __decorateParam from "../../../_virtual/_@oxc-project_runtime@0.157.0/helpers/esm/decorateParam.js";
|
|
7
|
+
import __decorate from "../../../_virtual/_@oxc-project_runtime@0.157.0/helpers/esm/decorate.js";
|
|
8
8
|
import { BootConfigurationError, Inject, Service, nodefonyError } from "nodefony";
|
|
9
9
|
import http2 from "node:http2";
|
|
10
10
|
// nodefony/service/servers/server-h2c.ts
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { asServersConfig, configuredServerPort } from "../../src/servers/kernelServers.js";
|
|
2
|
-
import __decorate from "../../../_virtual/_@oxc-project_runtime@0.155.0/helpers/esm/decorate.js";
|
|
3
1
|
import { handleClientError } from "./clientError.js";
|
|
2
|
+
import { routeSocketTimeout } from "../../src/context/http/HttpContext.js";
|
|
4
3
|
import { createDrainTerminator } from "./serverShutdown.js";
|
|
5
4
|
import { bindWithFallback, buildBindPlan } from "../../src/servers/portBinder.js";
|
|
6
|
-
import
|
|
5
|
+
import { asServersConfig, configuredServerPort } from "../../src/servers/kernelServers.js";
|
|
6
|
+
import __decorateParam from "../../../_virtual/_@oxc-project_runtime@0.157.0/helpers/esm/decorateParam.js";
|
|
7
|
+
import __decorate from "../../../_virtual/_@oxc-project_runtime@0.157.0/helpers/esm/decorate.js";
|
|
7
8
|
import { Inject, Service, extend, nodefonyError } from "nodefony";
|
|
8
9
|
import http from "node:http";
|
|
9
10
|
// nodefony/service/servers/server-http.ts
|
|
@@ -41,6 +42,7 @@ let ServerHttp = class ServerHttp extends Service {
|
|
|
41
42
|
this.server = http.createServer(opt);
|
|
42
43
|
this.httpTerminator = createDrainTerminator(this.server, this.options.shutdownTimeout);
|
|
43
44
|
if (this.options.maxHeadersCount) this.server.maxHeadersCount = this.options.maxHeadersCount;
|
|
45
|
+
this.server.on("timeout", routeSocketTimeout);
|
|
44
46
|
if (this.options.timeout) {
|
|
45
47
|
this.server.setTimeout(this.options.timeout, () => {
|
|
46
48
|
this.fire("onTimeout", this);
|