@nodefony/http 10.0.0-alpha.9 → 10.0.0-beta.1
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 +7 -0
- package/dist/index.js +4 -2
- package/dist/nodefony/command/assetsPublishCommand.js +1 -1
- package/dist/nodefony/command/certificatesCommand.js +1 -1
- package/dist/nodefony/command/networkCommand.js +1 -1
- package/dist/nodefony/command/proxyGenerateCommand.js +1 -1
- package/dist/nodefony/config/config.js +22 -1
- package/dist/nodefony/interfaces/IRouting.js +1 -0
- package/dist/nodefony/interfaces/ISecurity.js +1 -0
- package/dist/nodefony/service/ProfilerAdminApi.js +2 -1
- package/dist/nodefony/service/audit-logger.js +16 -13
- package/dist/nodefony/service/certificates.js +25 -13
- package/dist/nodefony/service/error-renderer.js +11 -8
- package/dist/nodefony/service/http-kernel.js +340 -163
- package/dist/nodefony/service/pretty-request-logger.js +3 -10
- package/dist/nodefony/service/servers/clientError.js +2 -2
- package/dist/nodefony/service/servers/server-http.js +20 -20
- package/dist/nodefony/service/servers/server-https.js +38 -39
- package/dist/nodefony/service/servers/server-static.js +13 -17
- package/dist/nodefony/service/servers/server-websocket-secure.js +8 -7
- package/dist/nodefony/service/servers/server-websocket.js +8 -7
- package/dist/nodefony/service/servers/wsHeartbeat.js +2 -2
- package/dist/nodefony/service/sessions/sessions-service.js +19 -12
- package/dist/nodefony/service/trace.js +1 -1
- package/dist/nodefony/service/upload/upload-service.js +8 -7
- package/dist/nodefony/src/assets/prebuiltUi.js +1 -1
- package/dist/nodefony/src/context/Context.js +34 -19
- package/dist/nodefony/src/context/forwarded.js +4 -4
- package/dist/nodefony/src/context/http/HttpContext.js +133 -88
- package/dist/nodefony/src/context/http/Request.js +93 -44
- package/dist/nodefony/src/context/http/Response.js +107 -51
- package/dist/nodefony/src/context/http/parser.js +9 -10
- package/dist/nodefony/src/context/http/urlFastPath.js +1 -2
- package/dist/nodefony/src/context/http2/Response.js +53 -25
- package/dist/nodefony/src/context/metaData.js +1 -1
- package/dist/nodefony/src/context/trustProxy.js +4 -3
- package/dist/nodefony/src/context/websocket/Response.js +13 -6
- package/dist/nodefony/src/context/websocket/WebsocketContext.js +59 -26
- package/dist/nodefony/src/context/websocket/wsCloseReason.js +41 -0
- package/dist/nodefony/src/cookies/cookie.js +3 -3
- package/dist/nodefony/src/errors/httpError.js +6 -6
- package/dist/nodefony/src/errors/thrownMessage.js +27 -0
- package/dist/nodefony/src/profiler/FrameProfile.js +4 -3
- package/dist/nodefony/src/profiler/Profiler.js +4 -4
- package/dist/nodefony/src/servers/kernelServers.js +29 -0
- package/dist/nodefony/src/servers/portBinder.js +8 -21
- package/dist/nodefony/src/session/session.js +18 -10
- package/dist/nodefony/src/session/sessionStoreFailure.js +2 -1
- package/dist/nodefony/src/session/storage/RevocationGuardStorage.js +24 -9
- package/dist/types/index.d.ts +16 -0
- package/dist/types/nodefony/config/config.d.ts +22 -0
- package/dist/types/nodefony/interfaces/IContext.d.ts +24 -7
- package/dist/types/nodefony/interfaces/ICookie.d.ts +18 -18
- package/dist/types/nodefony/interfaces/IHttpKernel.d.ts +10 -4
- package/dist/types/nodefony/interfaces/IResponse.d.ts +1 -1
- package/dist/types/nodefony/interfaces/IRouting.d.ts +87 -0
- package/dist/types/nodefony/interfaces/ISecurity.d.ts +45 -0
- package/dist/types/nodefony/interfaces/ISession.d.ts +8 -8
- package/dist/types/nodefony/interfaces/IUpload.d.ts +9 -9
- package/dist/types/nodefony/interfaces/index.d.ts +2 -0
- package/dist/types/nodefony/service/audit-logger.d.ts +14 -10
- package/dist/types/nodefony/service/certificates.d.ts +10 -10
- package/dist/types/nodefony/service/http-kernel.d.ts +192 -42
- package/dist/types/nodefony/service/servers/server-http.d.ts +4 -1
- package/dist/types/nodefony/service/servers/server-https.d.ts +4 -1
- package/dist/types/nodefony/service/servers/server-websocket-secure.d.ts +4 -1
- package/dist/types/nodefony/service/servers/server-websocket.d.ts +4 -1
- package/dist/types/nodefony/service/sessions/sessions-service.d.ts +6 -2
- package/dist/types/nodefony/service/upload/upload-service.d.ts +1 -1
- package/dist/types/nodefony/src/context/Context.d.ts +21 -10
- package/dist/types/nodefony/src/context/forwarded.d.ts +3 -3
- package/dist/types/nodefony/src/context/http/HttpContext.d.ts +75 -18
- package/dist/types/nodefony/src/context/http/Request.d.ts +39 -2
- package/dist/types/nodefony/src/context/http/Response.d.ts +47 -7
- package/dist/types/nodefony/src/context/http/parser.d.ts +1 -1
- package/dist/types/nodefony/src/context/http2/Response.d.ts +32 -2
- package/dist/types/nodefony/src/context/websocket/Response.d.ts +4 -4
- package/dist/types/nodefony/src/context/websocket/WebsocketContext.d.ts +11 -2
- package/dist/types/nodefony/src/context/websocket/wsCloseReason.d.ts +21 -0
- package/dist/types/nodefony/src/cookies/cookie.d.ts +9 -9
- package/dist/types/nodefony/src/errors/httpError.d.ts +7 -7
- package/dist/types/nodefony/src/errors/thrownMessage.d.ts +12 -0
- package/dist/types/nodefony/src/profiler/Profiler.d.ts +28 -28
- package/dist/types/nodefony/src/servers/kernelServers.d.ts +20 -0
- package/dist/types/nodefony/src/servers/portBinder.d.ts +6 -19
- package/dist/types/nodefony/src/session/session.d.ts +12 -12
- package/dist/types/nodefony/src/session/storage/RevocationGuardStorage.d.ts +1 -1
- package/docs/cookies.md +20 -20
- package/docs/observabilite.md +25 -25
- package/docs/rate-limit.md +18 -18
- package/docs/servers.md +52 -52
- package/docs/session.md +65 -60
- package/docs/upload.md +36 -30
- package/package.json +7 -7
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Severity } from "nodefony";
|
|
2
2
|
import type { IRequestLogger, IRequestLogEntry } from "../interfaces/IRequestLogger.js";
|
|
3
|
-
import type { IHttpContext, IWebsocketContext } from "../interfaces/IContext.js";
|
|
3
|
+
import type { IHttpContext, IWebsocketContext, PhaseTiming } from "../interfaces/IContext.js";
|
|
4
4
|
/**
|
|
5
5
|
* Canonical audit log entry — 1 JSON PDU per request (P3.1).
|
|
6
6
|
*
|
|
@@ -32,9 +32,9 @@ export interface AuditLogEntry {
|
|
|
32
32
|
phases?: {
|
|
33
33
|
name: string;
|
|
34
34
|
durationMs: number | null;
|
|
35
|
-
}[];
|
|
36
|
-
error?: AuditErrorEntry;
|
|
37
|
-
protocol?: string | null;
|
|
35
|
+
}[] | undefined;
|
|
36
|
+
error?: AuditErrorEntry | undefined;
|
|
37
|
+
protocol?: string | null | undefined;
|
|
38
38
|
}
|
|
39
39
|
/**
|
|
40
40
|
* Enriched error description for audit logs (P3.5).
|
|
@@ -51,6 +51,15 @@ export interface AuditErrorEntry {
|
|
|
51
51
|
/** Recursive — capped to depth 5 to avoid pathological cycles. */
|
|
52
52
|
cause?: AuditErrorEntry;
|
|
53
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* Durée totale de la requête, de la première phase (`startMs`) à maintenant.
|
|
56
|
+
* `null` si le chronométrage est coupé (aucune phase enregistrée). Partagée
|
|
57
|
+
* par le journal d'audit et le journal lisible — une seule définition.
|
|
58
|
+
*
|
|
59
|
+
* @param phases - phases chronométrées du contexte
|
|
60
|
+
* @returns durée en millisecondes, ou `null`
|
|
61
|
+
*/
|
|
62
|
+
declare function computeDurationMs(phases: PhaseTiming[]): number | null;
|
|
54
63
|
/**
|
|
55
64
|
* Severity derived from HTTP status code — RFC 9110 categories.
|
|
56
65
|
* 1xx/2xx/3xx → INFO ; 4xx → WARNING ; 5xx → ERROR.
|
|
@@ -127,11 +136,6 @@ declare class JsonAuditLogger implements IRequestLogger {
|
|
|
127
136
|
shouldSample(context: IHttpContext, error?: Error | null): boolean;
|
|
128
137
|
renderHttp(context: IHttpContext, error?: Error | null): IRequestLogEntry;
|
|
129
138
|
renderWebsocket(context: IWebsocketContext, error?: Error | null, acceptedProtocol?: string | null): IRequestLogEntry;
|
|
130
|
-
/**
|
|
131
|
-
* Total request duration computed from the first phase startMs to now.
|
|
132
|
-
* Returns null if timing is disabled (no phases recorded).
|
|
133
|
-
*/
|
|
134
|
-
private computeDurationMs;
|
|
135
139
|
/**
|
|
136
140
|
* Serialise an Error (recursively for `cause` chain) into an AuditErrorEntry.
|
|
137
141
|
* Stack is included only when `includeStack === true` (dev default).
|
|
@@ -140,4 +144,4 @@ declare class JsonAuditLogger implements IRequestLogger {
|
|
|
140
144
|
private serializeError;
|
|
141
145
|
}
|
|
142
146
|
export default JsonAuditLogger;
|
|
143
|
-
export { severityFromStatus };
|
|
147
|
+
export { computeDurationMs, severityFromStatus };
|
|
@@ -56,19 +56,19 @@ export interface CertificateOptions {
|
|
|
56
56
|
* est un confort de DÉVELOPPEMENT — en production, fournir un vrai certificat
|
|
57
57
|
* (Let's Encrypt, ingress, reverse-proxy) : Nodefony n'est pas une CA.
|
|
58
58
|
*/
|
|
59
|
-
strategy?: CertStrategyConfig;
|
|
59
|
+
strategy?: CertStrategyConfig | undefined;
|
|
60
60
|
selfSigned: SelfSignedOptions;
|
|
61
61
|
dev: CertificateDevOptions;
|
|
62
|
-
san?: CertificateSanOptions;
|
|
62
|
+
san?: CertificateSanOptions | undefined;
|
|
63
63
|
/** Permissions POSIX de la clé privée écrite (0600 = owner-only). */
|
|
64
|
-
privateKeyMode?: number;
|
|
65
|
-
path?: string;
|
|
66
|
-
keyPath?: string;
|
|
67
|
-
certPath?: string;
|
|
68
|
-
caPath?: string;
|
|
69
|
-
key?: string | Buffer;
|
|
70
|
-
cert?: string | Buffer;
|
|
71
|
-
ca?: string | Buffer;
|
|
64
|
+
privateKeyMode?: number | undefined;
|
|
65
|
+
path?: string | undefined;
|
|
66
|
+
keyPath?: string | undefined;
|
|
67
|
+
certPath?: string | undefined;
|
|
68
|
+
caPath?: string | undefined;
|
|
69
|
+
key?: string | Buffer | undefined;
|
|
70
|
+
cert?: string | Buffer | undefined;
|
|
71
|
+
ca?: string | Buffer | undefined;
|
|
72
72
|
}
|
|
73
73
|
interface filesCertType {
|
|
74
74
|
path: string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Service, Module, Scope, EnvironmentType, DebugType, nodefonyError } from "nodefony";
|
|
2
|
-
import type {
|
|
3
|
-
import type {
|
|
2
|
+
import type { MaybePromise } from "nodefony";
|
|
3
|
+
import type { IRouteResolver, IRequestRouter } from "../interfaces/IRouting.js";
|
|
4
4
|
import HttpError from "../src/errors/httpError.js";
|
|
5
5
|
import { type TrustProxyChecker } from "../src/context/trustProxy.js";
|
|
6
6
|
import type { IRateLimitStore } from "../src/rateLimit/IRateLimitStore.js";
|
|
@@ -20,9 +20,11 @@ import Context, { HTTPMethod, WebSocketState } from "../src/context/Context.js";
|
|
|
20
20
|
import Certicates from "./certificates.js";
|
|
21
21
|
import SessionsService from "./sessions/sessions-service.js";
|
|
22
22
|
import Session from "../src/session/session.js";
|
|
23
|
-
import {
|
|
23
|
+
import type { IFirewallGate } from "../interfaces/ISecurity.js";
|
|
24
24
|
import type { IErrorRenderer } from "../interfaces/IErrorRenderer.js";
|
|
25
25
|
import type { IRequestLogger } from "../interfaces/IRequestLogger.js";
|
|
26
|
+
import type { DefaultOptionsService } from "nodefony";
|
|
27
|
+
import type { IHttpConfig } from "../config/config.js";
|
|
26
28
|
export type ProtocolType = "1.1" | "2.0" | "3.0";
|
|
27
29
|
export type httpRequest = http.IncomingMessage | http2.Http2ServerRequest;
|
|
28
30
|
export type httpResponse = http.ServerResponse | http2.Http2ServerResponse;
|
|
@@ -31,11 +33,11 @@ export type ServerType = "http" | "https" | "http2" | "http3" | "websocket" | "w
|
|
|
31
33
|
export type responseTimeoutType = "http" | "https" | "http2" | "http3";
|
|
32
34
|
export type SchemeType = "http" | "https" | "ws" | "wss";
|
|
33
35
|
export interface WsMetaData {
|
|
34
|
-
type?: "message" | "handshake";
|
|
35
|
-
state?: WebSocketState;
|
|
36
|
-
messageType?: "utf8" | "binary";
|
|
37
|
-
protocol?: string | null;
|
|
38
|
-
id?: string;
|
|
36
|
+
type?: "message" | "handshake" | undefined;
|
|
37
|
+
state?: WebSocketState | undefined;
|
|
38
|
+
messageType?: "utf8" | "binary" | undefined;
|
|
39
|
+
protocol?: string | null | undefined;
|
|
40
|
+
id?: string | undefined;
|
|
39
41
|
}
|
|
40
42
|
/**
|
|
41
43
|
* Vue minimale d'une route exposée dans l'enveloppe `nodefony.route` (frame WS
|
|
@@ -46,32 +48,33 @@ export interface WsMetaData {
|
|
|
46
48
|
*/
|
|
47
49
|
export interface RouteMetaData {
|
|
48
50
|
name: string;
|
|
49
|
-
path?: string;
|
|
51
|
+
path?: string | undefined;
|
|
50
52
|
variablesMap: Record<string, unknown>;
|
|
51
53
|
}
|
|
52
54
|
export interface MetaData {
|
|
53
|
-
name?: string;
|
|
54
|
-
version?: string;
|
|
55
|
-
url?: URL | string;
|
|
56
|
-
environment?: EnvironmentType;
|
|
57
|
-
debug?: DebugType;
|
|
58
|
-
token?: string;
|
|
59
|
-
method?: HTTPMethod;
|
|
60
|
-
scheme?: SchemeType;
|
|
61
|
-
requestId?: string;
|
|
62
|
-
websocket?: WsMetaData;
|
|
63
|
-
route?: RouteMetaData;
|
|
55
|
+
name?: string | undefined;
|
|
56
|
+
version?: string | undefined;
|
|
57
|
+
url?: URL | string | undefined;
|
|
58
|
+
environment?: EnvironmentType | undefined;
|
|
59
|
+
debug?: DebugType | undefined;
|
|
60
|
+
token?: string | undefined;
|
|
61
|
+
method?: HTTPMethod | undefined;
|
|
62
|
+
scheme?: SchemeType | undefined;
|
|
63
|
+
requestId?: string | undefined;
|
|
64
|
+
websocket?: WsMetaData | undefined;
|
|
65
|
+
route?: RouteMetaData | undefined;
|
|
64
66
|
}
|
|
65
67
|
export interface Data {
|
|
66
|
-
error?: Error;
|
|
68
|
+
error?: Error | undefined;
|
|
67
69
|
nodefony: MetaData;
|
|
68
70
|
message?: unknown;
|
|
69
|
-
code?: number;
|
|
71
|
+
code?: number | undefined;
|
|
70
72
|
result: unknown;
|
|
71
73
|
}
|
|
72
74
|
import type { IHttpKernel as IHttpKernelInterface } from "../interfaces/IHttpKernel.js";
|
|
73
75
|
declare class HttpKernel extends Service implements IHttpKernelInterface {
|
|
74
76
|
#private;
|
|
77
|
+
options: IHttpConfig & DefaultOptionsService;
|
|
75
78
|
certificates: unknown;
|
|
76
79
|
serviceCerticats: Certicates | null;
|
|
77
80
|
key: string;
|
|
@@ -79,12 +82,12 @@ declare class HttpKernel extends Service implements IHttpKernelInterface {
|
|
|
79
82
|
ca: string;
|
|
80
83
|
serverStatic: Statics | null;
|
|
81
84
|
domain: string;
|
|
82
|
-
trustedHosts?: ITrustedHostsConfig;
|
|
85
|
+
trustedHosts?: ITrustedHostsConfig | undefined;
|
|
83
86
|
domainCheck: boolean;
|
|
84
87
|
regAlias: RegExp[];
|
|
85
88
|
module: Module;
|
|
86
|
-
httpsPort?: number;
|
|
87
|
-
httpPort?: number;
|
|
89
|
+
httpsPort?: number | undefined;
|
|
90
|
+
httpPort?: number | undefined;
|
|
88
91
|
responseTimeout: {
|
|
89
92
|
http: number;
|
|
90
93
|
https: number;
|
|
@@ -98,9 +101,9 @@ declare class HttpKernel extends Service implements IHttpKernelInterface {
|
|
|
98
101
|
private secContentTypeOptions;
|
|
99
102
|
private secFrameOptions;
|
|
100
103
|
private secHsts;
|
|
101
|
-
sessionService?: SessionsService | null;
|
|
102
|
-
router?:
|
|
103
|
-
firewall?:
|
|
104
|
+
sessionService?: SessionsService | null | undefined;
|
|
105
|
+
router?: IRequestRouter | null | undefined;
|
|
106
|
+
firewall?: IFirewallGate | null | undefined;
|
|
104
107
|
private errorRenderer;
|
|
105
108
|
private requestLogger;
|
|
106
109
|
private profiler;
|
|
@@ -225,7 +228,17 @@ declare class HttpKernel extends Service implements IHttpKernelInterface {
|
|
|
225
228
|
* gagne toujours sur la config (idempotent — last setter wins).
|
|
226
229
|
*/
|
|
227
230
|
private applyRequestLoggerFromConfig;
|
|
228
|
-
|
|
231
|
+
/**
|
|
232
|
+
* Ouvre le scope `request` de la requête et la confie au pipeline HTTP.
|
|
233
|
+
*
|
|
234
|
+
* Pas `async` : rien n'y est attendu — la fonction rend directement ce que
|
|
235
|
+
* rend le pipeline, sans l'envelopper dans une promesse (#505).
|
|
236
|
+
*
|
|
237
|
+
* @returns le contexte servi, ou sa promesse (celle de {@link handleHttp}).
|
|
238
|
+
* @throws de façon synchrone si le scope `request` n'est pas déclaré — faute
|
|
239
|
+
* de configuration ; l'appelant ({@link onHttpRequest}) l'absorbe.
|
|
240
|
+
*/
|
|
241
|
+
handle(request: httpRequest, response: httpResponse | null, type: ServerType): MaybePromise<HttpContext>;
|
|
229
242
|
/**
|
|
230
243
|
* Arme le contexte pour l'exécution : zone firewall (`context.secure`) et
|
|
231
244
|
* résolveur de route — **sans instancier le controller**.
|
|
@@ -237,13 +250,16 @@ declare class HttpKernel extends Service implements IHttpKernelInterface {
|
|
|
237
250
|
* elle, exécute du code utilisateur (`initialize()`) et résout des dépendances
|
|
238
251
|
* DI : elle n'a rien à faire avant que la requête soit autorisée.
|
|
239
252
|
*
|
|
253
|
+
* Synchrone : le match est un calcul pur, rien n'y est attendu (#505).
|
|
254
|
+
*
|
|
240
255
|
* @param checkFirewall - calcule `context.secure` (zone protégée) ; laissé à
|
|
241
256
|
* `false` par un appelant qui l'a déjà tranché.
|
|
257
|
+
* @returns le résolveur de la route, posé sur `context.resolver`.
|
|
242
258
|
* @throws HttpError 404 quand aucune route ne matche, ou l'exception portée
|
|
243
|
-
* par le résolveur (405…) —
|
|
244
|
-
* route inexistante ne devient pas un 401.
|
|
259
|
+
* par le résolveur (405…) — de façon SYNCHRONE, et toujours avant le
|
|
260
|
+
* firewall : une route inexistante ne devient pas un 401.
|
|
245
261
|
*/
|
|
246
|
-
prepareFrontController(context: ContextType, checkFirewall?: boolean):
|
|
262
|
+
prepareFrontController(context: ContextType, checkFirewall?: boolean): IRouteResolver;
|
|
247
263
|
/**
|
|
248
264
|
* {@link prepareFrontController} + instanciation immédiate du controller
|
|
249
265
|
* (DI + hook `initialize()`).
|
|
@@ -254,7 +270,7 @@ declare class HttpKernel extends Service implements IHttpKernelInterface {
|
|
|
254
270
|
* est trop tard. Le HTTP, lui, arme la route ici et laisse
|
|
255
271
|
* `Resolver.executeAction` créer l'instance une fois la requête autorisée.
|
|
256
272
|
*/
|
|
257
|
-
handleFrontController(context: ContextType, checkFirewall?: boolean): Promise<
|
|
273
|
+
handleFrontController(context: ContextType, checkFirewall?: boolean): Promise<object>;
|
|
258
274
|
/**
|
|
259
275
|
* Handles errors by setting appropriate response codes and messages.
|
|
260
276
|
*
|
|
@@ -300,7 +316,29 @@ declare class HttpKernel extends Service implements IHttpKernelInterface {
|
|
|
300
316
|
getRequestLogger(): IRequestLogger;
|
|
301
317
|
onError(error: Error | HttpError | nodefonyError, context?: ContextType, _extraHeaders?: Record<string, unknown> | object): Promise<HttpContext | WebsocketContext>;
|
|
302
318
|
compileAlias(): RegExp[];
|
|
303
|
-
|
|
319
|
+
/**
|
|
320
|
+
* Point d'entrée d'une requête HTTP, appelé par les serveurs depuis leur
|
|
321
|
+
* écouteur `request`.
|
|
322
|
+
*
|
|
323
|
+
* Ne lève et ne rejette JAMAIS : dans un écouteur d'événement, une exception
|
|
324
|
+
* deviendrait une erreur non capturée du processus. Ce qui échappe au
|
|
325
|
+
* pipeline a déjà été traité — rendu au client par `onError`, qui journalise
|
|
326
|
+
* en CRITIC l'échec de son propre rendu avant de le relever. C'était le
|
|
327
|
+
* `.catch` vide des serveurs ; il vit désormais ici, une seule fois.
|
|
328
|
+
*
|
|
329
|
+
* @returns `undefined` quand la requête est servie sans pipeline (sonde de
|
|
330
|
+
* santé, 429) ; sinon la promesse de la fin du pipeline — elle ne rejette
|
|
331
|
+
* pas, et personne n'est tenu de l'attendre.
|
|
332
|
+
*/
|
|
333
|
+
onHttpRequest(request: httpRequest, response: httpResponse, type: ServerType): Promise<void> | undefined;
|
|
334
|
+
/**
|
|
335
|
+
* Le travail d'{@link onHttpRequest} : en-têtes de transport, sondes de
|
|
336
|
+
* santé, rate-limit, hook `onServerRequest`, puis le pipeline.
|
|
337
|
+
*
|
|
338
|
+
* @returns `undefined` quand la requête est servie ici ; sinon la promesse du
|
|
339
|
+
* pipeline.
|
|
340
|
+
*/
|
|
341
|
+
private dispatchHttpRequest;
|
|
304
342
|
initServers(): Promise<(httpServer | httpsServer | websocketServer | websocketSecureServer)[]>;
|
|
305
343
|
/**
|
|
306
344
|
* Publie les ports EFFECTIFS du runtime (state file), une fois l'écoute établie.
|
|
@@ -342,24 +380,136 @@ declare class HttpKernel extends Service implements IHttpKernelInterface {
|
|
|
342
380
|
* session (lazy). Remplace l'ancien `sessionAutoStart` global « démarre sur
|
|
343
381
|
* toutes les routes » (le moteur du ×23).
|
|
344
382
|
*
|
|
383
|
+
* Le contrat de retour suit ce qui est réellement attendu : `null` tout de
|
|
384
|
+
* suite quand aucune session n'est à ouvrir (le cas nominal — ni intent, ni
|
|
385
|
+
* cookie), la promesse de la session seulement quand le store est interrogé
|
|
386
|
+
* (#505).
|
|
387
|
+
*
|
|
345
388
|
* @param context - contexte HTTP/HTTP2/WS courant.
|
|
346
|
-
* @returns
|
|
389
|
+
* @returns `null` si la requête n'en requiert aucune ; sinon la promesse de
|
|
390
|
+
* la session active.
|
|
347
391
|
*/
|
|
348
|
-
startSession(context: WebsocketContext | HttpContext):
|
|
392
|
+
startSession(context: WebsocketContext | HttpContext): MaybePromise<Session | null>;
|
|
349
393
|
/**
|
|
350
|
-
* Teardown post-réponse — fire-and-forget depuis
|
|
351
|
-
* `createHttpContext
|
|
352
|
-
*
|
|
394
|
+
* Teardown post-réponse — fire-and-forget depuis l'écouteur `close` posé par
|
|
395
|
+
* `createHttpContext`. Loggue la requête, draine les hooks
|
|
396
|
+
* afterResponse/onFinish, libère le scope DI.
|
|
397
|
+
*
|
|
398
|
+
* Synchrone dans le cas nominal — aucun hook `onAfterResponse`, aucun écouteur
|
|
399
|
+
* `onFinish` : rien n'y est attendu (#505). Ne lève et ne rejette jamais —
|
|
400
|
+
* l'appelant est un écouteur d'événement : une faute est journalisée, et le
|
|
401
|
+
* scope TOUJOURS libéré.
|
|
402
|
+
*
|
|
403
|
+
* @returns `undefined` quand le démontage est terminé ; sinon la promesse de
|
|
404
|
+
* sa fin, qui ne rejette pas.
|
|
353
405
|
*/
|
|
354
406
|
private teardownHttp;
|
|
407
|
+
/**
|
|
408
|
+
* Suite ASYNCHRONE du démontage, quand il y a des hooks à attendre :
|
|
409
|
+
* `onAfterResponse` puis `onFinish`, avant de libérer le contexte. Ne rejette
|
|
410
|
+
* jamais (cf {@link teardownHttp}).
|
|
411
|
+
*/
|
|
412
|
+
private finishTeardownHttp;
|
|
413
|
+
/** Fin d'un contexte HTTP : marqué fini, scope DI libéré, contexte nettoyé. */
|
|
414
|
+
private releaseHttpContext;
|
|
415
|
+
/**
|
|
416
|
+
* R2 — le démontage est fire-and-forget : un throw (hook onFinish /
|
|
417
|
+
* afterResponse) serait une erreur non capturée du processus. On journalise,
|
|
418
|
+
* et on GARANTIT la libération du scope DI (sinon le scope `request` fuit à
|
|
419
|
+
* chaque hook qui lève).
|
|
420
|
+
*/
|
|
421
|
+
private failTeardownHttp;
|
|
355
422
|
createHttpContext(scope: Scope, request: httpRequest, response: httpResponse, type: ServerType): HttpContext;
|
|
356
|
-
|
|
357
|
-
|
|
423
|
+
/**
|
|
424
|
+
* Crée le contexte HTTP de la requête et la sert dans sa bulle ALS ; toute
|
|
425
|
+
* erreur — synchrone ou rejet — passe par {@link onError}.
|
|
426
|
+
*
|
|
427
|
+
* Pas `async` : un GET servi par une action synchrone traverse le pipeline
|
|
428
|
+
* sans une seule promesse (#505). Une promesse seulement quand une étape
|
|
429
|
+
* attend, ou quand l'erreur est rendue (`onError`).
|
|
430
|
+
*
|
|
431
|
+
* @returns le contexte servi, ou sa promesse.
|
|
432
|
+
*/
|
|
433
|
+
handleHttp(scope: Scope, request: httpRequest, response: httpResponse, type: ServerType): MaybePromise<HttpContext>;
|
|
434
|
+
/**
|
|
435
|
+
* Traceparent, tampon du profileur, puis le pipeline dans la bulle ALS de la
|
|
436
|
+
* requête (`RequestContext.run`).
|
|
437
|
+
*/
|
|
438
|
+
private serveHttpContext;
|
|
439
|
+
/**
|
|
440
|
+
* Chemin d'erreur de {@link handleHttp} : libère le scope d'un contexte qui
|
|
441
|
+
* n'a pas pu se construire, puis rend l'erreur au client (`onError`).
|
|
442
|
+
*/
|
|
443
|
+
private failHttp;
|
|
444
|
+
/**
|
|
445
|
+
* Le trajet d'une requête HTTP DANS la bulle ALS : CORS, calque de
|
|
446
|
+
* configuration, route, en-têtes de sécurité, repli statique, corps, gardes,
|
|
447
|
+
* action.
|
|
448
|
+
*
|
|
449
|
+
* Chaque étape rend sa valeur, ou une promesse quand elle attend réellement
|
|
450
|
+
* (préflight à terminer, hook asynchrone, fichier statique, corps à lire,
|
|
451
|
+
* session, pare-feu, action asynchrone) — et la suite ne s'enchaîne par
|
|
452
|
+
* `then` que dans ce cas. Un `await` sur une valeur déjà connue suspendait le
|
|
453
|
+
* pipeline pour rien : ~50 évènements asynchrones par GET (#505).
|
|
454
|
+
*
|
|
455
|
+
* @returns le contexte servi, ou la promesse de ce contexte.
|
|
456
|
+
* @throws l'erreur d'une étape synchrone — {@link handleHttp} la traite comme
|
|
457
|
+
* un rejet (même `catch`, même `onError`).
|
|
458
|
+
*/
|
|
459
|
+
private runHttpPipeline;
|
|
460
|
+
/**
|
|
461
|
+
* Route, en-têtes de sécurité applicatifs, repli statique — puis le corps.
|
|
462
|
+
* N'attend que le repli statique, et seulement quand aucune route ne matche.
|
|
463
|
+
*/
|
|
464
|
+
private routeHttpRequest;
|
|
465
|
+
/**
|
|
466
|
+
* Le corps — lu seulement s'il y en a un à lire —, puis les gardes
|
|
467
|
+
* ({@link onRequestEnd}) et l'action. Chacun n'est attendu que s'il attend.
|
|
468
|
+
*/
|
|
469
|
+
private serveHttpRequest;
|
|
470
|
+
/**
|
|
471
|
+
* Les gardes d'une requête HTTP, corps lu : en-têtes de scheme, domaine, hook
|
|
472
|
+
* `beforeResolve`, armement de la route, CSRF, session, pare-feu — dans cet
|
|
473
|
+
* ordre.
|
|
474
|
+
*
|
|
475
|
+
* Synchrone tant qu'aucune garde n'attend (#505) : le hook `beforeResolve`
|
|
476
|
+
* n'est attendu que s'il a un écouteur, la session que si la requête en
|
|
477
|
+
* ouvre une, le pare-feu que sur une zone protégée ou à accès contrôlé.
|
|
478
|
+
*
|
|
479
|
+
* @param context - le contexte de la requête.
|
|
480
|
+
* @param error - une erreur survenue en amont : relevée telle quelle.
|
|
481
|
+
* @returns le contexte une fois les gardes passées, ou la promesse de ce
|
|
482
|
+
* contexte quand une garde attend.
|
|
483
|
+
* @throws de façon SYNCHRONE l'erreur reçue, l'exception du résolveur (404,
|
|
484
|
+
* 405…) et le refus CSRF ; un refus du pare-feu ou de session rompt la
|
|
485
|
+
* promesse rendue.
|
|
486
|
+
*/
|
|
487
|
+
onRequestEnd(context: HttpContext, error?: Error | null): MaybePromise<HttpContext>;
|
|
488
|
+
/**
|
|
489
|
+
* Suite d'{@link onRequestEnd} : armement de la route, CSRF, session, puis
|
|
490
|
+
* pare-feu. Synchrone tant que ni la session ni le pare-feu n'attendent.
|
|
491
|
+
*/
|
|
492
|
+
private armAndGuard;
|
|
493
|
+
/**
|
|
494
|
+
* Le pare-feu d'une requête en zone protégée : `handleSecurity`, puis les
|
|
495
|
+
* hooks `afterAuth` (succès) ou `onAuthFailure` (refus, relevé ensuite).
|
|
496
|
+
*
|
|
497
|
+
* @returns la promesse du contexte autorisé.
|
|
498
|
+
* @throws (rejet) le refus du pare-feu, après `onAuthFailure`.
|
|
499
|
+
*/
|
|
500
|
+
private runFirewall;
|
|
358
501
|
createWebsocketContext(scope: Scope, req: IncomingMessage, ws: Ws, type: ServerType): WebsocketContext;
|
|
502
|
+
/**
|
|
503
|
+
* Teardown d'une connexion WS à sa fermeture : signal d'abandon, hooks
|
|
504
|
+
* post-réponse, persistance de la session, puis libération du scope.
|
|
505
|
+
* Ne rejette jamais : toute erreur est journalisée (l'appelant est un
|
|
506
|
+
* écouteur d'événement synchrone, qui n'attend rien).
|
|
507
|
+
*/
|
|
508
|
+
private finishWebsocket;
|
|
359
509
|
onWebsocketRequest(ws: Ws, req: IncomingMessage, type: ServerType): Promise<unknown>;
|
|
360
510
|
handleWebsocket(scope: Scope, ws: Ws, req: IncomingMessage, type: ServerType): Promise<unknown>;
|
|
361
511
|
private releaseOrphanWsScope;
|
|
362
|
-
onConnect(context: WebsocketContext, error?:
|
|
512
|
+
onConnect(context: WebsocketContext | null, error?: unknown): Promise<Ws | number>;
|
|
363
513
|
checkValidDomain(context: ContextType): number;
|
|
364
514
|
isValidDomain(context: ContextType): boolean;
|
|
365
515
|
/**
|
|
@@ -4,8 +4,11 @@ import http from "node:http";
|
|
|
4
4
|
import http2 from "node:http2";
|
|
5
5
|
import { AddressInfo } from "node:net";
|
|
6
6
|
import { HttpTerminator } from "./serverShutdown.js";
|
|
7
|
+
import type { IHttpConfig } from "../../config/config.js";
|
|
8
|
+
import type { DefaultOptionsService } from "nodefony";
|
|
7
9
|
declare class ServerHttp extends Service {
|
|
8
|
-
private httpKernel;
|
|
10
|
+
private readonly httpKernel;
|
|
11
|
+
options: IHttpConfig["http"] & DefaultOptionsService;
|
|
9
12
|
module: Module;
|
|
10
13
|
server: http.Server | http2.Http2Server | null;
|
|
11
14
|
httpTerminator: HttpTerminator | null;
|
|
@@ -4,8 +4,11 @@ import https from "node:https";
|
|
|
4
4
|
import http2 from "node:http2";
|
|
5
5
|
import { HttpTerminator } from "./serverShutdown.js";
|
|
6
6
|
import { AddressInfo } from "node:net";
|
|
7
|
+
import type { IHttpConfig } from "../../config/config.js";
|
|
8
|
+
import type { DefaultOptionsService } from "nodefony";
|
|
7
9
|
declare class ServerHttps extends Service {
|
|
8
|
-
private httpKernel;
|
|
10
|
+
private readonly httpKernel;
|
|
11
|
+
options: IHttpConfig["https"] & DefaultOptionsService;
|
|
9
12
|
httpTerminator: HttpTerminator | null;
|
|
10
13
|
module: Module;
|
|
11
14
|
server: https.Server | http2.Http2SecureServer | null;
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import Ws, { WebSocketServer } from "ws";
|
|
2
2
|
import { Service, Module, FamilyType } from "nodefony";
|
|
3
|
+
import type { DefaultOptionsService } from "nodefony";
|
|
4
|
+
import type { IHttpConfig } from "../../config/config.js";
|
|
3
5
|
import HttpKernel, { ProtocolType, ServerType, SchemeType } from "../http-kernel.js";
|
|
4
6
|
import { AddressInfo } from "node:net";
|
|
5
7
|
import type { IncomingMessage } from "node:http";
|
|
6
8
|
import httpsServers from "./server-https.js";
|
|
7
9
|
declare class WebsocketSecure extends Service {
|
|
8
|
-
private httpKernel;
|
|
10
|
+
private readonly httpKernel;
|
|
11
|
+
options: IHttpConfig["websocketSecure"] & DefaultOptionsService;
|
|
9
12
|
module: Module;
|
|
10
13
|
ready: boolean;
|
|
11
14
|
server: WebSocketServer | null;
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import Ws, { WebSocketServer } from "ws";
|
|
2
2
|
import { Service, Module, FamilyType } from "nodefony";
|
|
3
|
+
import type { DefaultOptionsService } from "nodefony";
|
|
4
|
+
import type { IHttpConfig } from "../../config/config.js";
|
|
3
5
|
import HttpKernel, { ProtocolType, ServerType, SchemeType } from "../http-kernel.js";
|
|
4
6
|
import { AddressInfo } from "node:net";
|
|
5
7
|
import type { IncomingMessage } from "node:http";
|
|
6
8
|
import httpServer from "./server-http.js";
|
|
7
9
|
declare class Websocket extends Service {
|
|
8
|
-
private httpKernel;
|
|
10
|
+
private readonly httpKernel;
|
|
11
|
+
options: IHttpConfig["websocket"] & DefaultOptionsService;
|
|
9
12
|
module: Module;
|
|
10
13
|
ready: boolean;
|
|
11
14
|
server: WebSocketServer | null;
|
|
@@ -3,6 +3,8 @@ import type { IPage } from "nodefony";
|
|
|
3
3
|
import type { ISessionStorage, ISessionSummary, ISessionRecord, ISessionListQuery } from "../../interfaces/ISession.js";
|
|
4
4
|
import HttpKernel, { ContextType } from "../http-kernel.js";
|
|
5
5
|
import Session, { OptionsSessionType } from "../../src/session/session.js";
|
|
6
|
+
import type { IHttpConfig } from "../../config/config.js";
|
|
7
|
+
import type { DefaultOptionsService } from "nodefony";
|
|
6
8
|
import Certificate from "../../service/certificates.js";
|
|
7
9
|
import type { ISessionCounts } from "../../src/session/storage/sessionFilters.js";
|
|
8
10
|
export type sessionStrategyType = "none" | "migrate" | "invalidate";
|
|
@@ -25,6 +27,7 @@ export declare function computeSessionRef(secret: Buffer, id: string): string;
|
|
|
25
27
|
* testée isolément).
|
|
26
28
|
*/
|
|
27
29
|
export declare function toSessionSummary(rec: ISessionRecord, ref: string, currentRef?: string | null): ISessionSummary;
|
|
30
|
+
export type ISessionServiceOptions = IHttpConfig["session"] & DefaultOptionsService;
|
|
28
31
|
declare class SessionsService extends Service {
|
|
29
32
|
httpKernel: HttpKernel;
|
|
30
33
|
/**
|
|
@@ -48,12 +51,13 @@ declare class SessionsService extends Service {
|
|
|
48
51
|
static getStorage(name: string): SessionStorageCtor | undefined;
|
|
49
52
|
/** Noms des handlers de session enregistrés. */
|
|
50
53
|
static storageHandlers(): string[];
|
|
54
|
+
options: ISessionServiceOptions;
|
|
51
55
|
sessionStrategy: sessionStrategyType;
|
|
52
56
|
storage: ISessionStorage | null;
|
|
53
57
|
module: Module;
|
|
54
58
|
defaultSessionName: string;
|
|
55
|
-
secret?: Buffer;
|
|
56
|
-
iv?: Buffer;
|
|
59
|
+
secret?: Buffer | undefined;
|
|
60
|
+
iv?: Buffer | undefined;
|
|
57
61
|
certificates: Certificate | null;
|
|
58
62
|
private gcScheduler;
|
|
59
63
|
constructor(module: Module, httpKernel: HttpKernel);
|
|
@@ -4,7 +4,7 @@ import fs from "node:fs";
|
|
|
4
4
|
import type { IParsedUploadFile } from "../../interfaces/IUpload.js";
|
|
5
5
|
export declare class upload extends Service {
|
|
6
6
|
httpKernel: HttpKernel;
|
|
7
|
-
path?: string | fs.PathLike;
|
|
7
|
+
path?: string | fs.PathLike | undefined;
|
|
8
8
|
module: Module;
|
|
9
9
|
constructor(module: Module, httpKernel: HttpKernel);
|
|
10
10
|
/**
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Container, Service, Severity, Msgid, Message, Pci, Pdu, KernelEventsType, nodefonyError, Scope } from "nodefony";
|
|
2
2
|
import type { IProfilerQuery } from "nodefony";
|
|
3
|
-
import type {
|
|
3
|
+
import type { IRouteResolver, IRequestRouter } from "../../interfaces/IRouting.js";
|
|
4
|
+
import type { ISecurityZone } from "../../interfaces/ISecurity.js";
|
|
4
5
|
import { WebSocketServer } from "ws";
|
|
5
6
|
import http2 from "node:http2";
|
|
6
7
|
import http from "node:http";
|
|
@@ -16,7 +17,6 @@ import { URL } from "node:url";
|
|
|
16
17
|
import Session from "../session/session.js";
|
|
17
18
|
import Cookie from "../cookies/cookie.js";
|
|
18
19
|
import HttpError from "../errors/httpError.js";
|
|
19
|
-
import { SecuredArea } from "@nodefony/security";
|
|
20
20
|
/**
|
|
21
21
|
* Bascule runtime du gate des logs d'events lifecycle. Réservé HORS hot path :
|
|
22
22
|
* tests et futur « Audit à chaud » (fenêtre bornée prod→verbeux, auto-revert
|
|
@@ -41,7 +41,7 @@ export declare function nextCspNonce(): string;
|
|
|
41
41
|
declare class Context extends Service implements IContextInterface {
|
|
42
42
|
#private;
|
|
43
43
|
secure: boolean;
|
|
44
|
-
security?:
|
|
44
|
+
security?: ISecurityZone | null | undefined;
|
|
45
45
|
cleaned: boolean;
|
|
46
46
|
isControlledAccess: boolean;
|
|
47
47
|
validDomain: boolean;
|
|
@@ -52,7 +52,7 @@ declare class Context extends Service implements IContextInterface {
|
|
|
52
52
|
sessionStarting: boolean;
|
|
53
53
|
domain: string;
|
|
54
54
|
type: ServerType;
|
|
55
|
-
server?: WebSocketServer | http.Server | https.Server | http2.Http2SecureServer | null;
|
|
55
|
+
server?: WebSocketServer | http.Server | https.Server | http2.Http2SecureServer | null | undefined;
|
|
56
56
|
httpKernel: HttpKernel | null;
|
|
57
57
|
request: contextRequest | null;
|
|
58
58
|
response: contextResponse | null;
|
|
@@ -64,7 +64,7 @@ declare class Context extends Service implements IContextInterface {
|
|
|
64
64
|
set originUrl(value: URL | undefined | null);
|
|
65
65
|
cookies: Cookies;
|
|
66
66
|
error: Error | HttpError | nodefonyError | null | undefined;
|
|
67
|
-
sessionService?: SessionsService | null;
|
|
67
|
+
sessionService?: SessionsService | null | undefined;
|
|
68
68
|
session: Session | null | undefined;
|
|
69
69
|
cookieSession: Cookie | null | undefined;
|
|
70
70
|
user: unknown;
|
|
@@ -79,8 +79,8 @@ declare class Context extends Service implements IContextInterface {
|
|
|
79
79
|
*/
|
|
80
80
|
protected resolveIsHtml(): boolean;
|
|
81
81
|
crossDomain: boolean;
|
|
82
|
-
router:
|
|
83
|
-
resolver:
|
|
82
|
+
router: IRequestRouter | null;
|
|
83
|
+
resolver: IRouteResolver | null;
|
|
84
84
|
/**
|
|
85
85
|
* Intent de session de la route courante (posé par le Resolver depuis
|
|
86
86
|
* `@UseSession` / paramètre `@Session`). Pilote le point d'activation unique
|
|
@@ -110,8 +110,8 @@ declare class Context extends Service implements IContextInterface {
|
|
|
110
110
|
profilerQueries: IProfilerQuery[] | null;
|
|
111
111
|
profiling: boolean;
|
|
112
112
|
securityTrace: ISecurityTrace | null;
|
|
113
|
-
private _timingEnabled;
|
|
114
|
-
private _timingVerbose;
|
|
113
|
+
private readonly _timingEnabled;
|
|
114
|
+
private readonly _timingVerbose;
|
|
115
115
|
readonly phases: PhaseTiming[];
|
|
116
116
|
private _phaseIndex;
|
|
117
117
|
private _afterResponseFns;
|
|
@@ -145,7 +145,18 @@ declare class Context extends Service implements IContextInterface {
|
|
|
145
145
|
phaseStart(name: PhaseName): void;
|
|
146
146
|
phaseEnd(name: PhaseName): void;
|
|
147
147
|
onAfterResponse(fn: AfterResponseHandler): void;
|
|
148
|
-
|
|
148
|
+
/**
|
|
149
|
+
* Exécute les hooks `onAfterResponse`, une seule fois, dans l'ordre
|
|
150
|
+
* d'enregistrement ; un hook qui lève est journalisé sans arrêter les suivants.
|
|
151
|
+
*
|
|
152
|
+
* @returns `undefined` quand aucun hook n'est enregistré (le cas nominal) ou
|
|
153
|
+
* qu'ils ont déjà tourné : rien à attendre, le démontage continue sans
|
|
154
|
+
* suspension. Une `Promise` sinon, réglée quand le dernier hook a fini — elle
|
|
155
|
+
* ne rejette jamais.
|
|
156
|
+
*/
|
|
157
|
+
_runAfterResponse(): Promise<void> | undefined;
|
|
158
|
+
/** Boucle des hooks `onAfterResponse` — séquentielle, erreurs journalisées. */
|
|
159
|
+
private runAfterResponseHooks;
|
|
149
160
|
get signal(): AbortSignal;
|
|
150
161
|
_abortIfPending(reason?: string): void;
|
|
151
162
|
log(pci: Pci, severity?: Severity, msgid?: Msgid, msg?: Message): Pdu;
|
|
@@ -32,11 +32,11 @@ export interface ForwardedElement {
|
|
|
32
32
|
/** Résolution canonique unifiée des en-têtes forwarded. */
|
|
33
33
|
export interface ResolvedProxy {
|
|
34
34
|
/** Scheme effectif côté client (`http` / `https`), ou `undefined` si indéterminé. */
|
|
35
|
-
proto?: string;
|
|
35
|
+
proto?: string | undefined;
|
|
36
36
|
/** Host effectif côté client (Host d'origine), ou `undefined` si non fourni. */
|
|
37
|
-
host?: string;
|
|
37
|
+
host?: string | undefined;
|
|
38
38
|
/** Chaîne `for` brute (node identifiers, gauche→droite) — pour log/metadata interne. */
|
|
39
|
-
forwardedFor?: string;
|
|
39
|
+
forwardedFor?: string | undefined;
|
|
40
40
|
/** IP cliente réelle, résolue from-right (toujours fiable, jamais l'IP forgée). */
|
|
41
41
|
clientIp: string | null;
|
|
42
42
|
/** Le header standard `Forwarded` a-t-il été la source (sinon `X-Forwarded-*`) ? */
|