@nodefony/http 10.0.0-beta.3 → 10.0.0-beta.5
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.153.0 → _@oxc-project_runtime@0.155.0}/helpers/esm/decorate.js +1 -2
- package/dist/_virtual/{_@oxc-project_runtime@0.153.0 → _@oxc-project_runtime@0.155.0}/helpers/esm/decorateParam.js +1 -2
- package/dist/index.js +13 -10
- package/dist/nodefony/command/assetsPublishCommand.js +5 -3
- package/dist/nodefony/command/certificatesCommand.js +5 -3
- package/dist/nodefony/command/networkCommand.js +5 -3
- package/dist/nodefony/command/proxyGenerateCommand.js +8 -3
- package/dist/nodefony/config/config.js +3 -3
- package/dist/nodefony/config/defineModuleConfig.js +1 -2
- package/dist/nodefony/interfaces/IRequestTrace.js +1 -0
- package/dist/nodefony/service/HttpAdminApi.js +2 -2
- package/dist/nodefony/service/ProfilerAdminApi.js +13 -3
- package/dist/nodefony/service/audit-logger.js +23 -30
- package/dist/nodefony/service/certificates.js +97 -13
- package/dist/nodefony/service/error-renderer.js +29 -12
- package/dist/nodefony/service/http-kernel.js +198 -50
- package/dist/nodefony/service/pretty-request-logger.js +15 -13
- package/dist/nodefony/service/request-logger.js +24 -16
- package/dist/nodefony/service/reverse-proxy.js +3 -3
- package/dist/nodefony/service/servers/clientError.js +22 -3
- package/dist/nodefony/service/servers/server-h2c.js +126 -0
- package/dist/nodefony/service/servers/server-http.js +5 -8
- package/dist/nodefony/service/servers/server-https.js +12 -24
- package/dist/nodefony/service/servers/server-static.js +3 -3
- package/dist/nodefony/service/servers/server-websocket-secure.js +10 -18
- package/dist/nodefony/service/servers/server-websocket.js +10 -18
- package/dist/nodefony/service/servers/serverShutdown.js +78 -18
- package/dist/nodefony/service/servers/wsHeartbeat.js +1 -2
- package/dist/nodefony/service/sessions/sessions-service.js +7 -14
- package/dist/nodefony/service/trace.js +1 -2
- package/dist/nodefony/service/upload/upload-service.js +5 -8
- package/dist/nodefony/service/x509.js +1 -2
- package/dist/nodefony/src/assets/collectAssets.js +1 -2
- package/dist/nodefony/src/assets/prebuiltUi.js +1 -2
- package/dist/nodefony/src/context/Context.js +78 -43
- package/dist/nodefony/src/context/domainMatcher.js +1 -2
- package/dist/nodefony/src/context/forwarded.js +1 -2
- package/dist/nodefony/src/context/http/HttpContext.js +28 -6
- package/dist/nodefony/src/context/http/Request.js +33 -29
- package/dist/nodefony/src/context/http/Response.js +23 -6
- package/dist/nodefony/src/context/http/SseStream.js +1 -2
- package/dist/nodefony/src/context/http/parser.js +36 -10
- package/dist/nodefony/src/context/http/urlFastPath.js +1 -2
- package/dist/nodefony/src/context/http2/Request.js +4 -8
- package/dist/nodefony/src/context/http2/Response.js +41 -9
- package/dist/nodefony/src/context/metaData.js +1 -2
- package/dist/nodefony/src/context/requestId.js +1 -2
- package/dist/nodefony/src/context/requestTrace.js +104 -0
- package/dist/nodefony/src/context/responseEnded.js +1 -2
- package/dist/nodefony/src/context/timingMode.js +21 -0
- package/dist/nodefony/src/context/transport.js +45 -0
- package/dist/nodefony/src/context/trustProxy.js +1 -2
- package/dist/nodefony/src/context/unsentResponse.js +30 -0
- package/dist/nodefony/src/context/websocket/Response.js +33 -6
- package/dist/nodefony/src/context/websocket/WebsocketContext.js +104 -25
- package/dist/nodefony/src/context/websocket/isWebsocketContext.js +17 -0
- package/dist/nodefony/src/context/websocket/websocketUrl.js +1 -2
- package/dist/nodefony/src/context/websocket/wsBackpressure.js +1 -2
- package/dist/nodefony/src/context/websocket/wsCloseReason.js +1 -2
- package/dist/nodefony/src/context/websocket/wsLogContent.js +1 -2
- package/dist/nodefony/src/cookies/cookie.js +3 -7
- package/dist/nodefony/src/errors/httpError.js +1 -2
- package/dist/nodefony/src/errors/thrownMessage.js +1 -2
- package/dist/nodefony/src/profiler/FrameProfile.js +23 -2
- package/dist/nodefony/src/profiler/Profiler.js +104 -32
- package/dist/nodefony/src/profiler/aggregate.js +442 -0
- package/dist/nodefony/src/proxy/forward.js +29 -5
- package/dist/nodefony/src/proxy/generateProxyConfig.js +14 -6
- package/dist/nodefony/src/proxy/rules.js +12 -6
- package/dist/nodefony/src/rateLimit/MemoryRateLimitStore.js +1 -2
- package/dist/nodefony/src/rateLimit/WsConnectionCounter.js +1 -2
- package/dist/nodefony/src/rateLimit/rateLimitFilters.js +1 -2
- package/dist/nodefony/src/servers/http2Limits.js +50 -0
- package/dist/nodefony/src/servers/kernelServers.js +12 -3
- package/dist/nodefony/src/servers/portBinder.js +41 -10
- package/dist/nodefony/src/servers/upgradeDispatch.js +86 -7
- package/dist/nodefony/src/session/session.js +2 -3
- package/dist/nodefony/src/session/sessionStoreFailure.js +1 -2
- package/dist/nodefony/src/session/storage/MemorySessionStorage.js +1 -2
- package/dist/nodefony/src/session/storage/RevocationGuardStorage.js +1 -2
- package/dist/nodefony/src/session/storage/sessionFilters.js +1 -2
- package/dist/nodefony/src/session/storage/sessionSort.js +1 -2
- package/dist/types/index.d.ts +9 -3
- package/dist/types/nodefony/config/config.d.ts +4 -0
- package/dist/types/nodefony/interfaces/IContext.d.ts +72 -4
- package/dist/types/nodefony/interfaces/IRequest.d.ts +9 -4
- 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 +26 -0
- package/dist/types/nodefony/interfaces/index.d.ts +3 -2
- package/dist/types/nodefony/service/ProfilerAdminApi.d.ts +3 -0
- package/dist/types/nodefony/service/audit-logger.d.ts +3 -11
- package/dist/types/nodefony/service/certificates.d.ts +55 -4
- package/dist/types/nodefony/service/error-renderer.d.ts +31 -0
- package/dist/types/nodefony/service/http-kernel.d.ts +96 -10
- package/dist/types/nodefony/service/servers/clientError.d.ts +16 -0
- package/dist/types/nodefony/service/servers/server-h2c.d.ts +51 -0
- package/dist/types/nodefony/service/servers/server-http.d.ts +3 -3
- package/dist/types/nodefony/service/servers/server-https.d.ts +4 -4
- package/dist/types/nodefony/service/servers/server-websocket-secure.d.ts +1 -3
- package/dist/types/nodefony/service/servers/server-websocket.d.ts +1 -3
- package/dist/types/nodefony/service/servers/serverShutdown.d.ts +30 -16
- package/dist/types/nodefony/src/context/Context.d.ts +38 -5
- package/dist/types/nodefony/src/context/http/HttpContext.d.ts +16 -2
- package/dist/types/nodefony/src/context/http/Request.d.ts +20 -15
- package/dist/types/nodefony/src/context/http/Response.d.ts +11 -0
- package/dist/types/nodefony/src/context/http/parser.d.ts +18 -2
- package/dist/types/nodefony/src/context/http2/Request.d.ts +1 -1
- package/dist/types/nodefony/src/context/http2/Response.d.ts +27 -5
- 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 +53 -0
- package/dist/types/nodefony/src/context/unsentResponse.d.ts +16 -0
- package/dist/types/nodefony/src/context/websocket/Response.d.ts +18 -2
- package/dist/types/nodefony/src/context/websocket/WebsocketContext.d.ts +58 -8
- package/dist/types/nodefony/src/context/websocket/isWebsocketContext.d.ts +15 -0
- package/dist/types/nodefony/src/profiler/FrameProfile.d.ts +28 -12
- package/dist/types/nodefony/src/profiler/Profiler.d.ts +79 -111
- package/dist/types/nodefony/src/profiler/aggregate.d.ts +132 -0
- package/dist/types/nodefony/src/proxy/forward.d.ts +14 -2
- package/dist/types/nodefony/src/proxy/generateProxyConfig.d.ts +16 -0
- package/dist/types/nodefony/src/proxy/rules.d.ts +3 -0
- package/dist/types/nodefony/src/servers/http2Limits.d.ts +37 -0
- package/dist/types/nodefony/src/servers/kernelServers.d.ts +10 -1
- package/dist/types/nodefony/src/servers/portBinder.d.ts +29 -5
- package/dist/types/nodefony/src/servers/upgradeDispatch.d.ts +33 -3
- package/docs/cookies.md +85 -78
- package/docs/diagnostics-channel.md +303 -0
- package/docs/index.md +6 -1
- package/docs/observabilite.md +159 -81
- package/docs/rate-limit.md +83 -83
- package/docs/reverse-proxy.md +72 -70
- package/docs/servers.md +338 -224
- package/docs/session.md +179 -179
- package/docs/sse.md +24 -20
- package/docs/upload.md +120 -117
- package/package.json +8 -9
- package/dist/_virtual/_@oxc-project_runtime@0.153.0/helpers/esm/decorateMetadata.js +0 -6
|
@@ -7,6 +7,7 @@ import http2 from "node:http2";
|
|
|
7
7
|
import http from "node:http";
|
|
8
8
|
import https from "node:https";
|
|
9
9
|
import HttpKernel, { ServerType, Data, SchemeType } from "../../service/http-kernel.js";
|
|
10
|
+
import type { AlpnProtocol, ProtocolName } from "../../interfaces/IContext.js";
|
|
10
11
|
import HttpResponse from "./http/Response.js";
|
|
11
12
|
import Http2Response from "./http2/Response.js";
|
|
12
13
|
import WebsocketResponse from "./websocket/Response.js";
|
|
@@ -50,7 +51,15 @@ declare class Context extends Service implements IContextInterface {
|
|
|
50
51
|
requested: boolean;
|
|
51
52
|
sessionStarting: boolean;
|
|
52
53
|
domain: string;
|
|
53
|
-
|
|
54
|
+
/** Famille traitée (`network.protocol.name` d'OpenTelemetry) — union ouverte. */
|
|
55
|
+
type: ProtocolName;
|
|
56
|
+
/** Serveur qui a reçu la requête — choisit la configuration par serveur. */
|
|
57
|
+
serverType: ServerType;
|
|
58
|
+
/**
|
|
59
|
+
* Protocole du saut local (registre IANA ALPN) — union ouverte. Une connexion
|
|
60
|
+
* WebSocket naît d'un upgrade HTTP/1.1 ; `HttpContext` le constate sur la requête.
|
|
61
|
+
*/
|
|
62
|
+
alpnProtocol: AlpnProtocol;
|
|
54
63
|
server?: WebSocketServer | http.Server | https.Server | http2.Http2SecureServer | null | undefined;
|
|
55
64
|
httpKernel: HttpKernel | null;
|
|
56
65
|
request: contextRequest | null;
|
|
@@ -88,6 +97,12 @@ declare class Context extends Service implements IContextInterface {
|
|
|
88
97
|
crossDomain: boolean;
|
|
89
98
|
router: IRequestRouter | null;
|
|
90
99
|
resolver: IRouteResolver | null;
|
|
100
|
+
/**
|
|
101
|
+
* Module qui signe les lignes de CETTE requête : la ligne d'accès et les
|
|
102
|
+
* lignes du contrôleur. `null` = le module de la route (sans route : le
|
|
103
|
+
* journal du noyau). Un contrôleur le change : `this.context.logModule = "billing"`.
|
|
104
|
+
*/
|
|
105
|
+
logModule: string | null;
|
|
91
106
|
/**
|
|
92
107
|
* Intent de session de la route courante (posé par le Resolver depuis
|
|
93
108
|
* `@UseSession` / paramètre `@Session`). Pilote le point d'activation unique
|
|
@@ -119,6 +134,9 @@ declare class Context extends Service implements IContextInterface {
|
|
|
119
134
|
securityTrace: ISecurityTrace | null;
|
|
120
135
|
private readonly _timingEnabled;
|
|
121
136
|
private readonly _timingVerbose;
|
|
137
|
+
private readonly _clockEnabled;
|
|
138
|
+
private readonly _clockStartMs;
|
|
139
|
+
private _durationMs;
|
|
122
140
|
readonly phases: PhaseTiming[];
|
|
123
141
|
private _phaseIndex;
|
|
124
142
|
private _afterResponseFns;
|
|
@@ -128,7 +146,7 @@ declare class Context extends Service implements IContextInterface {
|
|
|
128
146
|
metaData: Data;
|
|
129
147
|
scheme: SchemeType;
|
|
130
148
|
webSocketState: WebSocketState;
|
|
131
|
-
constructor(container: Container | Scope,
|
|
149
|
+
constructor(container: Container | Scope, serverType: ServerType);
|
|
132
150
|
/**
|
|
133
151
|
* Assemble l'enveloppe `metaData` de la requête (`nodefony.*` + `route`
|
|
134
152
|
* snapshot per-requête + overrides appelant). Délègue au builder monomorphe
|
|
@@ -152,6 +170,21 @@ declare class Context extends Service implements IContextInterface {
|
|
|
152
170
|
get timingEnabled(): boolean;
|
|
153
171
|
phaseStart(name: PhaseName): void;
|
|
154
172
|
phaseEnd(name: PhaseName): void;
|
|
173
|
+
/**
|
|
174
|
+
* Arrête l'horloge de la requête et rend sa durée, en millisecondes.
|
|
175
|
+
*
|
|
176
|
+
* La durée court de la construction du contexte au PREMIER appel : le
|
|
177
|
+
* démontage HTTP (`close` de la réponse, dernier octet remis — la mesure
|
|
178
|
+
* d'OpenTelemetry et du `$request_time` de nginx), la ligne d'accès pour un
|
|
179
|
+
* handshake WebSocket. Les appels suivants rendent la même valeur : journal
|
|
180
|
+
* d'accès, profiler et trace verbose lisent UNE durée, jamais trois.
|
|
181
|
+
*
|
|
182
|
+
* Active par défaut dans tous les environnements, production comprise :
|
|
183
|
+
* seul `timing.enabled: false` la coupe (cf `resolveTimingMode`).
|
|
184
|
+
*
|
|
185
|
+
* @returns la durée, ou `null` si `timing.enabled` vaut `false`
|
|
186
|
+
*/
|
|
187
|
+
stopClock(): number | null;
|
|
155
188
|
onAfterResponse(fn: AfterResponseHandler): void;
|
|
156
189
|
/**
|
|
157
190
|
* Exécute les hooks `onAfterResponse`, une seule fois, dans l'ordre
|
|
@@ -167,7 +200,7 @@ declare class Context extends Service implements IContextInterface {
|
|
|
167
200
|
private runAfterResponseHooks;
|
|
168
201
|
get signal(): AbortSignal;
|
|
169
202
|
_abortIfPending(reason?: string): void;
|
|
170
|
-
log(pci: Pci, severity?: Severity, msgid?: Msgid, msg?: Message): Pdu;
|
|
203
|
+
log(pci: Pci, severity?: Severity, msgid?: Msgid, msg?: Message, moduleName?: string): Pdu;
|
|
171
204
|
/**
|
|
172
205
|
* Confie au contexte le magasin `RequestContext` de son unité de travail
|
|
173
206
|
* (requête HTTP, connexion WebSocket), pour qu'il le VIDE à son nettoyage.
|
|
@@ -183,8 +216,8 @@ declare class Context extends Service implements IContextInterface {
|
|
|
183
216
|
clean(): void;
|
|
184
217
|
/**
|
|
185
218
|
* Log d'un event du cycle de vie — gate boot-time (V2.1) : en production le
|
|
186
|
-
* Pdu n'est PAS construit (return avant toute allocation). Hors prod,
|
|
187
|
-
*
|
|
219
|
+
* Pdu n'est PAS construit (return avant toute allocation). Hors prod : DEBUG,
|
|
220
|
+
* pour le Suivi de requête de Studio — la ligne `req` porte le bilan.
|
|
188
221
|
*/
|
|
189
222
|
private logEvent;
|
|
190
223
|
fire(event: KernelEventsType, ...args: unknown[]): boolean;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ServerType, SchemeType } from "../../../service/http-kernel.js";
|
|
2
|
+
import HttpError from "../../errors/httpError.js";
|
|
2
3
|
import Context, { HTTPMethod } from "../Context.js";
|
|
3
|
-
import { Container, Scope } from "nodefony";
|
|
4
|
+
import { Container, Scope, nodefonyError } from "nodefony";
|
|
4
5
|
import type { MaybePromise } from "nodefony";
|
|
5
6
|
import HttpRequest from "./Request.js";
|
|
6
7
|
import HttpResponse from "./Response.js";
|
|
@@ -50,10 +51,15 @@ declare class HttpContext extends Context implements IHttpContextInterface {
|
|
|
50
51
|
private activeSocket;
|
|
51
52
|
isRedirect: boolean;
|
|
52
53
|
sended: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Requête relayée par le proxy inverse : la politique de ligne d'accès de
|
|
56
|
+
* son montage. `null` = servie par l'application (cas de toute requête).
|
|
57
|
+
*/
|
|
58
|
+
relayAccessLog: "all" | "errors" | null;
|
|
53
59
|
request: HttpRequestType;
|
|
54
60
|
response: HttpRsponseType;
|
|
55
61
|
uploadService: uploadService | null;
|
|
56
|
-
constructor(container: Container | Scope, request: http.IncomingMessage | http2.Http2ServerRequest, response: http.ServerResponse | http2.Http2ServerResponse,
|
|
62
|
+
constructor(container: Container | Scope, request: http.IncomingMessage | http2.Http2ServerRequest, response: http.ServerResponse | http2.Http2ServerResponse, serverType: ServerType);
|
|
57
63
|
/**
|
|
58
64
|
* URL d'origine effective, construite À LA DEMANDE (1ʳᵉ lecture, puis
|
|
59
65
|
* mémoïsée). `Origin` invalide ou `null` (RFC 6454) → repli sur l'URL de la
|
|
@@ -84,6 +90,14 @@ declare class HttpContext extends Context implements IHttpContextInterface {
|
|
|
84
90
|
*/
|
|
85
91
|
handle(): MaybePromise<this>;
|
|
86
92
|
setTimeout(): void;
|
|
93
|
+
/**
|
|
94
|
+
* Ligne d'accès — pour une requête RELAYÉE, avec le statut que l'amont a
|
|
95
|
+
* réellement rendu (le relais écrit la réponse brute, pas celle du
|
|
96
|
+
* contexte) ; un montage `accessLog: "errors"` ne l'écrit qu'en échec.
|
|
97
|
+
*
|
|
98
|
+
* @param httpError - l'erreur de la requête, s'il y en a une
|
|
99
|
+
*/
|
|
100
|
+
logRequest(httpError?: Error | HttpError | nodefonyError | null): import("nodefony").Pdu | undefined;
|
|
87
101
|
/**
|
|
88
102
|
* Nettoie le contexte, et le DÉTACHE de sa socket h1 : la connexion
|
|
89
103
|
* persistante survit à la requête, et l'entrée de `socketActiveContext`
|
|
@@ -5,6 +5,7 @@ import { URL } from "node:url";
|
|
|
5
5
|
import { HTTPMethod, Cookies } from "../Context.js";
|
|
6
6
|
import QS from "qs";
|
|
7
7
|
import { ParserXml, ParserQs, Parser, acceptParser } from "./parser.js";
|
|
8
|
+
import type { RequestBody } from "../../../interfaces/IRequest.js";
|
|
8
9
|
import { UploadedFile } from "../../../service/upload/upload-service.js";
|
|
9
10
|
import type { IParsedUploadFile, IUploadOptions } from "../../../interfaces/IUpload.js";
|
|
10
11
|
import { Pci, Pdu, Message, Severity, Msgid } from "nodefony";
|
|
@@ -75,22 +76,24 @@ declare class HttpRequest {
|
|
|
75
76
|
get url(): URL;
|
|
76
77
|
set url(u: URL);
|
|
77
78
|
parser: ParserType | null;
|
|
78
|
-
queryPost: Record<string, unknown>;
|
|
79
|
-
queryGet: Record<string, unknown>;
|
|
80
|
-
queryFile: UploadedFile[];
|
|
81
|
-
query: Record<string, unknown>;
|
|
82
79
|
/**
|
|
83
|
-
* Le CORPS de la requête, parsé
|
|
84
|
-
* Fastify, NestJS)
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
80
|
+
* Le CORPS de la requête, parsé (JSON, urlencoded, XML, champs multipart) —
|
|
81
|
+
* nom universel de l'écosystème (Express, Fastify, NestJS) et celui
|
|
82
|
+
* d'`IAdminRequest.body`. Jamais un paramètre d'URL : ceux-là sont dans
|
|
83
|
+
* {@link query}, et les deux ne fusionnent pas.
|
|
84
|
+
*/
|
|
85
|
+
body: RequestBody;
|
|
86
|
+
/**
|
|
87
|
+
* Les fichiers reçus dans un corps multipart, déjà écrits dans le répertoire
|
|
88
|
+
* temporaire — lus par `@UploadedFile()` / `@UploadedFiles()`. Les champs
|
|
89
|
+
* texte du même corps sont dans {@link body}.
|
|
90
90
|
*
|
|
91
|
-
*
|
|
91
|
+
* Lecture seule : une requête sans upload rend un vide partagé et gelé,
|
|
92
|
+
* aucune allocation.
|
|
92
93
|
*/
|
|
93
|
-
get
|
|
94
|
+
get files(): readonly UploadedFile[];
|
|
95
|
+
/** Les paramètres d'URL, seuls — jamais un champ du corps ({@link body}). */
|
|
96
|
+
query: Record<string, unknown>;
|
|
94
97
|
queryStringOptions: (QS.IParseOptions & {
|
|
95
98
|
decoder?: undefined;
|
|
96
99
|
}) | undefined;
|
|
@@ -112,7 +115,7 @@ declare class HttpRequest {
|
|
|
112
115
|
* remplace le `once("onRequestEnd")` du ctor qui allouait 1 onceWrapper +
|
|
113
116
|
* 1 closure à CHAQUE requête.
|
|
114
117
|
*
|
|
115
|
-
* Il porte le CORPS (`
|
|
118
|
+
* Il porte le CORPS (`body`), jamais les paramètres d'URL : un seul sens de
|
|
116
119
|
* `body` dans tout le framework, celui de l'écosystème et d'`IAdminRequest`.
|
|
117
120
|
*/
|
|
118
121
|
private fireRequestEnd;
|
|
@@ -228,7 +231,9 @@ declare class HttpRequest {
|
|
|
228
231
|
/**
|
|
229
232
|
* Scheme effectif côté client : `Forwarded`/`X-Forwarded-*` résolu de façon
|
|
230
233
|
* canonique (this.forwarded, gated proxy de confiance) ; sinon le transport
|
|
231
|
-
* réel. HTTP/2
|
|
234
|
+
* réel. Vaut pour HTTP/2 aussi : le pseudo-en-tête `:scheme` vient du client
|
|
235
|
+
* (RFC 9113 §8.3.1) et n'est jamais cru — sur h2c, `:scheme: https` ferait
|
|
236
|
+
* passer une connexion claire pour chiffrée.
|
|
232
237
|
*/
|
|
233
238
|
protected resolveScheme(request: http.IncomingMessage | http2.Http2ServerRequest): string;
|
|
234
239
|
getFullUrl(request?: http.IncomingMessage | http2.Http2ServerRequest): string;
|
|
@@ -50,6 +50,17 @@ declare class HttpResponse {
|
|
|
50
50
|
deleteCookieByName(name: string): boolean;
|
|
51
51
|
setCookies(): void | http.ServerResponse<http.IncomingMessage>;
|
|
52
52
|
setCookie(cookie: Cookie): void | http.ServerResponse<http.IncomingMessage>;
|
|
53
|
+
/**
|
|
54
|
+
* Journalise en DEBUG un cookie posé, sa VALEUR MASQUÉE : nom et attributs
|
|
55
|
+
* seulement. Le journal de développement se lit par le plan d'administration
|
|
56
|
+
* — un identifiant de session y entrerait chez quiconque le consulte.
|
|
57
|
+
* La ligne n'est construite que si DEBUG passe le gate du syslog pour ce
|
|
58
|
+
* `msgid` (debug ciblé compris) : rien n'est alloué en production.
|
|
59
|
+
*
|
|
60
|
+
* @param cookie - le cookie posé.
|
|
61
|
+
* @param serialized - sa forme `Set-Cookie`, déjà calculée pour l'en-tête.
|
|
62
|
+
*/
|
|
63
|
+
private logCookie;
|
|
53
64
|
setHeader(name: string, value: number | string | readonly string[]): void | http.ServerResponse<http.IncomingMessage>;
|
|
54
65
|
setHeaders(obj: OutgoingHttpHeaders): OutgoingHttpHeaders;
|
|
55
66
|
setContentType(type?: string, encoding?: BufferEncoding): void | http.ServerResponse<http.IncomingMessage>;
|
|
@@ -22,6 +22,22 @@ declare class Parser {
|
|
|
22
22
|
protected ended(): Promise<void>;
|
|
23
23
|
parse(): Promise<this>;
|
|
24
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* Corps d'un type que le framework ne décode pas : livré TEL QU'IL EST REÇU,
|
|
27
|
+
* jamais remplacé par un objet vide qui mentirait.
|
|
28
|
+
*
|
|
29
|
+
* - `text/*` → une chaîne, décodée au `charset` du `Content-Type` (UTF-8 par
|
|
30
|
+
* défaut) — comme une trame texte RFC 6455 ;
|
|
31
|
+
* - tout autre type, et un corps sans `Content-Type` (RFC 9110 §8.3 : supposé
|
|
32
|
+
* `application/octet-stream`) → les OCTETS : le `Buffer` déjà assemblé dans
|
|
33
|
+
* `request.data`, sans copie — donc un `Uint8Array`, le type d'octets que
|
|
34
|
+
* la socket livrera aussi (#618). Un même schéma passe par les deux portes.
|
|
35
|
+
*
|
|
36
|
+
* Un corps VIDE laisse `body` tel quel : une requête sans charge ne change pas.
|
|
37
|
+
*/
|
|
38
|
+
declare class ParserRaw extends Parser {
|
|
39
|
+
parse(): Promise<this>;
|
|
40
|
+
}
|
|
25
41
|
declare class ParserQs extends Parser {
|
|
26
42
|
parserOptions: QS.IParseOptions;
|
|
27
43
|
charset: BufferEncoding;
|
|
@@ -35,7 +51,7 @@ declare class ParserXml extends Parser {
|
|
|
35
51
|
parse(): Promise<this>;
|
|
36
52
|
}
|
|
37
53
|
/**
|
|
38
|
-
* Parse un corps `application/json` (ou `*+json`) en objet → `
|
|
54
|
+
* Parse un corps `application/json` (ou `*+json`) en objet → `body`
|
|
39
55
|
* (source lue par le décorateur `@Body`). Reprend le rôle de l'ancien plugin
|
|
40
56
|
* `json` de formidable, supprimé avec le passage à busboy (multipart seul).
|
|
41
57
|
* Lenient : un corps JSON malformé est ignoré (pas de throw — `parse()` est
|
|
@@ -62,4 +78,4 @@ type AcceptEntry = {
|
|
|
62
78
|
[key: string]: RegExp | number | string | undefined;
|
|
63
79
|
};
|
|
64
80
|
declare const acceptParser: (acc?: string) => AcceptEntry[];
|
|
65
|
-
export { Parser, ParserXml, ParserQs, ParserJson, acceptParser };
|
|
81
|
+
export { Parser, ParserRaw, ParserXml, ParserQs, ParserJson, acceptParser };
|
|
@@ -5,10 +5,10 @@ import { HTTPMethod } from "../Context.js";
|
|
|
5
5
|
declare class Http2Request extends HttpRequest {
|
|
6
6
|
request: http2.Http2ServerRequest;
|
|
7
7
|
constructor(request: http2.Http2ServerRequest, context: HttpContext);
|
|
8
|
+
/** `:authority`, sinon `Host` — cf `requestAuthority` (RFC 9113 §8.3.1). */
|
|
8
9
|
getHost(): string | undefined;
|
|
9
10
|
getUserAgent(): string | undefined;
|
|
10
11
|
getMethod(): HTTPMethod;
|
|
11
12
|
getRawTarget(): string | undefined;
|
|
12
|
-
protected resolveScheme(): string;
|
|
13
13
|
}
|
|
14
14
|
export default Http2Request;
|
|
@@ -3,6 +3,11 @@ import type { MaybePromise } from "nodefony";
|
|
|
3
3
|
import http from "node:http";
|
|
4
4
|
import HttpContext from "../http/HttpContext.js";
|
|
5
5
|
import HttpResponse from "../http/Response.js";
|
|
6
|
+
/** Ce que la fusion lit d'une réponse Node : ses en-têtes posés, nom par nom. */
|
|
7
|
+
interface IHeaderSource {
|
|
8
|
+
getHeaderNames(): string[];
|
|
9
|
+
getHeader(name: string): http.OutgoingHttpHeader | undefined;
|
|
10
|
+
}
|
|
6
11
|
/**
|
|
7
12
|
* Compose les en-têtes d'une réponse HTTP/2 : ceux posés sur la réponse, puis
|
|
8
13
|
* ceux passés à `writeHead`, qui gagnent.
|
|
@@ -13,12 +18,17 @@ import HttpResponse from "../http/Response.js";
|
|
|
13
18
|
* (`ERR_HTTP2_HEADER_SINGLE_VALUE`), soit une 500 sur tout média servi en
|
|
14
19
|
* HTTP/2. HTTP/2 n'a de toute façon que des noms en minuscules (RFC 9113 §8.2.1).
|
|
15
20
|
*
|
|
21
|
+
* Les en-têtes posés se lisent nom par nom (`getHeaderNames`, déjà en
|
|
22
|
+
* minuscules) : `getHeaders()` les recopiait d'abord dans un objet sans
|
|
23
|
+
* prototype, avant cette fusion — 3,8 µs par réponse pour 12 en-têtes,
|
|
24
|
+
* contre 1,1 µs en une passe.
|
|
25
|
+
*
|
|
16
26
|
* @param statusMessage - message de statut, recopié en `x-status-message`
|
|
17
|
-
* @param current - en-têtes déjà posés
|
|
27
|
+
* @param current - la réponse Node qui porte les en-têtes déjà posés
|
|
18
28
|
* @param extra - en-têtes passés à `writeHead` (objet, ou liste plate clé/valeur)
|
|
19
29
|
* @returns les en-têtes à transmettre à `respond()`
|
|
20
30
|
*/
|
|
21
|
-
export declare function mergeResponseHeaders(statusMessage: string, current:
|
|
31
|
+
export declare function mergeResponseHeaders(statusMessage: string, current: IHeaderSource, extra?: http.OutgoingHttpHeaders | http.OutgoingHttpHeader[]): http.OutgoingHttpHeaders;
|
|
22
32
|
declare class Http2Response extends HttpResponse {
|
|
23
33
|
statusCode: number;
|
|
24
34
|
stream: http2.ServerHttp2Stream | null;
|
|
@@ -27,9 +37,9 @@ declare class Http2Response extends HttpResponse {
|
|
|
27
37
|
isHeaderSent(): boolean;
|
|
28
38
|
writeHead(statusCode?: number, headers?: http.OutgoingHttpHeaders | http.OutgoingHttpHeader[]): void;
|
|
29
39
|
/**
|
|
30
|
-
* Écrit le corps sur le flux HTTP/2
|
|
31
|
-
*
|
|
32
|
-
*
|
|
40
|
+
* Écrit le corps sur le flux HTTP/2 et, pour une réponse unique, le termine
|
|
41
|
+
* d'un seul `end(corps)`. Synchrone, sauf en streaming (`flush()`) : une
|
|
42
|
+
* promesse réglée par le rappel d'écriture, la contre-pression (#505).
|
|
33
43
|
*
|
|
34
44
|
* @returns la réponse, ou sa promesse.
|
|
35
45
|
*/
|
|
@@ -41,6 +51,18 @@ declare class Http2Response extends HttpResponse {
|
|
|
41
51
|
* @throws de façon SYNCHRONE ce que lève la fin du flux.
|
|
42
52
|
*/
|
|
43
53
|
end(chunk?: string | Buffer, encoding?: BufferEncoding): MaybePromise<http.ServerResponse | http2.ServerHttp2Stream>;
|
|
54
|
+
/**
|
|
55
|
+
* Termine la réponse de compatibilité d'un HEAD, dont Node a clos le flux de
|
|
56
|
+
* lui-même au `respond()`.
|
|
57
|
+
*
|
|
58
|
+
* Sans ce geste, `Http2ServerResponse` n'émet ni `finish` ni `close` : le
|
|
59
|
+
* démontage de la requête, branché sur ce `close` (ligne d'accès, profil,
|
|
60
|
+
* hooks d'après-réponse, libération du scope DI), n'a jamais lieu. Les statuts
|
|
61
|
+
* que Node termine aussi d'office (204, 205, 304) émettent leur `close` seuls,
|
|
62
|
+
* et un flux détruit (client parti) aussi : ils ne sont pas touchés. Hors du
|
|
63
|
+
* chemin servi — appelé seulement quand le flux est déjà clos.
|
|
64
|
+
*/
|
|
65
|
+
private settleHeadResponse;
|
|
44
66
|
getStatusMessage(code?: number | string): string;
|
|
45
67
|
getStatus(): {
|
|
46
68
|
code: number;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { IRequestTrace, RequestTraceProtocol } from "../../interfaces/IRequestTrace.js";
|
|
2
|
+
import type Context from "./Context.js";
|
|
3
|
+
/**
|
|
4
|
+
* Nom du `TracingChannel` qui encadre chaque requête HTTP et chaque handshake
|
|
5
|
+
* WebSocket — forme `module.fonction` recommandée par la documentation de
|
|
6
|
+
* `node:diagnostics_channel` ; les sous-canaux sont
|
|
7
|
+
* `tracing:nodefony.http.request:start`, `:end`, `:asyncStart`, `:asyncEnd`,
|
|
8
|
+
* `:error`.
|
|
9
|
+
*/
|
|
10
|
+
export declare const REQUEST_TRACE_CHANNEL = "nodefony.http.request";
|
|
11
|
+
/**
|
|
12
|
+
* Le message tel que Nodefony le remplit au fil de la requête : la vue
|
|
13
|
+
* modifiable d'{@link IRequestTrace}, qui reste en lecture seule pour
|
|
14
|
+
* l'abonné.
|
|
15
|
+
*/
|
|
16
|
+
export type RequestTraceRecord = {
|
|
17
|
+
-readonly [K in keyof IRequestTrace]: IRequestTrace[K];
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Dit si quelqu'un écoute le canal — LA garde du chemin chaud : ~1,6 ns
|
|
21
|
+
* mesurées, aucune allocation. Lue une seule fois, au début de la requête :
|
|
22
|
+
* un abonné arrivé ensuite ne voit que les requêtes suivantes (règle des
|
|
23
|
+
* `TracingChannel`, qui interdit une fin sans début).
|
|
24
|
+
*
|
|
25
|
+
* @returns `true` si au moins un sous-canal a un abonné
|
|
26
|
+
*/
|
|
27
|
+
export declare function hasRequestTraceSubscribers(): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Crée le message de la requête et le range sur son contexte. À n'appeler
|
|
30
|
+
* que sous {@link hasRequestTraceSubscribers} : c'est la seule allocation
|
|
31
|
+
* du canal, et elle n'a lieu que si quelqu'un écoute.
|
|
32
|
+
*
|
|
33
|
+
* @param context - le contexte qui vient d'être construit
|
|
34
|
+
* @param protocol - requête HTTP ou handshake WebSocket
|
|
35
|
+
* @returns le message, de forme fixe, dont les champs de résultat valent `null`
|
|
36
|
+
*/
|
|
37
|
+
export declare function openRequestTrace(context: Context, protocol: RequestTraceProtocol): RequestTraceRecord;
|
|
38
|
+
/**
|
|
39
|
+
* Exécute la partie synchrone du traitement sous `start.runStores` puis
|
|
40
|
+
* publie `end` — la séquence de `TracingChannel.tracePromise`. Les stores
|
|
41
|
+
* liés au canal (`bindStore`, ce que fait un agent APM) restent actifs pour
|
|
42
|
+
* toute la suite asynchrone de la requête.
|
|
43
|
+
*
|
|
44
|
+
* @param trace - le message de la requête
|
|
45
|
+
* @param fn - la fonction qui sert la requête
|
|
46
|
+
* @param thisArg - le receveur de `fn`
|
|
47
|
+
* @param args - les arguments de `fn`
|
|
48
|
+
* @returns ce que rend `fn` — un contexte ou sa promesse
|
|
49
|
+
*/
|
|
50
|
+
export declare function runTraced<ThisArg, Args extends unknown[], Result>(trace: RequestTraceRecord, fn: (this: ThisArg, ...args: Args) => Result, thisArg: ThisArg, ...args: Args): Result;
|
|
51
|
+
/**
|
|
52
|
+
* Termine la trace de la requête : remplit les champs de résultat puis
|
|
53
|
+
* publie `error` (s'il y a une exception), `asyncStart` et `asyncEnd`.
|
|
54
|
+
* Idempotent — le premier appel gagne, les suivants ne font rien : un
|
|
55
|
+
* handshake WebSocket a plusieurs sorties, une seule publie la fin.
|
|
56
|
+
*
|
|
57
|
+
* La durée est celle de l'horloge unique du contexte (`stopClock`), la même
|
|
58
|
+
* que celle de la ligne d'accès : le canal n'en mesure aucune.
|
|
59
|
+
*
|
|
60
|
+
* @param context - le contexte de la requête
|
|
61
|
+
* @param statusCode - le statut rendu au client, `null` s'il n'y en a pas eu
|
|
62
|
+
* @param error - l'exception attrapée par le pipeline, `null` ou `undefined` sinon
|
|
63
|
+
*/
|
|
64
|
+
export declare function closeRequestTrace(context: Context, statusCode: number | null, error: unknown): void;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ce que le chronométrage mesure sur une requête :
|
|
3
|
+
*
|
|
4
|
+
* - `"off"` — rien : ni horloge, ni phases ; la ligne d'accès sort sans durée ;
|
|
5
|
+
* - `"clock"` — la durée seule : une lecture d'horloge à l'entrée, une à la fin ;
|
|
6
|
+
* - `"phases"` — la durée ET le détail par phase (resolve, firewall, action…).
|
|
7
|
+
*/
|
|
8
|
+
export type TimingMode = "off" | "clock" | "phases";
|
|
9
|
+
/**
|
|
10
|
+
* Résout le mode de chronométrage d'une requête depuis `timing.enabled` et
|
|
11
|
+
* l'environnement — la SEULE règle, appelée par le constructeur du `Context`.
|
|
12
|
+
*
|
|
13
|
+
* Défaut (`null`, pas d'avis) : la durée partout, les phases hors production.
|
|
14
|
+
* La durée coûte deux lectures d'horloge, l'équivalent de quelques pourcents
|
|
15
|
+
* de la ligne d'accès qu'elle complète ; les phases coûtent une lecture par
|
|
16
|
+
* phase, un tableau et un index par requête — réservées au développement, ou
|
|
17
|
+
* à un diagnostic à chaud (`true`). `false` coupe tout, durée comprise.
|
|
18
|
+
*
|
|
19
|
+
* @param enabled - valeur de `timing.enabled` (`true`, `false`, `null`)
|
|
20
|
+
* @param environment - environnement normalisé du noyau (`"production"`…)
|
|
21
|
+
* @returns le mode de la requête — une chaîne littérale, rien d'alloué
|
|
22
|
+
*/
|
|
23
|
+
export declare function resolveTimingMode(enabled: unknown, environment: string | undefined): TimingMode;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { AlpnProtocol, ProtocolName, ServerType } from "../../interfaces/IContext.js";
|
|
2
|
+
/**
|
|
3
|
+
* Famille du protocole traité par un contexte, déduite du serveur qui l'a
|
|
4
|
+
* reçu. Règle UNIQUE : `Context` la pose à la construction.
|
|
5
|
+
*
|
|
6
|
+
* @param serverType - identité du serveur (`http`, `https`, `ws`, `wss`)
|
|
7
|
+
* @returns `"websocket"` pour `ws`/`wss`, `"http"` sinon
|
|
8
|
+
*/
|
|
9
|
+
export declare function protocolNameOf(serverType: ServerType): ProtocolName;
|
|
10
|
+
/**
|
|
11
|
+
* Ce que la détection lit d'une requête Node, et rien d'autre : `http.IncomingMessage`
|
|
12
|
+
* (HTTP/1.1) et `http2.Http2ServerRequest` (h2, h2c) le satisfont par leur forme, sans
|
|
13
|
+
* transtypage — et une doublure de test s'écrit en littéral.
|
|
14
|
+
*/
|
|
15
|
+
export interface IAlpnSource {
|
|
16
|
+
readonly httpVersionMajor: number;
|
|
17
|
+
readonly stream?: {
|
|
18
|
+
readonly session?: {
|
|
19
|
+
readonly alpnProtocol?: string | undefined;
|
|
20
|
+
} | undefined;
|
|
21
|
+
} | undefined;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Protocole du saut qui a porté cette requête jusqu'au processus, au sens du
|
|
25
|
+
* registre IANA des identifiants ALPN. Règle UNIQUE : `HttpContext` la lit pour
|
|
26
|
+
* choisir ses classes de requête et de réponse.
|
|
27
|
+
*
|
|
28
|
+
* La valeur se CONSTATE sur la requête, elle ne se déduit pas du serveur : un
|
|
29
|
+
* même serveur TLS reçoit HTTP/1.1 et h2 (`allowHTTP1`). Pour h2, c'est la
|
|
30
|
+
* session de Node qui tranche — `Http2Session.alpnProtocol` vaut `"h2c"` sur
|
|
31
|
+
* une session en clair, l'ALPN négocié (`"h2"`) sur une session TLS.
|
|
32
|
+
*
|
|
33
|
+
* @param request - la requête Node reçue par le serveur
|
|
34
|
+
* @returns `"http/1.1"`, `"h2"` ou `"h2c"`
|
|
35
|
+
*/
|
|
36
|
+
export declare function alpnProtocolOf(request: IAlpnSource): AlpnProtocol;
|
|
37
|
+
/**
|
|
38
|
+
* Autorité (hôte et port) d'une requête, HTTP/1.1 comme HTTP/2. Règle UNIQUE
|
|
39
|
+
* des lecteurs du serveur.
|
|
40
|
+
*
|
|
41
|
+
* `:authority` d'abord, puis `Host` (RFC 9113 §8.3.1). Le repli n'est pas un
|
|
42
|
+
* confort : un intermédiaire qui convertit une requête HTTP/1.1 de forme origine
|
|
43
|
+
* (`GET /chemin`) en HTTP/2 ne DOIT PAS fabriquer `:authority` — il garde
|
|
44
|
+
* `Host`. C'est ce que fait HAProxy `proto h2` vers un port h2c : sans ce repli,
|
|
45
|
+
* l'hôte est introuvable et la requête part en 421.
|
|
46
|
+
*
|
|
47
|
+
* @param headers - en-têtes de la requête Node (`IncomingMessage` ou `Http2ServerRequest`).
|
|
48
|
+
* @returns l'autorité, ou `undefined` si la requête n'en porte aucune.
|
|
49
|
+
*/
|
|
50
|
+
export declare function requestAuthority(headers: {
|
|
51
|
+
host?: string | undefined;
|
|
52
|
+
":authority"?: string | undefined;
|
|
53
|
+
}): string | undefined;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { IRouteResolver } from "../../interfaces/IRouting.js";
|
|
2
|
+
/**
|
|
3
|
+
* Rédige l'avertissement de développement d'une requête restée sans réponse :
|
|
4
|
+
* l'action a rendu une valeur que le framework ne sait pas envoyer, et la
|
|
5
|
+
* requête a attendu son délai d'expiration.
|
|
6
|
+
*
|
|
7
|
+
* Le message nomme l'action fautive, ce qu'elle a rendu, et le geste qui
|
|
8
|
+
* convient à ce cas — `null` est presque toujours une entité absente, qui
|
|
9
|
+
* appelle un 404.
|
|
10
|
+
*
|
|
11
|
+
* @param resolver - le resolver de la requête (route, controller, action,
|
|
12
|
+
* valeur non rendue), `null` si aucune route n'a été résolue.
|
|
13
|
+
* @param url - l'URL de la requête, repli quand aucune route n'est connue.
|
|
14
|
+
* @returns le texte de l'avertissement.
|
|
15
|
+
*/
|
|
16
|
+
export declare function unsentResponseWarning(resolver: IRouteResolver | null | undefined, url: string): string;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { OutgoingHttpHeaders } from "node:http";
|
|
1
2
|
import Cookie from "../../cookies/cookie.js";
|
|
2
3
|
import { Message, Msgid, Pci, Severity } from "nodefony";
|
|
3
4
|
import WebsocketContext from "./WebsocketContext.js";
|
|
@@ -44,8 +45,23 @@ declare class WebsocketResponse {
|
|
|
44
45
|
};
|
|
45
46
|
clean(): void;
|
|
46
47
|
setEncoding(encoding: BufferEncoding): BufferEncoding;
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
/**
|
|
49
|
+
* En-têtes de la réponse au handshake, retenus tant que la socket n'est pas
|
|
50
|
+
* acceptée : ils ne partent que dans un REFUS (`refuseHandshake`) — le `101`
|
|
51
|
+
* de `ws` ne les porte pas. `null` tant que personne n'en pose (le cas
|
|
52
|
+
* nominal : aucune allocation).
|
|
53
|
+
*/
|
|
54
|
+
handshakeHeaders: Record<string, string | number> | null;
|
|
55
|
+
/**
|
|
56
|
+
* Retient un en-tête du handshake (cf {@link handshakeHeaders}) ; sans effet
|
|
57
|
+
* une fois la socket ouverte, où il n'y a plus de réponse HTTP.
|
|
58
|
+
*
|
|
59
|
+
* @param name - nom de l'en-tête
|
|
60
|
+
* @param value - valeur
|
|
61
|
+
* @returns toujours `true`
|
|
62
|
+
*/
|
|
63
|
+
setHeader(name: string, value: number | string | readonly string[]): boolean;
|
|
64
|
+
setHeaders(obj: OutgoingHttpHeaders): boolean;
|
|
49
65
|
addCookie(cookie: Cookie): void;
|
|
50
66
|
setCookies(): void;
|
|
51
67
|
setCookie(_cookie: Cookie): void;
|
|
@@ -4,21 +4,21 @@ import { Severity, Msgid, Message, nodefonyError, Scope } from "nodefony";
|
|
|
4
4
|
import Ws from "ws";
|
|
5
5
|
import type { IncomingMessage } from "node:http";
|
|
6
6
|
import WebsocketResponse from "./Response.js";
|
|
7
|
+
import type { IUpgradeAdmission } from "../../servers/upgradeDispatch.js";
|
|
7
8
|
import { URL } from "node:url";
|
|
8
9
|
import { HTTPMethod } from "../Context.js";
|
|
9
10
|
import HttpError from "../../errors/httpError.js";
|
|
10
11
|
import { FrameProfile } from "../../profiler/FrameProfile.js";
|
|
11
|
-
import type { PhaseName } from "../../../interfaces/IContext.js";
|
|
12
|
+
import type { PhaseName, IWebsocketContext } from "../../../interfaces/IContext.js";
|
|
12
13
|
import { ProxyType } from "../http/HttpContext.js";
|
|
13
14
|
import { type ResolvedProxy } from "../forwarded.js";
|
|
14
15
|
export interface IWsRequestExtension {
|
|
15
16
|
url: URL;
|
|
16
17
|
query: Record<string, string>;
|
|
17
|
-
queryGet: Record<string, string>;
|
|
18
18
|
path: string;
|
|
19
19
|
}
|
|
20
20
|
export type WsIncomingMessage = IncomingMessage & IWsRequestExtension;
|
|
21
|
-
import type { IWebsocketContext as IWebsocketContextInterface } from "../../../interfaces/IContext.js";
|
|
21
|
+
import type { IWebsocketContext as IWebsocketContextInterface, IWebsocketConnectionState } from "../../../interfaces/IContext.js";
|
|
22
22
|
/**
|
|
23
23
|
* Coerce un code (applicatif / HTTP / WS) en code de fermeture WebSocket VALIDE
|
|
24
24
|
* et conforme RFC 6455 §7.4, en PRÉFÉRANT les codes standard §7.4.1 quand le
|
|
@@ -36,7 +36,36 @@ import type { IWebsocketContext as IWebsocketContextInterface } from "../../../i
|
|
|
36
36
|
* @returns un code de fermeture WS valide.
|
|
37
37
|
*/
|
|
38
38
|
export declare function toWsCloseCode(code: number | undefined | null): number;
|
|
39
|
-
|
|
39
|
+
/**
|
|
40
|
+
* Statut HTTP d'un handshake REFUSÉ avant le `101` (RFC 6455 §4.2.2) — la
|
|
41
|
+
* réciproque de {@link toWsCloseCode} : une erreur levée pendant la décision
|
|
42
|
+
* porte un code HTTP (pare-feu, garde) ou, par héritage, un code de fermeture
|
|
43
|
+
* WS (origine `1008`, protocole `1002`, introuvable `4004`) ; le client qui
|
|
44
|
+
* lit le statut doit y reconnaître le même sens.
|
|
45
|
+
*
|
|
46
|
+
* @param code - code porté par l'erreur
|
|
47
|
+
* @returns un statut HTTP 4xx/5xx
|
|
48
|
+
*/
|
|
49
|
+
export declare function toHandshakeStatus(code: number | undefined | null): number;
|
|
50
|
+
/**
|
|
51
|
+
* Statut HTTP que reçoit le client d'un handshake refusé — ou `null` si la
|
|
52
|
+
* connexion était déjà acceptée.
|
|
53
|
+
*
|
|
54
|
+
* La ligne de journal d'un refus s'écrit AVANT `refuseHandshake` : sans ce
|
|
55
|
+
* calcul, elle affichait le code de fermeture par défaut (`1000`) en ERROR,
|
|
56
|
+
* pour un `400` que le client reçoit. Même conversion que le refus
|
|
57
|
+
* ({@link toHandshakeStatus}), donc même chiffre. Chemin d'ERREUR seulement :
|
|
58
|
+
* 1 comparaison et 1 `typeof`, aucune allocation.
|
|
59
|
+
*
|
|
60
|
+
* @param context - le contexte WS de la ligne
|
|
61
|
+
* @param error - l'erreur qui a interrompu le handshake
|
|
62
|
+
* @returns le statut HTTP du refus, ou `null` après l'acceptation
|
|
63
|
+
*/
|
|
64
|
+
export declare function handshakeRefusalStatus(context: IWebsocketContext, error: Error): number | null;
|
|
65
|
+
interface WebsocketContext extends IWebsocketConnectionState {
|
|
66
|
+
}
|
|
67
|
+
export { WebsocketContext as default };
|
|
68
|
+
declare class WebsocketContext extends Context implements IWebsocketContextInterface {
|
|
40
69
|
#private;
|
|
41
70
|
request: WsIncomingMessage | null;
|
|
42
71
|
response: WebsocketResponse | null;
|
|
@@ -45,16 +74,37 @@ export default class WebsocketContext extends Context implements IWebsocketConte
|
|
|
45
74
|
rejected: boolean;
|
|
46
75
|
teardownWired: boolean;
|
|
47
76
|
connection: Ws | null;
|
|
77
|
+
/**
|
|
78
|
+
* Le verdict du handshake encore à rendre : posé par le noyau tant que le
|
|
79
|
+
* pipeline décide (pare-feu, garde de l'action), `null` dès que la socket
|
|
80
|
+
* est acceptée ou refusée. Tant qu'il est là, AUCUN `101` n'est parti.
|
|
81
|
+
*/
|
|
82
|
+
admission: IUpgradeAdmission | null;
|
|
48
83
|
origin: string;
|
|
49
84
|
proxy: ProxyType | null;
|
|
50
85
|
forwarded: ResolvedProxy | null;
|
|
51
86
|
wsUrl: URL | null;
|
|
52
|
-
|
|
53
|
-
queryRequest: Record<string, string>;
|
|
87
|
+
query: Record<string, string>;
|
|
54
88
|
wsPath: string;
|
|
55
|
-
constructor(scope: Scope, req: IncomingMessage, ws: Ws,
|
|
56
|
-
log(pci: unknown, severity?: Severity, msgid?: Msgid, msg?: Message): import("nodefony").Pdu;
|
|
89
|
+
constructor(scope: Scope, req: IncomingMessage, ws: Ws | null, serverType: ServerType);
|
|
90
|
+
log(pci: unknown, severity?: Severity, msgid?: Msgid, msg?: Message, moduleName?: string): import("nodefony").Pdu;
|
|
57
91
|
logRequest(httpError?: Error | HttpError | nodefonyError | null, acceptedProtocol?: string | null): import("nodefony").Pdu | undefined;
|
|
92
|
+
/**
|
|
93
|
+
* Rattache la socket que le handshake vient d'ouvrir (`101` envoyé) : le
|
|
94
|
+
* contexte a été construit et jugé AVANT, sans elle.
|
|
95
|
+
*
|
|
96
|
+
* @param ws - la socket acceptée
|
|
97
|
+
*/
|
|
98
|
+
acceptConnection(ws: Ws): void;
|
|
99
|
+
/**
|
|
100
|
+
* Refuse le handshake par un statut HTTP, avant tout `101` (RFC 6455
|
|
101
|
+
* §4.2.2) — avec les en-têtes posés pendant la décision (`WWW-Authenticate`
|
|
102
|
+
* du pare-feu, `Retry-After`).
|
|
103
|
+
*
|
|
104
|
+
* @param status - statut HTTP du refus
|
|
105
|
+
* @returns `false` si le verdict était déjà rendu (rien n'est écrit)
|
|
106
|
+
*/
|
|
107
|
+
refuseHandshake(status: number): boolean;
|
|
58
108
|
connect(): Promise<Ws>;
|
|
59
109
|
handle(data?: unknown[]): Promise<this>;
|
|
60
110
|
render(chunk: unknown, encoding?: BufferEncoding): Promise<WebsocketResponse>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ContextType } from "../../../service/http-kernel.js";
|
|
2
|
+
import type WebsocketContext from "./WebsocketContext.js";
|
|
3
|
+
/**
|
|
4
|
+
* Dit si un contexte sert une connexion WebSocket — la garde qui rétrécit
|
|
5
|
+
* `ContextType` à `WebsocketContext` sans conversion.
|
|
6
|
+
*
|
|
7
|
+
* Elle lit la famille (`type`) posée à la construction du contexte, jamais
|
|
8
|
+
* `instanceof` : deux copies installées de `@nodefony/http` font échouer
|
|
9
|
+
* `instanceof` en silence, quand ce champ dit vrai pour les deux. Deux
|
|
10
|
+
* Une comparaison de chaînes, aucune allocation.
|
|
11
|
+
*
|
|
12
|
+
* @param context - le contexte à éprouver.
|
|
13
|
+
* @returns `true` si le contexte traite une connexion WebSocket (`ws` ou `wss`).
|
|
14
|
+
*/
|
|
15
|
+
export declare function isWebsocketContext(context: ContextType): context is WebsocketContext;
|