@nodefony/http 10.0.0-beta.6 → 10.0.0-beta.7
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/_virtual/{_@oxc-project_runtime@0.157.0 → _@oxc-project_runtime@0.158.0}/helpers/esm/decorate.js +1 -1
- package/dist/_virtual/{_@oxc-project_runtime@0.157.0 → _@oxc-project_runtime@0.158.0}/helpers/esm/decorateParam.js +1 -1
- package/dist/index.js +3 -2
- package/dist/nodefony/config/config.js +25 -2
- package/dist/nodefony/grpc/GrpcCall.js +298 -0
- package/dist/nodefony/grpc/GrpcEndpoint.js +528 -0
- package/dist/nodefony/grpc/index.js +4 -0
- package/dist/nodefony/interfaces/IRpcCall.js +1 -0
- package/dist/nodefony/service/audit-logger.js +52 -6
- package/dist/nodefony/service/error-renderer.js +41 -9
- package/dist/nodefony/service/http-kernel.js +198 -19
- package/dist/nodefony/service/pretty-request-logger.js +5 -3
- package/dist/nodefony/service/request-logger.js +21 -6
- package/dist/nodefony/service/reverse-proxy.js +1 -1
- package/dist/nodefony/service/servers/clientError.js +26 -1
- package/dist/nodefony/service/servers/server-h2c.js +3 -3
- package/dist/nodefony/service/servers/server-http.js +3 -3
- package/dist/nodefony/service/servers/server-https.js +4 -4
- package/dist/nodefony/service/servers/server-websocket-secure.js +3 -3
- package/dist/nodefony/service/servers/server-websocket.js +3 -3
- package/dist/nodefony/service/servers/serverShutdown.js +14 -36
- package/dist/nodefony/service/sessions/sessions-service.js +6 -6
- package/dist/nodefony/service/upload/upload-service.js +2 -2
- package/dist/nodefony/src/context/Context.js +12 -1
- package/dist/nodefony/src/context/http/ByteStream.js +9 -4
- package/dist/nodefony/src/context/http/HttpContext.js +86 -8
- package/dist/nodefony/src/context/http/Request.js +96 -16
- package/dist/nodefony/src/context/http/Response.js +120 -17
- package/dist/nodefony/src/context/http/SseStream.js +4 -4
- package/dist/nodefony/src/context/http/StreamLifecycle.js +328 -19
- package/dist/nodefony/src/context/http/parser.js +29 -11
- package/dist/nodefony/src/context/http/webResponse.js +56 -0
- package/dist/nodefony/src/context/http2/Response.js +47 -8
- package/dist/nodefony/src/context/requestTrace.js +6 -2
- package/dist/nodefony/src/context/rpcOutcome.js +20 -0
- package/dist/nodefony/src/context/websocket/Response.js +11 -1
- package/dist/nodefony/src/context/websocket/WebsocketContext.js +15 -4
- package/dist/nodefony/src/cookies/cookie.js +3 -1
- package/dist/nodefony/src/errors/httpError.js +58 -25
- package/dist/nodefony/src/grpc/cors.js +59 -0
- package/dist/nodefony/src/grpc/limits.js +16 -0
- package/dist/nodefony/src/grpc/refusal.js +271 -0
- package/dist/nodefony/src/profiler/aggregate.js +2 -2
- package/dist/nodefony/src/proxy/forward.js +1 -1
- package/dist/nodefony/src/proxy/generateProxyConfig.js +91 -15
- package/dist/nodefony/src/servers/http2Goaway.js +76 -0
- package/dist/nodefony/src/servers/http2Limits.js +41 -15
- package/dist/nodefony/src/servers/http2StreamGrace.js +100 -0
- package/dist/nodefony/src/servers/receiveDeadline.js +177 -0
- package/dist/types/index.d.ts +5 -2
- package/dist/types/nodefony/config/config.d.ts +10 -0
- package/dist/types/nodefony/grpc/GrpcCall.d.ts +142 -0
- package/dist/types/nodefony/grpc/GrpcEndpoint.d.ts +98 -0
- package/dist/types/nodefony/grpc/index.d.ts +15 -0
- package/dist/types/nodefony/interfaces/IContext.d.ts +9 -0
- package/dist/types/nodefony/interfaces/IErrorRenderer.d.ts +14 -0
- package/dist/types/nodefony/interfaces/IRequest.d.ts +2 -1
- package/dist/types/nodefony/interfaces/IRequestTrace.d.ts +10 -0
- package/dist/types/nodefony/interfaces/IRouting.d.ts +19 -0
- package/dist/types/nodefony/interfaces/IRpcCall.d.ts +83 -0
- package/dist/types/nodefony/interfaces/IUpload.d.ts +51 -4
- package/dist/types/nodefony/interfaces/index.d.ts +2 -1
- package/dist/types/nodefony/service/audit-logger.d.ts +33 -3
- package/dist/types/nodefony/service/error-renderer.d.ts +22 -1
- package/dist/types/nodefony/service/http-kernel.d.ts +21 -1
- package/dist/types/nodefony/service/request-logger.d.ts +10 -0
- package/dist/types/nodefony/service/servers/serverShutdown.d.ts +2 -2
- package/dist/types/nodefony/src/context/Context.d.ts +9 -1
- package/dist/types/nodefony/src/context/http/ByteStream.d.ts +1 -1
- package/dist/types/nodefony/src/context/http/HttpContext.d.ts +56 -2
- package/dist/types/nodefony/src/context/http/Request.d.ts +38 -8
- package/dist/types/nodefony/src/context/http/Response.d.ts +61 -7
- package/dist/types/nodefony/src/context/http/SseStream.d.ts +1 -1
- package/dist/types/nodefony/src/context/http/StreamLifecycle.d.ts +53 -0
- package/dist/types/nodefony/src/context/http/parser.d.ts +17 -3
- package/dist/types/nodefony/src/context/http/webResponse.d.ts +31 -0
- package/dist/types/nodefony/src/context/http2/Response.d.ts +35 -2
- package/dist/types/nodefony/src/context/requestTrace.d.ts +3 -1
- package/dist/types/nodefony/src/context/rpcOutcome.d.ts +11 -0
- package/dist/types/nodefony/src/context/websocket/Response.d.ts +6 -1
- package/dist/types/nodefony/src/context/websocket/WebsocketContext.d.ts +8 -0
- package/dist/types/nodefony/src/errors/httpError.d.ts +29 -4
- package/dist/types/nodefony/src/grpc/cors.d.ts +32 -0
- package/dist/types/nodefony/src/grpc/limits.d.ts +14 -0
- package/dist/types/nodefony/src/grpc/refusal.d.ts +136 -0
- package/dist/types/nodefony/src/proxy/forward.d.ts +7 -0
- package/dist/types/nodefony/src/proxy/generateProxyConfig.d.ts +15 -2
- package/dist/types/nodefony/src/servers/http2Goaway.d.ts +41 -0
- package/dist/types/nodefony/src/servers/http2Limits.d.ts +27 -14
- package/dist/types/nodefony/src/servers/http2StreamGrace.d.ts +34 -0
- package/dist/types/nodefony/src/servers/receiveDeadline.d.ts +77 -0
- package/docs/byte-stream.md +52 -17
- package/docs/diagnostics-channel.md +45 -1
- package/docs/servers.md +57 -44
- package/docs/upload.md +43 -17
- package/package.json +26 -6
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import HttpError from "../src/errors/httpError.js";
|
|
1
|
+
import HttpError, { serializeHttpError } from "../src/errors/httpError.js";
|
|
2
2
|
import { toWsCloseCode } from "../src/context/websocket/WebsocketContext.js";
|
|
3
3
|
import { Nodefony, nodefonyError } from "nodefony";
|
|
4
4
|
// nodefony/service/error-renderer.ts
|
|
@@ -49,17 +49,23 @@ const UNIQUE_VIOLATION_MESSAGE = "Conflict — a resource with these unique valu
|
|
|
49
49
|
*/
|
|
50
50
|
const OPAQUE_SERVER_ERROR = "Internal Server Error";
|
|
51
51
|
/**
|
|
52
|
+
* Le message rendu au client : celui de l'erreur, sauf une panne 5xx en
|
|
53
|
+
* production, qui devient opaque. Un 4xx garde le sien — c'est un message
|
|
54
|
+
* écrit POUR le client (validation, refus de politique), pas une fuite.
|
|
55
|
+
*/
|
|
56
|
+
const clientMessage = (status, message, production) => production && status >= 500 ? OPAQUE_SERVER_ERROR : message;
|
|
57
|
+
/**
|
|
52
58
|
* Clés d'un `HttpError` sérialisé qui décrivent les ENTRAILLES du serveur et ne
|
|
53
59
|
* doivent jamais franchir la frontière en production.
|
|
54
60
|
*/
|
|
55
|
-
const INTERNAL_ERROR_KEYS = [
|
|
61
|
+
const INTERNAL_ERROR_KEYS = /* @__PURE__ */ new Set([
|
|
56
62
|
"stack",
|
|
57
63
|
"controller",
|
|
58
64
|
"action",
|
|
59
65
|
"bundle",
|
|
60
66
|
"url",
|
|
61
67
|
"pdu"
|
|
62
|
-
];
|
|
68
|
+
]);
|
|
63
69
|
/**
|
|
64
70
|
* Vrai si le runtime tourne en production — le détail des erreurs reste alors au
|
|
65
71
|
* journal, seul le serveur le voit.
|
|
@@ -376,12 +382,10 @@ var DefaultErrorRenderer = class {
|
|
|
376
382
|
const status = this.normalizeHttpStatus(httpError.code);
|
|
377
383
|
httpError.code = status;
|
|
378
384
|
const obj = context.metaData;
|
|
379
|
-
const
|
|
380
|
-
const
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
serialized.message = message;
|
|
384
|
-
}
|
|
385
|
+
const production = isProduction();
|
|
386
|
+
const serialized = serializeHttpError(httpError, production ? INTERNAL_ERROR_KEYS : null);
|
|
387
|
+
const message = clientMessage(status, httpError.message, production);
|
|
388
|
+
if (production) serialized.message = message;
|
|
385
389
|
obj.error = serialized;
|
|
386
390
|
obj.code = status;
|
|
387
391
|
obj.message = message;
|
|
@@ -405,6 +409,34 @@ var DefaultErrorRenderer = class {
|
|
|
405
409
|
reason
|
|
406
410
|
};
|
|
407
411
|
}
|
|
412
|
+
/**
|
|
413
|
+
* Le statut HTTP et le message client qu'une route HTTP rendrait pour cette
|
|
414
|
+
* erreur — mêmes classements que {@link renderHttp} (validation 422, conflit
|
|
415
|
+
* 409, identifiant malformé, code numérique) et même opacité d'un 5xx en
|
|
416
|
+
* production —, sans RIEN écrire : ni statut sur la réponse, ni `metaData`,
|
|
417
|
+
* ni pile formatée. Sert les transports sans corps d'erreur : un appel gRPC
|
|
418
|
+
* traduit ce statut en code (ADR-0026 D7).
|
|
419
|
+
*
|
|
420
|
+
* Un identifiant malformé y vaut 400 : sans contexte, aucun chemin ne le
|
|
421
|
+
* désigne.
|
|
422
|
+
*
|
|
423
|
+
* @param error - l'erreur levée.
|
|
424
|
+
* @returns son statut émettable et le message destiné au client.
|
|
425
|
+
*
|
|
426
|
+
* @experimental Surface gRPC, ADR-0026 D11.
|
|
427
|
+
*/
|
|
428
|
+
describe(error) {
|
|
429
|
+
const httpError = this.toHttpError(error, void 0);
|
|
430
|
+
const status = this.normalizeHttpStatus(httpError.code);
|
|
431
|
+
return {
|
|
432
|
+
status,
|
|
433
|
+
message: clientMessage(status, httpError.message, isProduction())
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Ramène une erreur à une `HttpError`. Sans contexte (`describe`), rien
|
|
438
|
+
* n'est écrit : `HttpError` ne pose le statut sur la réponse qu'avec lui.
|
|
439
|
+
*/
|
|
408
440
|
toHttpError(error, context) {
|
|
409
441
|
if (error instanceof HttpError) return error;
|
|
410
442
|
const fields = toValidationFields(error);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import HttpError from "../src/errors/httpError.js";
|
|
2
2
|
import { isUnsupportedTransferCoding } from "../src/proxy/rules.js";
|
|
3
3
|
import { hasConflictingAuthority } from "../src/context/transport.js";
|
|
4
|
+
import { grpcWireOf, hasGrpcRoutes, prepareGrpcRefusal } from "../src/grpc/refusal.js";
|
|
4
5
|
import { buildTrustProxy } from "../src/context/trustProxy.js";
|
|
5
6
|
import { resolveForwarded } from "../src/context/forwarded.js";
|
|
6
7
|
import { MemoryRateLimitStore } from "../src/rateLimit/MemoryRateLimitStore.js";
|
|
@@ -10,7 +11,9 @@ import { isProfiledUrl } from "../src/profiler/aggregate.js";
|
|
|
10
11
|
import { responseEnded } from "../src/context/responseEnded.js";
|
|
11
12
|
import { unsentResponseWarning } from "../src/context/unsentResponse.js";
|
|
12
13
|
import { closeRequestTrace, hasRequestTraceSubscribers, openRequestTrace, runTraced } from "../src/context/requestTrace.js";
|
|
14
|
+
import { SHUTDOWN_STREAMS_WAIT_MS, ShutdownRegistry, StallSweep } from "../src/context/http/StreamLifecycle.js";
|
|
13
15
|
import WebsocketContext, { toHandshakeStatus } from "../src/context/websocket/WebsocketContext.js";
|
|
16
|
+
import { ReceiveDeadline } from "../src/servers/receiveDeadline.js";
|
|
14
17
|
import { trackPong } from "./servers/wsHeartbeat.js";
|
|
15
18
|
import { parseWebsocketUrl } from "../src/context/websocket/websocketUrl.js";
|
|
16
19
|
import HttpContext from "../src/context/http/HttpContext.js";
|
|
@@ -20,7 +23,7 @@ import PrettyRequestLogger from "./pretty-request-logger.js";
|
|
|
20
23
|
import JsonAuditLogger from "./audit-logger.js";
|
|
21
24
|
import { resolveTraceparent } from "./trace.js";
|
|
22
25
|
import { asServersConfig, configuredServerPort } from "../src/servers/kernelServers.js";
|
|
23
|
-
import __decorate from "../../_virtual/_@oxc-project_runtime@0.
|
|
26
|
+
import __decorate from "../../_virtual/_@oxc-project_runtime@0.158.0/helpers/esm/decorate.js";
|
|
24
27
|
import { BootConfigurationError, GcScheduler, IDENTITY_CHANGED_EVENT, RequestContext, RevocationWatch, Service, finallyMaybe, injectable, isPromise, logColor, nodefonyError, servedUrl, thenMaybe, writeRuntimeState } from "nodefony";
|
|
25
28
|
import cluster from "node:cluster";
|
|
26
29
|
import http from "node:http";
|
|
@@ -111,6 +114,16 @@ let HttpKernel = class HttpKernel extends Service {
|
|
|
111
114
|
}
|
|
112
115
|
responseTimeout;
|
|
113
116
|
closeTimeOutWs;
|
|
117
|
+
/**
|
|
118
|
+
* Échéance de réception du corps HTTP/2, par serveur (`requestTimeout` de sa
|
|
119
|
+
* section) : `node:http2` ne borne pas la réception, HTTP/1.1 l'est par Node.
|
|
120
|
+
* `undefined` tant qu'aucun corps HTTP/2 n'a été lu, `null` si le délai est
|
|
121
|
+
* désactivé (0). Cf {@link ReceiveDeadline}.
|
|
122
|
+
*/
|
|
123
|
+
#h2Receive = {
|
|
124
|
+
http: void 0,
|
|
125
|
+
https: void 0
|
|
126
|
+
};
|
|
114
127
|
secContentTypeOptions = null;
|
|
115
128
|
secFrameOptions = null;
|
|
116
129
|
secHsts = null;
|
|
@@ -142,9 +155,11 @@ let HttpKernel = class HttpKernel extends Service {
|
|
|
142
155
|
rateLimitGc = null;
|
|
143
156
|
#wsConnCounter = null;
|
|
144
157
|
#streams = null;
|
|
158
|
+
#stallSweep = null;
|
|
159
|
+
#openStreams = null;
|
|
145
160
|
#healthPaths = null;
|
|
146
161
|
#healthShutdownDelay = 0;
|
|
147
|
-
#
|
|
162
|
+
#shutdownSteps = null;
|
|
148
163
|
constructor(module) {
|
|
149
164
|
super(serviceName, module.container, module.notificationsCenter, module.options);
|
|
150
165
|
this.module = module;
|
|
@@ -292,7 +307,7 @@ let HttpKernel = class HttpKernel extends Service {
|
|
|
292
307
|
* Le message nomme l'action, ce qu'elle a rendu et le geste qui convient
|
|
293
308
|
* (`unsentResponseWarning`).
|
|
294
309
|
*
|
|
295
|
-
* Appelé au timeout, AVANT que le
|
|
310
|
+
* Appelé au timeout, AVANT que le 503 parte — après, `sended` est vrai et
|
|
296
311
|
* plus rien ne se signalerait — et au démontage, pour un client parti avant.
|
|
297
312
|
* Gratuit en production : rien n'est lu au-delà de `waitAsync`.
|
|
298
313
|
*
|
|
@@ -379,13 +394,10 @@ let HttpKernel = class HttpKernel extends Service {
|
|
|
379
394
|
});
|
|
380
395
|
this.once("onTerminate", () => {
|
|
381
396
|
this.rateLimitGc?.stop();
|
|
397
|
+
this.#h2Receive.http?.close();
|
|
398
|
+
this.#h2Receive.https?.close();
|
|
382
399
|
});
|
|
383
|
-
this.kernel?.once("
|
|
384
|
-
this.kernel?.prependOnceListener("onTerminate", async () => {
|
|
385
|
-
this.#terminating = true;
|
|
386
|
-
if (this.#healthShutdownDelay > 0) await new Promise((r) => setTimeout(r, this.#healthShutdownDelay));
|
|
387
|
-
});
|
|
388
|
-
});
|
|
400
|
+
this.kernel?.once("onDrain", () => this.drainTraffic());
|
|
389
401
|
this.kernel?.prependOnceListener("onBoot", () => {
|
|
390
402
|
this.router = this.get("router");
|
|
391
403
|
this.firewall = this.get("firewall");
|
|
@@ -478,7 +490,7 @@ let HttpKernel = class HttpKernel extends Service {
|
|
|
478
490
|
const secure = context.serverType === "wss";
|
|
479
491
|
const raw = context.origin;
|
|
480
492
|
if (this.isWebsocketOriginAllowed(raw, context.domain, secure)) return;
|
|
481
|
-
const error =
|
|
493
|
+
const error = HttpError.withoutStack(`WebSocket Origin "${raw}" not allowed (CSWSH protection)`, 1008, context);
|
|
482
494
|
error.type = "origin";
|
|
483
495
|
throw error;
|
|
484
496
|
}
|
|
@@ -582,7 +594,7 @@ let HttpKernel = class HttpKernel extends Service {
|
|
|
582
594
|
}
|
|
583
595
|
throw resolver.exception;
|
|
584
596
|
}
|
|
585
|
-
throw
|
|
597
|
+
throw HttpError.withoutStack("Not Found", 404, context);
|
|
586
598
|
}
|
|
587
599
|
/**
|
|
588
600
|
* {@link prepareFrontController} + instanciation immédiate du controller
|
|
@@ -669,8 +681,12 @@ let HttpKernel = class HttpKernel extends Service {
|
|
|
669
681
|
context.isRedirect = false;
|
|
670
682
|
context.response.removeHeader("Location");
|
|
671
683
|
}
|
|
684
|
+
context.response.closeUnfinishedRequest();
|
|
685
|
+
const headers = context.request.headers;
|
|
686
|
+
const wire = grpcWireOf(headers, context.method === "GET" && context.request.url.searchParams.get("connect") === "v1");
|
|
672
687
|
try {
|
|
673
|
-
await context.render(result.body);
|
|
688
|
+
if (wire === 0) await context.render(result.body);
|
|
689
|
+
else await context.send(prepareGrpcRefusal(context.response, wire, result.status, context.authenticatedUser() !== null, headers["content-type"]));
|
|
674
690
|
} catch (e) {
|
|
675
691
|
this.log(e, "CRITIC");
|
|
676
692
|
throw e;
|
|
@@ -744,7 +760,7 @@ let HttpKernel = class HttpKernel extends Service {
|
|
|
744
760
|
return;
|
|
745
761
|
}
|
|
746
762
|
if (url === this.#healthPaths.readiness) {
|
|
747
|
-
this.#respondHealth(response,
|
|
763
|
+
this.#respondHealth(response, this.kernel?.isTerminating !== true && this.kernel?.servable === true, type);
|
|
748
764
|
return;
|
|
749
765
|
}
|
|
750
766
|
}
|
|
@@ -910,13 +926,29 @@ let HttpKernel = class HttpKernel extends Service {
|
|
|
910
926
|
* @returns `undefined` quand le démontage est terminé ; sinon la promesse de
|
|
911
927
|
* sa fin, qui ne rejette pas.
|
|
912
928
|
*/
|
|
929
|
+
/**
|
|
930
|
+
* Fixe le code d'un appel RPC au `close` : « réponse finie » est la règle
|
|
931
|
+
* unique du noyau ({@link responseEnded}, celle du 499), jamais
|
|
932
|
+
* `writableFinished`, qui dit « finie » sur une annulation HTTP/2. Une cause
|
|
933
|
+
* rendue par l'appel devient l'erreur de la requête : la ligne d'accès la
|
|
934
|
+
* porte, le canal la publie — une ligne par appel en erreur (D10 point 3).
|
|
935
|
+
*
|
|
936
|
+
* @param context - le contexte de la requête.
|
|
937
|
+
* @param rpc - son appel RPC.
|
|
938
|
+
*/
|
|
939
|
+
settleRpc(context, rpc) {
|
|
940
|
+
const raw = context.response.response;
|
|
941
|
+
const cause = rpc.settle(raw !== null && responseEnded(raw));
|
|
942
|
+
if (cause !== null && !context.error) context.error = cause;
|
|
943
|
+
}
|
|
913
944
|
teardownHttp(context, scope) {
|
|
914
945
|
if (context.finished) return void 0;
|
|
915
946
|
try {
|
|
916
947
|
context.stopClock();
|
|
917
948
|
if (context.waitAsync) this.warnUnsentResponse(context);
|
|
949
|
+
if (context.rpc !== null) this.settleRpc(context, context.rpc);
|
|
918
950
|
context.logRequest();
|
|
919
|
-
if (context.requestTrace !== null) closeRequestTrace(context, context.response.statusCode, context.error);
|
|
951
|
+
if (context.requestTrace !== null) closeRequestTrace(context, context.response.statusCode, context.error, context.rpc);
|
|
920
952
|
context.logPhasesVerbose();
|
|
921
953
|
if (this.shouldProfile(context.url)) this.profiler?.collect(context);
|
|
922
954
|
const afterResponse = context._runAfterResponse();
|
|
@@ -1112,16 +1144,52 @@ let HttpKernel = class HttpKernel extends Service {
|
|
|
1112
1144
|
return this.serveHttpRequest(httpContext);
|
|
1113
1145
|
}
|
|
1114
1146
|
/**
|
|
1147
|
+
* L'échéance de réception HTTP/2 du serveur qui sert (`requestTimeout` de sa
|
|
1148
|
+
* section), créée au premier corps HTTP/2 lu ; `null` si le délai vaut 0.
|
|
1149
|
+
*
|
|
1150
|
+
* À l'échéance, la réception en cours est interrompue (`abortBody`) et la
|
|
1151
|
+
* requête rendue en 408 par le chemin d'erreur habituel — comme un corps qui
|
|
1152
|
+
* dépasse sa limite (413).
|
|
1153
|
+
*/
|
|
1154
|
+
http2ReceiveDeadline(serverType) {
|
|
1155
|
+
const key = serverType === "https" ? "https" : "http";
|
|
1156
|
+
let deadline = this.#h2Receive[key];
|
|
1157
|
+
if (deadline === void 0) {
|
|
1158
|
+
const timeoutMs = this.options[key].requestTimeout;
|
|
1159
|
+
deadline = timeoutMs > 0 ? new ReceiveDeadline(timeoutMs, (context) => {
|
|
1160
|
+
if (context.finished) return;
|
|
1161
|
+
context.request.abortBody(HttpError.withoutStack("Request Timeout", 408, context));
|
|
1162
|
+
}) : null;
|
|
1163
|
+
this.#h2Receive[key] = deadline;
|
|
1164
|
+
}
|
|
1165
|
+
return deadline;
|
|
1166
|
+
}
|
|
1167
|
+
/**
|
|
1115
1168
|
* Le corps — lu seulement s'il y en a un à lire —, puis les gardes
|
|
1116
1169
|
* ({@link onRequestEnd}) et l'action. Chacun n'est attendu que s'il attend.
|
|
1117
1170
|
*/
|
|
1118
1171
|
serveHttpRequest(httpContext) {
|
|
1119
1172
|
const headers = httpContext.request.headers;
|
|
1120
|
-
if (isUnsupportedTransferCoding(headers["transfer-encoding"])) return Promise.reject(
|
|
1121
|
-
if (hasConflictingAuthority(headers)) return Promise.reject(
|
|
1122
|
-
const
|
|
1173
|
+
if (isUnsupportedTransferCoding(headers["transfer-encoding"])) return Promise.reject(HttpError.withoutStack("Transfer-Encoding not implemented", 501, httpContext));
|
|
1174
|
+
if (hasConflictingAuthority(headers)) return Promise.reject(HttpError.withoutStack("Host differs from :authority", 400, httpContext));
|
|
1175
|
+
const resolver = httpContext.resolver;
|
|
1176
|
+
if (hasGrpcRoutes() && resolver?.resolve === true && httpContext.method === "POST" && resolver.route?.grpc !== true && grpcWireOf(headers) === 1) resolver.resolve = false;
|
|
1177
|
+
const skipBody = !(resolver?.resolve === true) || resolver.route?.bodyStream === true;
|
|
1123
1178
|
httpContext.phaseStart("parse");
|
|
1124
|
-
|
|
1179
|
+
let receiving = null;
|
|
1180
|
+
let receivingSeq = 0;
|
|
1181
|
+
const parsed = finallyMaybe(() => {
|
|
1182
|
+
if (skipBody) return void 0;
|
|
1183
|
+
const pending = httpContext.request.initialize();
|
|
1184
|
+
if (pending !== void 0 && (httpContext.alpnProtocol === "h2" || httpContext.alpnProtocol === "h2c")) {
|
|
1185
|
+
receiving = this.http2ReceiveDeadline(httpContext.serverType);
|
|
1186
|
+
if (receiving !== null) receivingSeq = receiving.track(httpContext);
|
|
1187
|
+
}
|
|
1188
|
+
return pending;
|
|
1189
|
+
}, () => {
|
|
1190
|
+
httpContext.phaseEnd("parse");
|
|
1191
|
+
if (receiving !== null) receiving.release(receivingSeq, httpContext);
|
|
1192
|
+
});
|
|
1125
1193
|
return thenMaybe(parsed, () => thenMaybe(this.onRequestEnd(httpContext), (guarded) => {
|
|
1126
1194
|
guarded.phaseStart("action");
|
|
1127
1195
|
return finallyMaybe(() => guarded.handle(), () => guarded.phaseEnd("action"));
|
|
@@ -1512,7 +1580,7 @@ let HttpKernel = class HttpKernel extends Service {
|
|
|
1512
1580
|
watchStream(stream) {
|
|
1513
1581
|
(this.#streams ??= new RevocationWatch({
|
|
1514
1582
|
isValid: (entry) => entry.revalidate(),
|
|
1515
|
-
revoke: (entry) => void entry.
|
|
1583
|
+
revoke: (entry) => void entry.revoke(),
|
|
1516
1584
|
identityOf: (entry) => entry.identifier ?? void 0
|
|
1517
1585
|
})).register(stream);
|
|
1518
1586
|
}
|
|
@@ -1521,6 +1589,117 @@ let HttpKernel = class HttpKernel extends Service {
|
|
|
1521
1589
|
this.#streams?.unregister(stream);
|
|
1522
1590
|
}
|
|
1523
1591
|
/**
|
|
1592
|
+
* Inscrit au balayage de blocage un flux dont un tiers tient les écritures :
|
|
1593
|
+
* un client qui ne lit plus y est coupé (`StreamLifecycle.sweepStall`).
|
|
1594
|
+
*
|
|
1595
|
+
* @internal — appelé par le cycle de vie du flux (`StreamLifecycle`).
|
|
1596
|
+
*/
|
|
1597
|
+
sweepStall(stream) {
|
|
1598
|
+
(this.#stallSweep ??= new StallSweep()).add(stream);
|
|
1599
|
+
}
|
|
1600
|
+
/** Retire un flux du balayage de blocage (à sa fermeture). @internal */
|
|
1601
|
+
unsweepStall(stream) {
|
|
1602
|
+
this.#stallSweep?.delete(stream);
|
|
1603
|
+
}
|
|
1604
|
+
/**
|
|
1605
|
+
* Inscrit un flux sans fin à lui (SSE, flux RPC, octets vivants) au registre
|
|
1606
|
+
* de l'arrêt : il sera terminé AVANT le drain ({@link shutdownStreams}).
|
|
1607
|
+
* Ouvert pendant l'arrêt, il l'est au tour suivant — une fois son appel RPC
|
|
1608
|
+
* relié, qui ne l'est qu'après l'ouverture.
|
|
1609
|
+
*
|
|
1610
|
+
* @internal — appelé par le cycle de vie du flux (`StreamLifecycle`).
|
|
1611
|
+
*/
|
|
1612
|
+
trackStream(stream) {
|
|
1613
|
+
(this.#openStreams ??= new ShutdownRegistry()).add(stream);
|
|
1614
|
+
}
|
|
1615
|
+
/** Retire un flux du registre de l'arrêt (à sa fermeture). @internal */
|
|
1616
|
+
untrackStream(stream) {
|
|
1617
|
+
this.#openStreams?.delete(stream);
|
|
1618
|
+
}
|
|
1619
|
+
/**
|
|
1620
|
+
* Inscrit une étape de l'arrêt du trafic, appelée par {@link drainTraffic}.
|
|
1621
|
+
*
|
|
1622
|
+
* Un serveur s'inscrit à sa création au lieu d'écouter le noyau : l'ordre
|
|
1623
|
+
* (congé des connexions longues AVANT le drain, ADR-0026 D8) vit alors dans
|
|
1624
|
+
* une seule fonction, plus dans des moments d'inscription.
|
|
1625
|
+
*
|
|
1626
|
+
* @param step - `dismiss` : congédier les connexions longues (WebSocket
|
|
1627
|
+
* 1001) ; `drain` : cesser d'accepter et laisser finir les requêtes.
|
|
1628
|
+
* @param action - l'étape ; une promesse rendue est attendue.
|
|
1629
|
+
*
|
|
1630
|
+
* @internal
|
|
1631
|
+
*/
|
|
1632
|
+
addShutdownStep(step, action) {
|
|
1633
|
+
this.#shutdownSteps ??= {
|
|
1634
|
+
dismiss: [],
|
|
1635
|
+
drain: []
|
|
1636
|
+
};
|
|
1637
|
+
this.#shutdownSteps[step].push(action);
|
|
1638
|
+
}
|
|
1639
|
+
/**
|
|
1640
|
+
* L'arrêt du trafic (ADR-0027 D2), écouteur unique d'`onDrain` :
|
|
1641
|
+
*
|
|
1642
|
+
* 1. si le pod a été annoncé disponible, attendre `health.shutdownDelay` —
|
|
1643
|
+
* la sonde répond déjà 503 (`kernel.isTerminating`), la répartition
|
|
1644
|
+
* retire le pod ;
|
|
1645
|
+
* 2. congédier ensemble les connexions longues : WebSocket (1001) et flux
|
|
1646
|
+
* longs (fin au format de leur protocole, {@link shutdownStreams}) ;
|
|
1647
|
+
* 3. drainer ensemble les serveurs HTTP, HTTPS et h2c (ports distincts).
|
|
1648
|
+
*
|
|
1649
|
+
* Une étape qui échoue est journalisée sans arrêter les autres.
|
|
1650
|
+
*
|
|
1651
|
+
* @internal
|
|
1652
|
+
*/
|
|
1653
|
+
async drainTraffic() {
|
|
1654
|
+
if (this.kernel?.postReady === true && this.#healthShutdownDelay > 0) await new Promise((r) => setTimeout(r, this.#healthShutdownDelay));
|
|
1655
|
+
const steps = this.#shutdownSteps;
|
|
1656
|
+
await this.#runShutdownSteps([...steps?.dismiss ?? [], () => this.shutdownStreams()]);
|
|
1657
|
+
if (steps !== null) await this.#runShutdownSteps(steps.drain);
|
|
1658
|
+
}
|
|
1659
|
+
/** Lance des étapes d'arrêt ensemble, attend la dernière, journalise les échecs. */
|
|
1660
|
+
async #runShutdownSteps(actions) {
|
|
1661
|
+
const settled = await Promise.allSettled(actions.map(async (action) => action()));
|
|
1662
|
+
for (const outcome of settled) if (outcome.status === "rejected") this.log(outcome.reason, "ERROR", "TERMINATE");
|
|
1663
|
+
}
|
|
1664
|
+
/**
|
|
1665
|
+
* L'arrêt du serveur : termine chaque flux long ouvert, puis attend leur
|
|
1666
|
+
* fermeture, au plus `waitMs` (ADR-0026 D8).
|
|
1667
|
+
*
|
|
1668
|
+
* Un appel RPC se termine au format de son protocole (gRPC : 14
|
|
1669
|
+
* `UNAVAILABLE`, le client le rejoue ailleurs), un flux SSE ou d'octets
|
|
1670
|
+
* vivant par une fin simple (`EventSource` se reconnecte). Un gestionnaire
|
|
1671
|
+
* qui n'entend rien laisse l'attente aller à son terme : sa connexion tombe
|
|
1672
|
+
* ensuite au drain, à `shutdownTimeout`.
|
|
1673
|
+
*
|
|
1674
|
+
* Appelé par {@link drainTraffic}, avec les fermetures 1001, avant le
|
|
1675
|
+
* drain : le client lit la fin du flux AVANT le GOAWAY définitif. Un appel unaire en vol n'est pas touché — il finit
|
|
1676
|
+
* pendant le drain.
|
|
1677
|
+
*
|
|
1678
|
+
* @param waitMs - attente maximale des fermetures.
|
|
1679
|
+
* @returns la promesse de l'attente, ou rien quand aucun flux n'est ouvert.
|
|
1680
|
+
*
|
|
1681
|
+
* @internal
|
|
1682
|
+
*/
|
|
1683
|
+
shutdownStreams(waitMs = SHUTDOWN_STREAMS_WAIT_MS) {
|
|
1684
|
+
const registry = this.#openStreams ??= new ShutdownRegistry();
|
|
1685
|
+
const count = registry.size;
|
|
1686
|
+
const left = registry.shutdown(waitMs);
|
|
1687
|
+
if (count === 0) return void 0;
|
|
1688
|
+
if (typeof left === "number") {
|
|
1689
|
+
this.#logStreamsShutdown(count, left, waitMs);
|
|
1690
|
+
return;
|
|
1691
|
+
}
|
|
1692
|
+
return left.then((still) => this.#logStreamsShutdown(count, still, waitMs));
|
|
1693
|
+
}
|
|
1694
|
+
/** Une ligne pour l'arrêt des flux longs ; un avertissement s'il en reste. */
|
|
1695
|
+
#logStreamsShutdown(count, still, waitMs) {
|
|
1696
|
+
if (still === 0) {
|
|
1697
|
+
this.log(`Arrêt : ${count} flux long(s) terminé(s)`, "INFO");
|
|
1698
|
+
return;
|
|
1699
|
+
}
|
|
1700
|
+
this.log(`Arrêt : ${still} flux long(s) sur ${count} toujours ouvert(s) après ${waitMs} ms — gestionnaire qui n'écoute pas son signal ; coupé(s) par le drain à shutdownTimeout`, "WARNING");
|
|
1701
|
+
}
|
|
1702
|
+
/**
|
|
1524
1703
|
* Re-valide tous les flux en cours et ferme ceux dont l'identité est morte.
|
|
1525
1704
|
* Publique pour qu'un banc la déclenche sans attendre le tick.
|
|
1526
1705
|
*/
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { severityFromStatus, statusOf } from "./audit-logger.js";
|
|
1
|
+
import { severityFromStatus, severityOfHttp, statusOf } from "./audit-logger.js";
|
|
2
|
+
import { rpcSegment } from "./request-logger.js";
|
|
2
3
|
import { handshakeRefusalStatus } from "../src/context/websocket/WebsocketContext.js";
|
|
3
4
|
import { accessStatusFamily, logColor } from "nodefony";
|
|
4
5
|
// nodefony/service/pretty-request-logger.ts
|
|
@@ -27,9 +28,10 @@ var PrettyRequestLogger = class {
|
|
|
27
28
|
const status = statusOf(ctx) ?? (err ? 500 : null);
|
|
28
29
|
const method = ctx.method ?? "?";
|
|
29
30
|
const duration = ctx.stopClock();
|
|
31
|
+
const rpc = ctx.rpc ?? null;
|
|
30
32
|
return {
|
|
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
|
-
severity:
|
|
33
|
+
text: `${logColor.cyan(method.padEnd(4))} ${colorizeStatus(status)} ${ctx.url} ${logColor.blackBright(formatDuration(duration))} ${logColor.blackBright(ctx.remoteAddress ?? "-")} ` + logColor.magenta(`[${shortId(ctx.requestId)}]`) + (rpc === null ? "" : ` ${rpcSegment(rpc)}`) + (err ? ` ${logColor.red(err.message)}` : ""),
|
|
34
|
+
severity: severityOfHttp(ctx, status),
|
|
33
35
|
msgid: "req"
|
|
34
36
|
};
|
|
35
37
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { severityFromStatus } from "./audit-logger.js";
|
|
1
|
+
import { severityFromRpcCode, severityFromStatus } from "./audit-logger.js";
|
|
2
2
|
import { handshakeRefusalStatus } from "../src/context/websocket/WebsocketContext.js";
|
|
3
3
|
import { logColor } from "nodefony";
|
|
4
4
|
// nodefony/service/request-logger.ts
|
|
@@ -11,6 +11,18 @@ const statusOf = (context) => {
|
|
|
11
11
|
return context.response?.statusCode;
|
|
12
12
|
};
|
|
13
13
|
/**
|
|
14
|
+
* Segment d'un appel RPC sur la ligne d'accès : `RPC : <méthode> <NOM>`
|
|
15
|
+
* (ADR-0026 D10 point 4) — le statut HTTP reste à sa place. Sans code (appel
|
|
16
|
+
* jamais commencé), la méthode seule.
|
|
17
|
+
*
|
|
18
|
+
* @param rpc - le résultat de l'appel.
|
|
19
|
+
* @returns le segment, sans espace de tête.
|
|
20
|
+
*/
|
|
21
|
+
function rpcSegment(rpc) {
|
|
22
|
+
const name = rpc.codeName;
|
|
23
|
+
return name === null ? `${logColor.cyan("RPC")} : ${rpc.method}` : `${logColor.cyan("RPC")} : ${rpc.method} ${name}`;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
14
26
|
* Default Nodefony per-request logger — preserves the legacy colored format.
|
|
15
27
|
*
|
|
16
28
|
* HTTP success: `URL : <url> FROM : <remote> ORIGIN : <host> ID : <uuid>`
|
|
@@ -23,21 +35,24 @@ const statusOf = (context) => {
|
|
|
23
35
|
*/
|
|
24
36
|
var DefaultRequestLogger = class {
|
|
25
37
|
renderHttp(context, error) {
|
|
26
|
-
const
|
|
38
|
+
const rpc = context.rpc ?? null;
|
|
39
|
+
const txt = `${logColor.cyan("URL")} : ${context.url} ${logColor.cyan("FROM")} : ${context.remoteAddress} ${logColor.cyan("ORIGIN")} : ${context.originUrl?.host} ${logColor.cyan("ID")} : ${context.requestId}` + (rpc === null ? "" : ` ${rpcSegment(rpc)}`);
|
|
40
|
+
const code = rpc === null ? null : rpc.code;
|
|
27
41
|
const err = error ?? context.error;
|
|
28
42
|
if (err) {
|
|
29
43
|
const errCode = err.code ?? statusOf(context) ?? 500;
|
|
30
44
|
const msgid = `${context.serverType} ${logColor.magenta(errCode)} ${logColor.red(context.method ?? "")}`;
|
|
31
45
|
return {
|
|
32
46
|
text: context.kernel?.environment === "production" ? `${txt} ${err}` : `${txt}\n ${err}`,
|
|
33
|
-
severity: "ERROR",
|
|
47
|
+
severity: code === null ? "ERROR" : severityFromRpcCode(code),
|
|
34
48
|
msgid
|
|
35
49
|
};
|
|
36
50
|
}
|
|
51
|
+
const msgid = `${context.serverType} ${logColor.magenta(statusOf(context) ?? "")} ${context.method}`;
|
|
37
52
|
return {
|
|
38
53
|
text: txt,
|
|
39
|
-
severity: "INFO",
|
|
40
|
-
msgid
|
|
54
|
+
severity: code === null ? "INFO" : severityFromRpcCode(code),
|
|
55
|
+
msgid
|
|
41
56
|
};
|
|
42
57
|
}
|
|
43
58
|
renderWebsocket(context, error, acceptedProtocol) {
|
|
@@ -59,4 +74,4 @@ var DefaultRequestLogger = class {
|
|
|
59
74
|
};
|
|
60
75
|
}
|
|
61
76
|
};
|
|
62
|
-
export { DefaultRequestLogger as default };
|
|
77
|
+
export { DefaultRequestLogger as default, rpcSegment };
|
|
@@ -67,7 +67,7 @@ var ReverseProxy = class extends Service {
|
|
|
67
67
|
this.defaultTimeoutMs = section?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
68
68
|
this.connectTimeoutMs = section?.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS;
|
|
69
69
|
this.maxSockets = section?.maxSockets ?? DEFAULT_MAX_SOCKETS;
|
|
70
|
-
this.kernel?.
|
|
70
|
+
this.kernel?.once("onTerminate", () => this.closeAll());
|
|
71
71
|
for (const [prefix, mount] of Object.entries(section?.mounts ?? {})) this.#add(prefix, mount, "config");
|
|
72
72
|
}
|
|
73
73
|
/**
|
|
@@ -1,6 +1,27 @@
|
|
|
1
1
|
import { isPeerGoneError } from "../../src/context/websocket/Response.js";
|
|
2
|
+
import { STATUS_CODES } from "node:http";
|
|
2
3
|
// nodefony/service/servers/clientError.ts
|
|
3
4
|
/**
|
|
5
|
+
* Le statut que Node lui-même aurait répondu (`_http_server.js#socketOnError`),
|
|
6
|
+
* hors débordement d'en-têtes ({@link overflowStatusLine}) ; 400 sinon.
|
|
7
|
+
* Brancher un écouteur `clientError` retire cette réponse par défaut : sans
|
|
8
|
+
* cette table, un délai de réception dépassé partait en `400 Bad Request` —
|
|
9
|
+
* le client lisait sa requête malformée alors qu'elle était seulement lente.
|
|
10
|
+
*/
|
|
11
|
+
const CLIENT_ERROR_STATUS = /* @__PURE__ */ new Map([["ERR_HTTP_REQUEST_TIMEOUT", 408], ["HPE_CHUNK_EXTENSIONS_OVERFLOW", 413]]);
|
|
12
|
+
function statusLineOf(status) {
|
|
13
|
+
return `${status} ${STATUS_CODES[status] ?? ""}`;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* La réponse en vol sur cette socket a-t-elle déjà écrit ses en-têtes ? La
|
|
17
|
+
* garde même de Node (`socketOnError` : `_httpMessage._headerSent`), lue par
|
|
18
|
+
* réflexion — deux champs internes, sans équivalent public sur la socket.
|
|
19
|
+
*/
|
|
20
|
+
function inFlightHeaderSent(socket) {
|
|
21
|
+
const message = Reflect.get(socket, "_httpMessage");
|
|
22
|
+
return message !== null && typeof message === "object" && Reflect.get(message, "_headerSent") === true;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
4
25
|
* Gère l'event `clientError` d'un `http.Server` / `https.Server`, et des connexions
|
|
5
26
|
* HTTP/1.1 d'un serveur HTTP/2 à `allowHTTP1`.
|
|
6
27
|
*
|
|
@@ -16,7 +37,11 @@ import { isPeerGoneError } from "../../src/context/websocket/Response.js";
|
|
|
16
37
|
function handleClientError(error, socket) {
|
|
17
38
|
const code = "code" in error ? error.code : void 0;
|
|
18
39
|
if (code === "ECONNRESET" || !socket.writable) return;
|
|
19
|
-
|
|
40
|
+
if (inFlightHeaderSent(socket)) {
|
|
41
|
+
socket.destroy();
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const statusLine = code === "HPE_HEADER_OVERFLOW" ? overflowStatusLine(error) : statusLineOf(CLIENT_ERROR_STATUS.get(code) ?? 400);
|
|
20
45
|
socket.end(`HTTP/1.1 ${statusLine}\r\nConnection: close\r\nContent-Length: 0\r\n\r\n`);
|
|
21
46
|
}
|
|
22
47
|
/**
|
|
@@ -3,8 +3,8 @@ import { handleSessionError } from "./clientError.js";
|
|
|
3
3
|
import { http2ServerLimits, setHttp2SessionTimeout } from "../../src/servers/http2Limits.js";
|
|
4
4
|
import { bindWithFallback, buildBindPlan, h2cPortConflict } from "../../src/servers/portBinder.js";
|
|
5
5
|
import { asServersConfig, configuredServerPort, isH2cEnabled } from "../../src/servers/kernelServers.js";
|
|
6
|
-
import __decorateParam from "../../../_virtual/_@oxc-project_runtime@0.
|
|
7
|
-
import __decorate from "../../../_virtual/_@oxc-project_runtime@0.
|
|
6
|
+
import __decorateParam from "../../../_virtual/_@oxc-project_runtime@0.158.0/helpers/esm/decorateParam.js";
|
|
7
|
+
import __decorate from "../../../_virtual/_@oxc-project_runtime@0.158.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
|
|
@@ -98,7 +98,7 @@ let ServerH2c = class ServerH2c extends Service {
|
|
|
98
98
|
server.close();
|
|
99
99
|
reject(error);
|
|
100
100
|
});
|
|
101
|
-
this.
|
|
101
|
+
this.httpKernel.addShutdownStep("drain", async () => {
|
|
102
102
|
if (!this.server) return;
|
|
103
103
|
try {
|
|
104
104
|
await this.httpTerminator?.terminate();
|
|
@@ -3,8 +3,8 @@ import { routeSocketTimeout } from "../../src/context/http/HttpContext.js";
|
|
|
3
3
|
import { createDrainTerminator } from "./serverShutdown.js";
|
|
4
4
|
import { bindWithFallback, buildBindPlan } from "../../src/servers/portBinder.js";
|
|
5
5
|
import { asServersConfig, configuredServerPort } from "../../src/servers/kernelServers.js";
|
|
6
|
-
import __decorateParam from "../../../_virtual/_@oxc-project_runtime@0.
|
|
7
|
-
import __decorate from "../../../_virtual/_@oxc-project_runtime@0.
|
|
6
|
+
import __decorateParam from "../../../_virtual/_@oxc-project_runtime@0.158.0/helpers/esm/decorateParam.js";
|
|
7
|
+
import __decorate from "../../../_virtual/_@oxc-project_runtime@0.158.0/helpers/esm/decorate.js";
|
|
8
8
|
import { Inject, Service, extend, nodefonyError } from "nodefony";
|
|
9
9
|
import http from "node:http";
|
|
10
10
|
// nodefony/service/servers/server-http.ts
|
|
@@ -69,7 +69,7 @@ let ServerHttp = class ServerHttp extends Service {
|
|
|
69
69
|
this.reportBindError(error);
|
|
70
70
|
reject(error);
|
|
71
71
|
});
|
|
72
|
-
this.
|
|
72
|
+
this.httpKernel.addShutdownStep("drain", async () => {
|
|
73
73
|
if (!this.server) return;
|
|
74
74
|
try {
|
|
75
75
|
await this.httpTerminator?.terminate();
|
|
@@ -4,8 +4,8 @@ import { handleClientError, handleSessionError } from "./clientError.js";
|
|
|
4
4
|
import { routeSocketTimeout } from "../../src/context/http/HttpContext.js";
|
|
5
5
|
import { bindWithFallback, buildBindPlan } from "../../src/servers/portBinder.js";
|
|
6
6
|
import { asServersConfig, configuredServerPort } from "../../src/servers/kernelServers.js";
|
|
7
|
-
import __decorateParam from "../../../_virtual/_@oxc-project_runtime@0.
|
|
8
|
-
import __decorate from "../../../_virtual/_@oxc-project_runtime@0.
|
|
7
|
+
import __decorateParam from "../../../_virtual/_@oxc-project_runtime@0.158.0/helpers/esm/decorateParam.js";
|
|
8
|
+
import __decorate from "../../../_virtual/_@oxc-project_runtime@0.158.0/helpers/esm/decorate.js";
|
|
9
9
|
import { Inject, Service, extend, nodefonyError } from "nodefony";
|
|
10
10
|
import http2 from "node:http2";
|
|
11
11
|
import https from "node:https";
|
|
@@ -76,7 +76,7 @@ let ServerHttps = class ServerHttps extends Service {
|
|
|
76
76
|
this.server.on("request", (request, response) => {
|
|
77
77
|
this.httpKernel.onHttpRequest(request, response, this.type);
|
|
78
78
|
});
|
|
79
|
-
this.
|
|
79
|
+
this.httpKernel.addShutdownStep("drain", async () => {
|
|
80
80
|
if (!this.server) return;
|
|
81
81
|
try {
|
|
82
82
|
await this.httpTerminator?.terminate();
|
|
@@ -120,7 +120,7 @@ let ServerHttps = class ServerHttps extends Service {
|
|
|
120
120
|
this.reportBindError(error);
|
|
121
121
|
reject(error);
|
|
122
122
|
});
|
|
123
|
-
this.
|
|
123
|
+
this.httpKernel.addShutdownStep("drain", async () => {
|
|
124
124
|
if (!this.server) return;
|
|
125
125
|
try {
|
|
126
126
|
await this.httpTerminator?.terminate();
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { asServersConfig, configuredServerPort } from "../../src/servers/kernelServers.js";
|
|
2
2
|
import { startHeartbeat } from "./wsHeartbeat.js";
|
|
3
3
|
import { attachUpgradeDispatch, createNodefonyWebSocketServer } from "../../src/servers/upgradeDispatch.js";
|
|
4
|
-
import __decorateParam from "../../../_virtual/_@oxc-project_runtime@0.
|
|
5
|
-
import __decorate from "../../../_virtual/_@oxc-project_runtime@0.
|
|
4
|
+
import __decorateParam from "../../../_virtual/_@oxc-project_runtime@0.158.0/helpers/esm/decorateParam.js";
|
|
5
|
+
import __decorate from "../../../_virtual/_@oxc-project_runtime@0.158.0/helpers/esm/decorate.js";
|
|
6
6
|
import { Inject, Service } from "nodefony";
|
|
7
7
|
import Ws from "ws";
|
|
8
8
|
// nodefony/service/servers/server-websocket-secure.ts
|
|
@@ -50,7 +50,7 @@ let WebsocketSecure = class WebsocketSecure extends Service {
|
|
|
50
50
|
this.httpKernel.onWebsocketUpgrade(req, admission, this.type).catch(() => {});
|
|
51
51
|
});
|
|
52
52
|
this.heartbeatTimer = startHeartbeat(this.server, this.options);
|
|
53
|
-
this.
|
|
53
|
+
this.httpKernel.addShutdownStep("dismiss", () => this.terminate());
|
|
54
54
|
this.ready = true;
|
|
55
55
|
this.module.fire("onServersReady", this.type, this);
|
|
56
56
|
return resolve(this.server);
|