@nodefony/http 10.0.0-alpha.8 → 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/_virtual/{_@oxc-project_runtime@0.150.0 → _@oxc-project_runtime@0.151.0}/helpers/esm/decorate.js +1 -1
- package/dist/_virtual/{_@oxc-project_runtime@0.150.0 → _@oxc-project_runtime@0.151.0}/helpers/esm/decorateMetadata.js +1 -1
- package/dist/_virtual/{_@oxc-project_runtime@0.150.0 → _@oxc-project_runtime@0.151.0}/helpers/esm/decorateParam.js +1 -1
- package/dist/index.js +6 -4
- 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 +118 -5
- package/dist/nodefony/service/http-kernel.js +342 -165
- 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 +23 -23
- package/dist/nodefony/service/servers/server-https.js +41 -42
- package/dist/nodefony/service/servers/server-static.js +13 -17
- package/dist/nodefony/service/servers/server-websocket-secure.js +11 -10
- package/dist/nodefony/service/servers/server-websocket.js +11 -10
- package/dist/nodefony/service/servers/wsHeartbeat.js +2 -2
- package/dist/nodefony/service/sessions/sessions-service.js +25 -17
- package/dist/nodefony/service/trace.js +1 -1
- package/dist/nodefony/service/upload/upload-service.js +11 -10
- 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/error-renderer.d.ts +14 -0
- 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 +25 -25
- package/docs/observabilite.md +30 -30
- package/docs/rate-limit.md +22 -22
- package/docs/servers.md +61 -61
- package/docs/session.md +82 -77
- package/docs/upload.md +43 -37
- package/package.json +11 -11
package/docs/servers.md
CHANGED
|
@@ -122,18 +122,18 @@ les probes.
|
|
|
122
122
|
|
|
123
123
|
**Un port TLS qui parle deux protocoles.** Quand `servers.https.protocol` vaut `"2.0"` (le défaut),
|
|
124
124
|
Nodefony crée un serveur HTTP/2 sécurisé avec `allowHTTP1: true` (`ServerHttps.createServerH2()`,
|
|
125
|
-
`server-https.ts:
|
|
125
|
+
`server-https.ts:187`) : les clients modernes négocient `h2` par ALPN, les autres restent en HTTP/1.1
|
|
126
126
|
**sur le même port**. Le protocole effectif est relu socket par socket pour taguer le contexte
|
|
127
127
|
(`server-https.ts:222`).
|
|
128
128
|
|
|
129
129
|
**Le WebSocket n'est jamais un citoyen de seconde zone.** Il est adossé au serveur HTTP porteur
|
|
130
130
|
(`server-websocket.ts:80`), passe par le **même** rate-limit d'IP que les requêtes HTTP — un upgrade
|
|
131
|
-
_est_ une requête HTTP (`HttpKernel.onWebsocketRequest()`, `http-kernel.ts:
|
|
131
|
+
_est_ une requête HTTP (`HttpKernel.onWebsocketRequest()`, `http-kernel.ts:1619`) —, hérite de la même
|
|
132
132
|
session et du même firewall, et se ferme avec le même soin qu'une réponse HTTP.
|
|
133
133
|
|
|
134
134
|
> [!NOTE]
|
|
135
135
|
> Le serveur HTTP/3 (QUIC) est **réservé, pas implémenté** : la clé `http3` existe dans le schéma,
|
|
136
|
-
> marquée `reserved` (`config.ts:
|
|
136
|
+
> marquée `reserved` (`config.ts:1050`). Elle ne fait rien aujourd'hui.
|
|
137
137
|
|
|
138
138
|
## 🚀 Démarrage rapide
|
|
139
139
|
|
|
@@ -249,7 +249,7 @@ export default PingController;
|
|
|
249
249
|
|
|
250
250
|
### 4. Ce qu'on observe au boot
|
|
251
251
|
|
|
252
|
-
Le kernel démarre les serveurs à la phase `onReady` (`Kernel.ts:
|
|
252
|
+
Le kernel démarre les serveurs à la phase `onReady` (`Kernel.ts:1274`), puis affiche les URL réellement
|
|
253
253
|
en écoute — le récap de développement liste HTTP, HTTP/2, WS et WSS dans cet ordre
|
|
254
254
|
(`BootReporter.ts:389`) :
|
|
255
255
|
|
|
@@ -264,7 +264,7 @@ en écoute — le récap de développement liste HTTP, HTTP/2, WS et WSS dans ce
|
|
|
264
264
|
```
|
|
265
265
|
|
|
266
266
|
Hors écran animé (production, CI, `--debug`), ce sont les bannières par serveur qui sortent
|
|
267
|
-
(`ServerHttp.showBanner()`, `server-http.ts:
|
|
267
|
+
(`ServerHttp.showBanner()`, `server-http.ts:242`, appelées par le kernel — `Kernel.ts:561`) :
|
|
268
268
|
|
|
269
269
|
```text
|
|
270
270
|
Server Listen on http://127.0.0.1:5151 Family: IPv4 Protocol : 1.1
|
|
@@ -327,34 +327,34 @@ Choisir en cinq secondes :
|
|
|
327
327
|
| `server-websocket-secure` | `wss://` — 5152 | `server-https` actif | WebSocket adossé au serveur TLS. |
|
|
328
328
|
| `server-static` | (aucun port propre) | toujours enregistré | Fichiers statiques, en **repli** après le routing. |
|
|
329
329
|
|
|
330
|
-
L'assemblage est fait par `HttpKernel.initServers()` (`http-kernel.ts:
|
|
331
|
-
consulté sur son drapeau `active`, un serveur désactivé est **sauté**, pas créé (`http-kernel.ts:
|
|
330
|
+
L'assemblage est fait par `HttpKernel.initServers()` (`http-kernel.ts:1065`) : chaque serveur est
|
|
331
|
+
consulté sur son drapeau `active`, un serveur désactivé est **sauté**, pas créé (`http-kernel.ts:1062`).
|
|
332
332
|
Les serveurs WebSocket ne sont montés que si leur porteur l'a été.
|
|
333
333
|
|
|
334
334
|
### `server-http` — HTTP/1.1 en clair
|
|
335
335
|
|
|
336
336
|
Le plus simple, et celui qui porte le trafic en cloud-native. `ServerHttp.createServer()`
|
|
337
|
-
(`server-http.ts:
|
|
337
|
+
(`server-http.ts:83`) crée le `http.Server`, applique les réglages de transport, branche
|
|
338
338
|
`onHttpRequest`, puis écoute selon la politique de port.
|
|
339
339
|
|
|
340
340
|
- **Actif** si `servers.http` n'est pas `false` — la décision est prise dès le constructeur
|
|
341
341
|
(`server-http.ts:56`), et le port est lu de la config d'app (`ServerHttp.setPort()`,
|
|
342
|
-
`server-http.ts:
|
|
342
|
+
`server-http.ts:74`).
|
|
343
343
|
- **Réglages appliqués** : `requestTimeout` (anti slow-loris), `maxHeadersCount`, `timeout` de socket
|
|
344
344
|
(qui émet un événement `onTimeout`), `keepAliveTimeout`.
|
|
345
345
|
- **Erreurs de protocole** : l'événement `clientError` est traité explicitement
|
|
346
|
-
(`server-http.ts:
|
|
346
|
+
(`server-http.ts:186`) — voir la section Résilience, c'est un piège Node à part entière.
|
|
347
347
|
|
|
348
348
|
### `server-https` — TLS, et HTTP/2 par défaut
|
|
349
349
|
|
|
350
|
-
Deux branches dans un seul service, choisies sur `servers.https.protocol` (`server-https.ts:
|
|
350
|
+
Deux branches dans un seul service, choisies sur `servers.https.protocol` (`server-https.ts:100`) :
|
|
351
351
|
|
|
352
352
|
- **`"2.0"` (défaut)** → `http2.createSecureServer` avec `allowHTTP1: true`
|
|
353
|
-
(`ServerHttps.createServerH2()`, `server-https.ts:
|
|
353
|
+
(`ServerHttps.createServerH2()`, `server-https.ts:187`). Les bornes anti-DoS HTTP/2 ne sont posées
|
|
354
354
|
**que si elles sont configurées**, pour ne pas écraser les défauts de Node
|
|
355
|
-
(`maxSessionMemory`, `server-https.ts:
|
|
355
|
+
(`maxSessionMemory`, `server-https.ts:215`).
|
|
356
356
|
Les erreurs de session et de flux sont journalisées sans tuer le serveur
|
|
357
|
-
(`sessionError`, `server-https.ts:
|
|
357
|
+
(`sessionError`, `server-https.ts:296`).
|
|
358
358
|
- **`"1.1"`** → `https.createServer` classique.
|
|
359
359
|
|
|
360
360
|
Le certificat vient du service `certificates`, lu au moment de la création via
|
|
@@ -375,7 +375,7 @@ automatiquement un éventuel décalage de port.
|
|
|
375
375
|
réglages **forcés** par Nodefony : `server` et `clientTracking: true`, requis par `broadcast()` et par
|
|
376
376
|
le battement de cœur (`server-websocket.ts:80`).
|
|
377
377
|
- **Keep-alive** armé à la création (`startHeartbeat()`, `server-websocket.ts:87`) et par connexion
|
|
378
|
-
(`trackPong()`, `server-websocket.ts:
|
|
378
|
+
(`trackPong()`, `server-websocket.ts:103`).
|
|
379
379
|
- **Arrêt** : il s'inscrit en tête des écouteurs de terminaison
|
|
380
380
|
(`prependOnceListener`, `server-websocket.ts:91`) — l'ordre
|
|
381
381
|
compte, voir la section Arrêt gracieux.
|
|
@@ -400,7 +400,7 @@ C'est la distinction la plus utile de cette page, et celle qu'on rate le plus so
|
|
|
400
400
|
| Question | Où ça se règle | Source |
|
|
401
401
|
| ----------------------------------------- | ------------------------------ | --------------------------------------------------------- |
|
|
402
402
|
| **Quels** serveurs, sur **quels ports** ? | `servers` (config d'app) | `serversSchema` (`src/nodefony/src/config/schema.ts:149`) |
|
|
403
|
-
| **Comment** ces serveurs se comportent ? | `use("@nodefony/http", { … })` | `httpConfigSchema` (`config.ts:
|
|
403
|
+
| **Comment** ces serveurs se comportent ? | `use("@nodefony/http", { … })` | `httpConfigSchema` (`config.ts:989`) |
|
|
404
404
|
|
|
405
405
|
Autrement dit : la **topologie** est une propriété du déploiement (elle change entre le poste du dev,
|
|
406
406
|
la CI et le cluster) ; le **réglage** est une propriété de l'application.
|
|
@@ -421,19 +421,19 @@ Défauts matérialisés dans `defaultAppConfig` (`src/nodefony/src/config/defaul
|
|
|
421
421
|
|
|
422
422
|
### Niveau 2 — le transport HTTP / HTTPS
|
|
423
423
|
|
|
424
|
-
Table dérivée de `httpServerSchema` (`config.ts:
|
|
424
|
+
Table dérivée de `httpServerSchema` (`config.ts:272`) ; la section `https` reprend les mêmes clés et en
|
|
425
425
|
ajoute une (`httpsServerSchema`, `config.ts:336`).
|
|
426
426
|
|
|
427
|
-
| Option | Type | Défaut
|
|
428
|
-
| ---------------------------- | ----- |
|
|
429
|
-
| `maxHeadersCount` | int | `2000`
|
|
430
|
-
| `keepAliveTimeout` | ms | `5000`
|
|
431
|
-
| `timeout` | ms | `
|
|
432
|
-
| `requestTimeout` | ms | `30000`
|
|
433
|
-
| `responseTimeout` | ms | `30000`
|
|
434
|
-
| `shutdownTimeout` | ms | `5000`
|
|
435
|
-
| `headers` | objet | `null`
|
|
436
|
-
| `rejectUnauthorized` (https) | bool | `false`
|
|
427
|
+
| Option | Type | Défaut | Effet |
|
|
428
|
+
| ---------------------------- | ----- | ------- | -------------------------------------------------------------------------------- |
|
|
429
|
+
| `maxHeadersCount` | int | `2000` | Nombre maximum d'en-têtes par requête — anti _header flooding_. |
|
|
430
|
+
| `keepAliveTimeout` | ms | `5000` | Délai de réutilisation de la socket TCP entre deux requêtes. |
|
|
431
|
+
| `timeout` | ms | `30000` | Timeout global de socket, aligné sur `responseTimeout`. `0` = désactivé. |
|
|
432
|
+
| `requestTimeout` | ms | `30000` | Délai de réception de la requête complète — **anti slow-loris**. |
|
|
433
|
+
| `responseTimeout` | ms | `30000` | Délai d'envoi de la réponse complète (couche pipeline). |
|
|
434
|
+
| `shutdownTimeout` | ms | `5000` | Drain au shutdown avant destruction forcée. Garder < grâce orchestrateur. |
|
|
435
|
+
| `headers` | objet | `null` | En-têtes ajoutés à toutes les réponses. |
|
|
436
|
+
| `rejectUnauthorized` (https) | bool | `false` | Rejette les certificats invalides. `false` en dev (auto-signés), `true` en prod. |
|
|
437
437
|
|
|
438
438
|
Ces deux sections sont **permissives** (`z.looseObject`) : toute option supplémentaire de
|
|
439
439
|
`http.Server` / `net.Server` / TLS (`insecureHTTPParser`, `ciphers`, `minVersion`…) est transmise telle
|
|
@@ -442,7 +442,7 @@ quelle à Node. C'est délibéré — un schéma strict effacerait silencieuseme
|
|
|
442
442
|
### Niveau 2 — HTTP/2
|
|
443
443
|
|
|
444
444
|
Depuis `http2Schema` (`config.ts:353`), appliqué seulement si défini
|
|
445
|
-
(`maxSessionMemory`, `server-https.ts:
|
|
445
|
+
(`maxSessionMemory`, `server-https.ts:215`).
|
|
446
446
|
|
|
447
447
|
| Option | Type | Défaut | Effet |
|
|
448
448
|
| ---------------------- | ---- | ------ | ----------------------------------------------------------------------------- |
|
|
@@ -452,7 +452,7 @@ Depuis `http2Schema` (`config.ts:353`), appliqué seulement si défini
|
|
|
452
452
|
### Niveau 2 — WebSocket (`websocket` et `websocketSecure`)
|
|
453
453
|
|
|
454
454
|
Depuis `websocketSchema` (`config.ts:517`). Les deux sections partagent la forme et les défauts ; le WSS
|
|
455
|
-
lit `websocketSecure` (`config.ts:
|
|
455
|
+
lit `websocketSecure` (`config.ts:1058`).
|
|
456
456
|
|
|
457
457
|
| Option | Type | Défaut | Effet |
|
|
458
458
|
| ------------------------ | ------------------- | ------- | ---------------------------------------------------------------------------------- |
|
|
@@ -484,7 +484,7 @@ panne, c'est une nuisance.
|
|
|
484
484
|
|
|
485
485
|
### La règle
|
|
486
486
|
|
|
487
|
-
`resolvePortPolicy()` (`
|
|
487
|
+
`resolvePortPolicy()` (cœur, `src/nodefony/src/service/dev/devProcess.ts:1730` — la même règle sert `nodefony doctor`) tranche selon l'environnement, et la valeur explicite gagne
|
|
488
488
|
toujours :
|
|
489
489
|
|
|
490
490
|
| Environnement | Défaut | Pourquoi |
|
|
@@ -507,17 +507,17 @@ détails d'implémentation valent d'être connus, parce qu'ils expliquent des co
|
|
|
507
507
|
1. **On retente au `listen()`, jamais après une sonde.** Demander « le port est-il libre ? » puis
|
|
508
508
|
binder est une course : entre la réponse et le bind, un autre processus peut prendre le port. Le
|
|
509
509
|
`listen()` est atomique — on retente donc sur l'échec réel (`bindWithFallback()`,
|
|
510
|
-
`portBinder.ts:
|
|
510
|
+
`portBinder.ts:263`).
|
|
511
511
|
2. **Le port de l'autre serveur est réservé.** Si HTTP est chassé de 5151, incrémenter naïvement le
|
|
512
512
|
ferait voler 5152 à HTTPS, qui se décalerait à son tour. Les ports convoités par les autres serveurs
|
|
513
|
-
sont sautés d'emblée (`buildBindPlan()`, `portBinder.ts:
|
|
513
|
+
sont sautés d'emblée (`buildBindPlan()`, `portBinder.ts:105`, réservation `portBinder.ts:105`).
|
|
514
514
|
3. **Le gestionnaire d'erreur durable est posé APRÈS le bind.** Attaché avant, il verrait passer les
|
|
515
515
|
`EADDRINUSE` de repli et terminerait le kernel en croyant à une panne
|
|
516
|
-
(`ServerHttp.attachErrorHandler()`, `server-http.ts:
|
|
516
|
+
(`ServerHttp.attachErrorHandler()`, `server-http.ts:199`).
|
|
517
517
|
|
|
518
518
|
Quand le bind échoue pour de bon — `strict`, ou tous les replis épuisés, ou une erreur qui n'est pas un
|
|
519
519
|
conflit de port — c'est **fatal** : message explicite puis terminaison du processus
|
|
520
|
-
(`ServerHttp.reportBindError()`, `server-http.ts:
|
|
520
|
+
(`ServerHttp.reportBindError()`, `server-http.ts:212`). Un serveur qui n'écoute pas ne doit jamais
|
|
521
521
|
laisser un processus se croire démarré.
|
|
522
522
|
|
|
523
523
|
### Le corollaire : les ports effectifs sont publiés
|
|
@@ -537,12 +537,12 @@ serveur qui, lui, écoute très bien.
|
|
|
537
537
|
**Générer un certificat est un confort de développement, pas une fonction de production.** Nodefony
|
|
538
538
|
n'est pas une autorité de certification : en production, on fournit un vrai certificat (Let's Encrypt,
|
|
539
539
|
ingress k8s, reverse-proxy). Le service crie un avertissement si ce n'est pas le cas
|
|
540
|
-
(`Certificate.resolveStrategy()`, `certificates.ts:
|
|
540
|
+
(`Certificate.resolveStrategy()`, `certificates.ts:407`).
|
|
541
541
|
|
|
542
542
|
### Les quatre stratégies
|
|
543
543
|
|
|
544
544
|
Réglées par `certificates.strategy` (`certificatesSchema`, `config.ts:475`), résolues par
|
|
545
|
-
`Certificate.resolveStrategy()` (`certificates.ts:
|
|
545
|
+
`Certificate.resolveStrategy()` (`certificates.ts:407`).
|
|
546
546
|
|
|
547
547
|
| Stratégie | Quand l'utiliser | Ce qui se passe |
|
|
548
548
|
| --------------- | ------------------------------------------- | --------------------------------------------------------------------------- |
|
|
@@ -577,8 +577,8 @@ export default defineConfig(() => ({
|
|
|
577
577
|
### Ce que le chemin `explicit` évite
|
|
578
578
|
|
|
579
579
|
`node-forge` est une grosse dépendance. Elle est chargée **paresseusement**, uniquement sur le chemin
|
|
580
|
-
de génération (`Certificate.loadForge()`, `certificates.ts:
|
|
581
|
-
fourni, elle n'entre jamais dans le processus (`certificates.ts:
|
|
580
|
+
de génération (`Certificate.loadForge()`, `certificates.ts:238`) : en production avec un certificat
|
|
581
|
+
fourni, elle n'entre jamais dans le processus (`certificates.ts:238`).
|
|
582
582
|
|
|
583
583
|
### Conformité de l'auto-signé
|
|
584
584
|
|
|
@@ -588,7 +588,7 @@ invalid »). Celui de Nodefony respecte les règles qui comptent :
|
|
|
588
588
|
| Exigence | Norme | Mise en œuvre |
|
|
589
589
|
| --------------------------------------- | -------------------- | --------------------------------------------------------- |
|
|
590
590
|
| Signature SHA-256, **jamais** SHA-1 | RFC 5280, CA/B Forum | `selfSigned.hash` par défaut `sha256` (`config.ts:400`) |
|
|
591
|
-
| Numéro de série aléatoire 128 bits | RFC 5280 §4.1.2.2 | `Certificate.generateSerialHex()` (`certificates.ts:
|
|
591
|
+
| Numéro de série aléatoire 128 bits | RFC 5280 §4.1.2.2 | `Certificate.generateSerialHex()` (`certificates.ts:302`) |
|
|
592
592
|
| Le SAN fait foi, pas le CN | RFC 6125 | SAN dérivé du kernel si non fourni (`config.ts:444`) |
|
|
593
593
|
| `notBefore` reculé (décalage d'horloge) | pratique | `selfSigned.backdateMinutes`, défaut 5 (`config.ts:415`) |
|
|
594
594
|
| Clé privée non lisible par tous | hygiène | `privateKeyMode` `0600` (`config.ts:499`) |
|
|
@@ -596,7 +596,7 @@ invalid »). Celui de Nodefony respecte les règles qui comptent :
|
|
|
596
596
|
### Régénération automatique
|
|
597
597
|
|
|
598
598
|
Un certificat présent sur disque n'est pas forcément **adéquat**. `Certificate.isCertAdequate()`
|
|
599
|
-
(`certificates.ts:
|
|
599
|
+
(`certificates.ts:579`) le régénère s'il est expiré, s'il est signé en SHA-1, ou si son SAN ne couvre
|
|
600
600
|
plus les noms requis — le dernier cas est celui qui sauve : changer le domaine d'écoute sans ce
|
|
601
601
|
contrôle laisserait un certificat obsolète en place indéfiniment.
|
|
602
602
|
|
|
@@ -618,7 +618,7 @@ sont ignorés** (`config.ts:965`), et l'IP retenue est celle de la socket réell
|
|
|
618
618
|
| `"loopback"`, `"linklocal"`, `"uniquelocal"` | Préréglages de plages privées. |
|
|
619
619
|
|
|
620
620
|
La politique est compilée **une seule fois** au premier usage (`HttpKernel.getTrustProxyChecker()`,
|
|
621
|
-
`http-kernel.ts:
|
|
621
|
+
`http-kernel.ts:562`, via `buildTrustProxy()`, `trustProxy.ts:100`) : aucune structure allouée par
|
|
622
622
|
requête. La résolution de l'IP cliente remonte la chaîne **de droite à gauche** depuis la socket réelle
|
|
623
623
|
(`resolveForwarded()`, `forwarded.ts:253`) — conforme RFC 7239 et à la recommandation OWASP.
|
|
624
624
|
|
|
@@ -626,7 +626,7 @@ requête. La résolution de l'IP cliente remonte la chaîne **de droite à gauch
|
|
|
626
626
|
|
|
627
627
|
Barrière testée **avant le routage**, contre l'injection d'en-tête `Host`. Le domaine canonique du
|
|
628
628
|
kernel est toujours accepté, plus le loopback en développement (`HttpKernel.compileAlias()`,
|
|
629
|
-
`http-kernel.ts:
|
|
629
|
+
`http-kernel.ts:964`). `false` (défaut) = ce socle seul ; une liste ajoute des vhosts (exact ou joker
|
|
630
630
|
d'un seul niveau, `*.cdn.example.com`) ; `true` désactive la barrière — à réserver au cas où le proxy
|
|
631
631
|
filtre déjà le `Host` (`config.ts:974`).
|
|
632
632
|
|
|
@@ -635,13 +635,13 @@ filtre déjà le `Host` (`config.ts:974`).
|
|
|
635
635
|
Les navigateurs **n'appliquent pas CORS aux WebSockets**. Sans contrôle, une page tierce peut ouvrir un
|
|
636
636
|
WS authentifié par le cookie de session de la victime (CSWSH, OWASP WSTG-CLNT-10). Nodefony exige donc
|
|
637
637
|
par défaut que l'`Origin` du handshake corresponde au `Host` servi
|
|
638
|
-
(`HttpKernel.checkWebsocketOrigin()`, `http-kernel.ts:
|
|
638
|
+
(`HttpKernel.checkWebsocketOrigin()`, `http-kernel.ts:621`), avec tolérance loopback en développement
|
|
639
639
|
et allowlist explicite pour les SPA cross-origine. Un refus se solde par une fermeture **1008 (Policy
|
|
640
640
|
Violation)**, jamais par un code HTTP.
|
|
641
641
|
|
|
642
642
|
> [!NOTE]
|
|
643
643
|
> Une requête **sans** `Origin` (client non navigateur : script, agent, test) est acceptée
|
|
644
|
-
> (`http-kernel.ts:
|
|
644
|
+
> (`http-kernel.ts:630`). Ce n'est pas un trou : un attaquant non navigateur n'a aucun besoin de CSWSH,
|
|
645
645
|
> il se connecte directement. Le contrôle protège les **utilisateurs**, pas le port.
|
|
646
646
|
|
|
647
647
|
## Probes de santé — `/livez` et `/readyz`
|
|
@@ -658,13 +658,13 @@ redémarrer le pod **en plein drain** par le kubelet, et casserait précisément
|
|
|
658
658
|
protéger.
|
|
659
659
|
|
|
660
660
|
Implémentation : court-circuit **total** du pipeline dans `HttpKernel.onHttpRequest()`
|
|
661
|
-
(`http-kernel.ts:
|
|
662
|
-
réponses pré-allouées (`HttpKernel.#respondHealth()`, `http-kernel.ts:
|
|
661
|
+
(`http-kernel.ts:972`) — pas de contexte, pas de portée DI, pas de session, pas de journal par sonde,
|
|
662
|
+
réponses pré-allouées (`HttpKernel.#respondHealth()`, `http-kernel.ts:497`). Et surtout : **avant le
|
|
663
663
|
rate-limit**. Un kubelet qui reçoit un `429` croit le pod mort → cascade de redémarrages.
|
|
664
664
|
|
|
665
665
|
| Option | Type | Défaut | Effet |
|
|
666
666
|
| --------------- | ------ | --------- | ---------------------------------------------------------------------- |
|
|
667
|
-
| `enabled` | bool | `true` | Expose les probes (`healthSchema`, `config.ts:
|
|
667
|
+
| `enabled` | bool | `true` | Expose les probes (`healthSchema`, `config.ts:940`). |
|
|
668
668
|
| `livenessPath` | string | `/livez` | Chemin de la sonde de vie (`livenessProbe.httpGet.path` k8s). |
|
|
669
669
|
| `readinessPath` | string | `/readyz` | Chemin de la sonde de disponibilité. |
|
|
670
670
|
| `shutdownDelay` | ms | `0` | Délai entre la bascule `503` et le début du drain (propagation du LB). |
|
|
@@ -732,8 +732,8 @@ inséré **en tête** et donc s'exécuter **en premier** (`http-kernel.ts:515`).
|
|
|
732
732
|
load balancer cesse d'envoyer du trafic, et `shutdownDelay` laisse à cette information le temps de se
|
|
733
733
|
propager.
|
|
734
734
|
|
|
735
|
-
**Étape 2 — les clients WebSocket sont prévenus.** `Websocket.terminate()` (`server-websocket.ts:
|
|
736
|
-
envoie un message applicatif puis une **frame Close 1001 « Going Away »** (`server-websocket.ts:
|
|
735
|
+
**Étape 2 — les clients WebSocket sont prévenus.** `Websocket.terminate()` (`server-websocket.ts:113`)
|
|
736
|
+
envoie un message applicatif puis une **frame Close 1001 « Going Away »** (`server-websocket.ts:113`).
|
|
737
737
|
Sans elle, la coupure TCP ferait voir un **1006** au client — code réservé, jamais émis sur le fil,
|
|
738
738
|
indiscernable d'une panne réseau. Avec 1001, le client sait qu'il doit simplement se reconnecter.
|
|
739
739
|
|
|
@@ -745,7 +745,7 @@ réponses en cours, sockets inactives fermées, destruction forcée au-delà de
|
|
|
745
745
|
> [!IMPORTANT]
|
|
746
746
|
> L'ordre WS-avant-HTTP n'est pas cosmétique. Les serveurs WebSocket s'inscrivent en
|
|
747
747
|
> `prependOnceListener` (`server-websocket.ts:91`), les serveurs HTTP en `once`
|
|
748
|
-
> (`server-http.ts:
|
|
748
|
+
> (`server-http.ts:170`). Inversé, le terminator détruirait les sockets déjà upgradées **sans** frame
|
|
749
749
|
> Close : retour du 1006 pour tous les clients temps réel.
|
|
750
750
|
|
|
751
751
|
Deux garde-fous encadrent la séquence : `shutdownTimeout` **par serveur** (le drain nominal) et
|
|
@@ -784,13 +784,13 @@ processus à l'arrêt.
|
|
|
784
784
|
|
|
785
785
|
L'upgrade WebSocket **est** une requête HTTP : il passe donc par le **même** compteur de rate-limit par
|
|
786
786
|
IP que les requêtes ordinaires, vérifié avant toute allocation de contexte
|
|
787
|
-
(`HttpKernel.onWebsocketRequest()`, `http-kernel.ts:
|
|
787
|
+
(`HttpKernel.onWebsocketRequest()`, `http-kernel.ts:1619`). Le `101` étant déjà émis par `ws`, un `429`
|
|
788
788
|
est impossible → la connexion est fermée en **1013 « Try Again Later »**
|
|
789
|
-
(`rateLimiter`, `http-kernel.ts:
|
|
789
|
+
(`rateLimiter`, `http-kernel.ts:1644`), sans
|
|
790
790
|
journalisation (un journal par handshake rejeté serait lui-même un amplificateur sous flood).
|
|
791
791
|
|
|
792
792
|
Un second plafond, **désactivé par défaut**, borne le nombre de connexions **simultanées** par IP :
|
|
793
|
-
`wsMaxConnectionsPerIp` (`config.ts:
|
|
793
|
+
`wsMaxConnectionsPerIp` (`config.ts:1067`). En cloud-native, laisser `null` et déléguer à l'edge —
|
|
794
794
|
nginx `limit_conn`, HAProxy `sc_conn_cur` — qui voit tout le trafic, rejette avant le coût du
|
|
795
795
|
descripteur et du TLS, et couvre tous les pods. Ne l'activer que sur une machine sans ingress.
|
|
796
796
|
|
|
@@ -809,7 +809,7 @@ par seconde. Les choix visibles dans le code :
|
|
|
809
809
|
- **Aucun timer par connexion** — un `setInterval` par serveur WebSocket, `unref`, et deux `number` par
|
|
810
810
|
socket (`wsHeartbeat.ts:69`).
|
|
811
811
|
- **Rien de compilé par requête** — la politique de trust-proxy, celle des `Origin` WS et les motifs de
|
|
812
|
-
`trustedHosts` sont compilés une fois et mémoïsés (`http-kernel.ts:
|
|
812
|
+
`trustedHosts` sont compilés une fois et mémoïsés (`http-kernel.ts:407`, `http-kernel.ts:407`).
|
|
813
813
|
- **Rejets avant allocation** — rate-limit HTTP et bornes WS sont vérifiés avant le contexte, la portée
|
|
814
814
|
DI et l'ALS : un flood coûte une recherche dans une table de hachage.
|
|
815
815
|
- **Probes hors pipeline** — réponses pré-allouées, aucun objet créé, aucun journal
|
|
@@ -834,18 +834,18 @@ demande le backplane realtime.
|
|
|
834
834
|
|
|
835
835
|
| Domaine | Norme | Ancrage |
|
|
836
836
|
| ------------------------------------- | ------------------ | -------------------------------------------------------------------------- |
|
|
837
|
-
| HTTP/1.1 (sémantique, message) | RFC 9110, 9112 | `node:http` + pipeline `HttpKernel.onHttpRequest()` (`http-kernel.ts:
|
|
838
|
-
| HTTP/2 | RFC 9113 | `ServerHttps.createServerH2()` (`server-https.ts:
|
|
837
|
+
| HTTP/1.1 (sémantique, message) | RFC 9110, 9112 | `node:http` + pipeline `HttpKernel.onHttpRequest()` (`http-kernel.ts:972`) |
|
|
838
|
+
| HTTP/2 | RFC 9113 | `ServerHttps.createServerH2()` (`server-https.ts:187`) |
|
|
839
839
|
| HTTP/2 Rapid Reset | CVE-2023-44487 | `maxConcurrentStreams` (`config.ts:355`) |
|
|
840
|
-
| En-têtes trop volumineux → 431 | RFC 6585 §5 | `handleClientError()` (`clientError.ts:
|
|
840
|
+
| En-têtes trop volumineux → 431 | RFC 6585 §5 | `handleClientError()` (`clientError.ts:15`) |
|
|
841
841
|
| WebSocket — protocole | RFC 6455 | `ws@8` + options (`config.ts:496`) |
|
|
842
|
-
| WebSocket — Close 1001 « Going Away » | RFC 6455 §7.4.1 | `Websocket.terminate()` (`server-websocket.ts:
|
|
842
|
+
| WebSocket — Close 1001 « Going Away » | RFC 6455 §7.4.1 | `Websocket.terminate()` (`server-websocket.ts:113`) |
|
|
843
843
|
| WebSocket — 1009 « Message Too Big » | RFC 6455 §7.4.1 | `maxPayload` (`config.ts:543`) |
|
|
844
844
|
| WebSocket — validation UTF-8 | RFC 6455 §8.1 | `skipUTF8Validation` (`config.ts:623`) |
|
|
845
845
|
| WebSocket — compression | RFC 7692 | `perMessageDeflate` (`config.ts:567`) |
|
|
846
|
-
| CSWSH (Origin au handshake) | OWASP WSTG-CLNT-10 | `HttpKernel.checkWebsocketOrigin()` (`http-kernel.ts:
|
|
846
|
+
| CSWSH (Origin au handshake) | OWASP WSTG-CLNT-10 | `HttpKernel.checkWebsocketOrigin()` (`http-kernel.ts:621`) |
|
|
847
847
|
| En-têtes forwarded | RFC 7239 | `resolveForwarded()` (`forwarded.ts:253`) |
|
|
848
|
-
| Certificat — série, SAN, extensions | RFC 5280 | `Certificate.generateSerialHex()` (`certificates.ts:
|
|
848
|
+
| Certificat — série, SAN, extensions | RFC 5280 | `Certificate.generateSerialHex()` (`certificates.ts:302`) |
|
|
849
849
|
| Certificat — identité par le SAN | RFC 6125 | `sanSchema` (`config.ts:442`) |
|
|
850
850
|
|
|
851
851
|
## ⚠️ Pièges (symptôme → cause → correction)
|
|
@@ -886,7 +886,7 @@ l'origine du transport.
|
|
|
886
886
|
|
|
887
887
|
`proxy:generate` mérite un mot : la configuration nginx/HAProxy est **dérivée** des domaines de
|
|
888
888
|
confiance, des ports effectifs et des dossiers statiques montés — donc elle ne diverge pas du code. Le
|
|
889
|
-
résumé de certificat vient de `Certificate.describe()` (`certificates.ts:
|
|
889
|
+
résumé de certificat vient de `Certificate.describe()` (`certificates.ts:862`), source unique partagée
|
|
890
890
|
par la commande, le boot et un futur écran d'administration.
|
|
891
891
|
|
|
892
892
|
**Runtime.** `nodefony status` et `nodefony stop` lisent les ports effectifs publiés au boot ; ils
|