@nodefony/http 10.0.0-alpha.3 → 10.0.0-alpha.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/LICENSE +201 -543
- package/README.md +1 -1
- package/dist/_virtual/{_@oxc-project_runtime@0.148.0 → _@oxc-project_runtime@0.149.0}/helpers/esm/decorate.js +1 -1
- package/dist/_virtual/{_@oxc-project_runtime@0.148.0 → _@oxc-project_runtime@0.149.0}/helpers/esm/decorateMetadata.js +1 -1
- package/dist/_virtual/{_@oxc-project_runtime@0.148.0 → _@oxc-project_runtime@0.149.0}/helpers/esm/decorateParam.js +1 -1
- package/dist/index.js +2 -2
- package/dist/nodefony/command/assetsPublishCommand.js +2 -4
- package/dist/nodefony/command/proxyGenerateCommand.js +28 -9
- package/dist/nodefony/config/config.js +6 -6
- package/dist/nodefony/config/defineModuleConfig.js +2 -2
- package/dist/nodefony/service/certificates.js +29 -4
- package/dist/nodefony/service/http-kernel.js +2 -2
- package/dist/nodefony/service/servers/server-http.js +3 -3
- package/dist/nodefony/service/servers/server-https.js +3 -3
- package/dist/nodefony/service/servers/server-websocket-secure.js +3 -3
- package/dist/nodefony/service/servers/server-websocket.js +3 -3
- package/dist/nodefony/service/sessions/sessions-service.js +5 -5
- package/dist/nodefony/service/upload/upload-service.js +3 -3
- package/dist/nodefony/src/context/domainMatcher.js +34 -1
- package/dist/nodefony/src/proxy/generateProxyConfig.js +34 -7
- package/dist/types/index.d.ts +2 -2
- package/dist/types/nodefony/command/proxyGenerateCommand.d.ts +4 -0
- package/dist/types/nodefony/config/config.d.ts +4 -4
- package/dist/types/nodefony/interfaces/IHttpConfig.d.ts +14 -10
- package/dist/types/nodefony/service/certificates.d.ts +33 -2
- package/dist/types/nodefony/service/http-kernel.d.ts +2 -2
- package/dist/types/nodefony/src/context/domainMatcher.d.ts +25 -2
- package/dist/types/nodefony/src/proxy/generateProxyConfig.d.ts +24 -0
- package/docs/cookies.md +2 -2
- package/docs/observabilite.md +2 -2
- package/docs/rate-limit.md +5 -5
- package/docs/servers.md +39 -39
- package/docs/session.md +8 -8
- package/docs/upload.md +3 -3
- package/package.json +6 -6
|
@@ -10,7 +10,16 @@ type ForgeModule = typeof import("node-forge");
|
|
|
10
10
|
export type CertHash = "sha256" | "sha384" | "sha512";
|
|
11
11
|
/** Stratégie de fourniture du certificat exposée en configuration. */
|
|
12
12
|
export type CertStrategyConfig = "auto" | "mkcert" | "selfsigned" | "explicit";
|
|
13
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Options de génération du certificat AUTO-SIGNÉ (node-forge, JavaScript pur —
|
|
15
|
+
* aucun binaire externe n'est invoqué).
|
|
16
|
+
*
|
|
17
|
+
* ⚠️ Portée : ces réglages ne valent QUE pour la stratégie `selfsigned`. Sous
|
|
18
|
+
* `mkcert` (le défaut en développement) ils sont ignorés — mkcert ne reçoit que
|
|
19
|
+
* les noms d'hôtes — et sous `strategy: "explicit"` le certificat est fourni,
|
|
20
|
+
* donc rien n'est généré.
|
|
21
|
+
*/
|
|
22
|
+
export interface SelfSignedOptions {
|
|
14
23
|
/** Taille de la clé RSA (bits). */
|
|
15
24
|
size: number;
|
|
16
25
|
/** Algorithme de hachage de la signature (jamais SHA-1). */
|
|
@@ -48,7 +57,7 @@ export interface CertificateOptions {
|
|
|
48
57
|
* (Let's Encrypt, ingress, reverse-proxy) : Nodefony n'est pas une CA.
|
|
49
58
|
*/
|
|
50
59
|
strategy?: CertStrategyConfig;
|
|
51
|
-
|
|
60
|
+
selfSigned: SelfSignedOptions;
|
|
52
61
|
dev: CertificateDevOptions;
|
|
53
62
|
san?: CertificateSanOptions;
|
|
54
63
|
/** Permissions POSIX de la clé privée écrite (0600 = owner-only). */
|
|
@@ -137,6 +146,28 @@ declare class Certificate extends Service {
|
|
|
137
146
|
loadForge(): Promise<ForgeModule>;
|
|
138
147
|
/** Accès au backend forge déjà chargé (lève si `loadForge` n'a pas été appelé). */
|
|
139
148
|
private get forgeLib();
|
|
149
|
+
/**
|
|
150
|
+
* Fabrique-t-on un certificat au démarrage ?
|
|
151
|
+
*
|
|
152
|
+
* 🔴 Seulement si un serveur TLS est ACTIF. Sans cette question, le hook
|
|
153
|
+
* ci-dessous écrit dans `nodefony/config/certificates` à CHAQUE boot — y
|
|
154
|
+
* compris celui d'une application qui a coupé son écoute TLS, et y compris un
|
|
155
|
+
* run de console qui n'ouvre aucun port.
|
|
156
|
+
*
|
|
157
|
+
* Ce qu'il en coûtait, mesuré sur une image générée : le code d'une image
|
|
158
|
+
* appartient à `root` et le processus tourne en `1000` (c'est voulu — une
|
|
159
|
+
* application qui peut réécrire son propre `dist/` offre à une faille un moyen
|
|
160
|
+
* de PERSISTER). Le `mkdir` mourait donc en `EACCES`, le hook de boot était
|
|
161
|
+
* « critique », et l'application ne démarrait PAS — quel que soit son préset.
|
|
162
|
+
* L'erreur nommait un dossier de certificats sur une application qui n'en veut
|
|
163
|
+
* aucun : elle envoyait chercher du côté du TLS un défaut de permission.
|
|
164
|
+
*
|
|
165
|
+
* C'est aussi ce que le gabarit d'application promet en toutes lettres : en
|
|
166
|
+
* production, l'écoute TLS est coupée tant qu'aucun port HTTPS n'est demandé,
|
|
167
|
+
* précisément pour ne PAS fabriquer une clé RSA à chaque démarrage de chaque
|
|
168
|
+
* exemplaire. La promesse était écrite ; rien ne la tenait.
|
|
169
|
+
*/
|
|
170
|
+
private get tlsWanted();
|
|
140
171
|
init(): Promise<this>;
|
|
141
172
|
/**
|
|
142
173
|
* Numéro de série X.509 — RFC 5280 §4.1.2.2 : entier positif unique par CA.
|
|
@@ -4,7 +4,7 @@ import type { Controller } from "@nodefony/framework";
|
|
|
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";
|
|
7
|
-
import { type
|
|
7
|
+
import { type ITrustedHostsConfig } from "../src/context/domainMatcher.js";
|
|
8
8
|
import http from "node:http";
|
|
9
9
|
import http2 from "node:http2";
|
|
10
10
|
import type { IncomingMessage } from "node:http";
|
|
@@ -79,7 +79,7 @@ declare class HttpKernel extends Service implements IHttpKernelInterface {
|
|
|
79
79
|
ca: string;
|
|
80
80
|
serverStatic: Statics | null;
|
|
81
81
|
domain: string;
|
|
82
|
-
trustedHosts?:
|
|
82
|
+
trustedHosts?: ITrustedHostsConfig;
|
|
83
83
|
domainCheck: boolean;
|
|
84
84
|
regAlias: RegExp[];
|
|
85
85
|
module: Module;
|
|
@@ -28,7 +28,7 @@ export type DomainPattern = string | RegExp;
|
|
|
28
28
|
* filtre déjà le `Host`, cf doctrine cloud-native).
|
|
29
29
|
* - `string` / `string[]` : patterns additionnels (exact ou `*`-wildcard).
|
|
30
30
|
*/
|
|
31
|
-
export type
|
|
31
|
+
export type ITrustedHostsConfig = boolean | DomainPattern | DomainPattern[];
|
|
32
32
|
/**
|
|
33
33
|
* Compile UN pattern de domaine en `RegExp` selon la politique sûre.
|
|
34
34
|
*
|
|
@@ -56,7 +56,30 @@ export declare function compileDomainPatterns(patterns: DomainPattern | DomainPa
|
|
|
56
56
|
* @param isDev - vrai en environnement `development` (ajoute le loopback).
|
|
57
57
|
* @returns liste de `RegExp` pour {@link isDomainAllowed}.
|
|
58
58
|
*/
|
|
59
|
-
export declare function compileTrustedHosts(domain: string, trusted:
|
|
59
|
+
export declare function compileTrustedHosts(domain: string, trusted: ITrustedHostsConfig | undefined, isDev: boolean): RegExp[];
|
|
60
|
+
/**
|
|
61
|
+
* Rend les NOMS d'hôtes que la barrière `trustedHosts` accepte — la même
|
|
62
|
+
* politique que {@link compileTrustedHosts}, mais lisible par un humain ou par
|
|
63
|
+
* un générateur de configuration (`server_name` nginx, `hdr(host)` haproxy).
|
|
64
|
+
*
|
|
65
|
+
* Pourquoi une seconde lecture de la même règle : une `RegExp` ne se réécrit pas
|
|
66
|
+
* en nom d'hôte. La commande `proxy:generate` lisait donc `trustedHosts` à sa
|
|
67
|
+
* façon, en le supposant TOUJOURS `string[]` — alors que sa valeur par DÉFAUT
|
|
68
|
+
* est `false`, celle de toute application générée. Une politique lue à deux
|
|
69
|
+
* endroits finit par diverger : ici les deux fonctions partent de la même
|
|
70
|
+
* valeur, et la règle « le domaine canonique est toujours accepté » n'est
|
|
71
|
+
* écrite qu'une fois.
|
|
72
|
+
*
|
|
73
|
+
* Le loopback de développement n'en fait volontairement pas partie : une
|
|
74
|
+
* configuration de proxy décrit un déploiement, pas la machine de l'auteur.
|
|
75
|
+
*
|
|
76
|
+
* @param domain - domaine canonique du serveur (`kernel.domain`).
|
|
77
|
+
* @param trusted - config `http.trustedHosts` (optionnelle).
|
|
78
|
+
* @returns les noms acceptés, sans doublon. **Vide** si `trusted === true`
|
|
79
|
+
* (bypass : le proxy filtre déjà le `Host`, aucun nom n'est à imposer) ; les
|
|
80
|
+
* motifs `RegExp` sont écartés, faute d'être exprimables en nom d'hôte.
|
|
81
|
+
*/
|
|
82
|
+
export declare function resolveTrustedHostNames(domain: string, trusted: ITrustedHostsConfig | undefined): string[];
|
|
60
83
|
/**
|
|
61
84
|
* Teste un `Host` entrant contre une liste de `RegExp` pré-compilée.
|
|
62
85
|
*
|
|
@@ -16,6 +16,22 @@ export interface ProxyStaticMount {
|
|
|
16
16
|
/** Dossier absolu servi. */
|
|
17
17
|
dir: string;
|
|
18
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* Terminaison TLS au frontal — **nginx uniquement**.
|
|
21
|
+
*
|
|
22
|
+
* Les chemins sont ceux vus par le PROXY à l'exécution, jamais ceux de la
|
|
23
|
+
* machine qui a généré la configuration : une clé privée n'entre pas dans une
|
|
24
|
+
* image (une couche reste lisible même effacée plus loin), elle se MONTE au
|
|
25
|
+
* déploiement — volume compose, secret Kubernetes.
|
|
26
|
+
*/
|
|
27
|
+
export interface ProxyTlsTermination {
|
|
28
|
+
/** Chaîne de certificats servie au client (`fullchain.pem`). */
|
|
29
|
+
certPath: string;
|
|
30
|
+
/** Clé privée correspondante (`privkey.pem`). */
|
|
31
|
+
keyPath: string;
|
|
32
|
+
/** Port d'écoute TLS du proxy. */
|
|
33
|
+
listen: number;
|
|
34
|
+
}
|
|
19
35
|
/** Modèle d'introspection consommé par les générateurs. */
|
|
20
36
|
export interface ProxyIntrospection {
|
|
21
37
|
/** `server_name` (hôtes de confiance, IP exclues). Vide → `_` (catch-all). */
|
|
@@ -55,6 +71,14 @@ export interface ProxyIntrospection {
|
|
|
55
71
|
* saines.
|
|
56
72
|
*/
|
|
57
73
|
keepaliveIntervalMs: number;
|
|
74
|
+
/**
|
|
75
|
+
* Terminaison TLS au frontal, ou `null` pour n'écouter qu'en clair.
|
|
76
|
+
*
|
|
77
|
+
* **nginx seulement** : haproxy exige un PEM COMBINÉ (certificat + clé dans
|
|
78
|
+
* un même fichier), que Nodefony ne fabrique pas — la commande REFUSE donc
|
|
79
|
+
* l'option sur cette cible plutôt que de l'accepter et de la jeter.
|
|
80
|
+
*/
|
|
81
|
+
tls: ProxyTlsTermination | null;
|
|
58
82
|
}
|
|
59
83
|
/** Valeurs par défaut d'un modèle d'introspection (complété par la commande). */
|
|
60
84
|
export declare const defaultIntrospection: ProxyIntrospection;
|
package/docs/cookies.md
CHANGED
|
@@ -284,8 +284,8 @@ posé pendant la **phase HTTP** qui précède l'upgrade. La forme d'un cookie d
|
|
|
284
284
|
|
|
285
285
|
Les cookies **applicatifs** ne se configurent pas par schéma : on les construit dans le code, avec les
|
|
286
286
|
défauts sûrs de `cookieDefaultSettings` (`cookie.ts:43`). Le seul cookie **piloté par la config** est celui
|
|
287
|
-
de la **session** — bloc Zod `sessionCookieSchema` (`config.ts:
|
|
288
|
-
(`config.ts:
|
|
287
|
+
de la **session** — bloc Zod `sessionCookieSchema` (`config.ts:748`), avec notamment `hostPrefix`
|
|
288
|
+
(`config.ts:770`) qui décide du préfixe `__Host-`. Tout cela est documenté dans [Sessions](session.md) :
|
|
289
289
|
cette page ne le duplique pas.
|
|
290
290
|
|
|
291
291
|
Le nom effectif du cookie de session (avec ou sans `__Host-` selon le transport) est calculé par
|
package/docs/observabilite.md
CHANGED
|
@@ -112,7 +112,7 @@ un `requestId`, un `traceparent` et un contrat de logger **uniques** couvrent le
|
|
|
112
112
|
|
|
113
113
|
**Le `requestId` est un citoyen du contexte, pas un décor.** Il naît dans le constructeur de base
|
|
114
114
|
`Context.requestId = randomUUID()` (`Context.ts:244`), voyage dans l'ALS via `RequestContext.run(...)`
|
|
115
|
-
(`http-kernel.ts:
|
|
115
|
+
(`http-kernel.ts:435` pour HTTP, `http-kernel.ts:435` pour WS), et se lit de n'importe où avec
|
|
116
116
|
`RequestContext.getRequestId()` — un controller, un service, un adapter ORM, sans jamais le threader.
|
|
117
117
|
|
|
118
118
|
**La ligne de bilan est branchable.** Le kernel ne code pas un format en dur : il consulte un
|
|
@@ -420,7 +420,7 @@ instancié **qu'en dev** (fuite d'info + coût en prod).
|
|
|
420
420
|
| Le `X-Request-Id` que j'envoie n'est pas réfléchi | Valeur non conforme (espace, CR/LF, non-ASCII, > 128) → **rejetée** | Utiliser `[A-Za-z0-9._-]{1,128}` (UUID/nanoid/traceparent OK) — sinon UUID serveur |
|
|
421
421
|
| Les logs de fin de requête n'ont pas de `requestId` | Ils sont émis hors bulle ALS | Déjà géré : l'override `log()` rouvre une micro-bulle (`Context.ts:459`) |
|
|
422
422
|
| Réponse HTTP/2 sans `x-request-id` | Chemin de réponse h2 distinct du 1.1 | Déjà géré (`http2/Response.ts:71`) — le port 5152 réfléchit aussi |
|
|
423
|
-
| Pas de `traceparent` renvoyé sur un WebSocket | `ws` n'expose pas l'écriture d'en-tête au handshake | Attendu — la trace WS reste propagée en ALS (`http-kernel.ts:
|
|
423
|
+
| Pas de `traceparent` renvoyé sur un WebSocket | `ws` n'expose pas l'écriture d'en-tête au handshake | Attendu — la trace WS reste propagée en ALS (`http-kernel.ts:1505`) |
|
|
424
424
|
| Frame WS binaire loggée en `{"0":..,"1":..}` | Sérialisation naïve d'un Buffer | Déjà géré : résumé `[binary N B]` (`wsLogContent.ts:63`) |
|
|
425
425
|
| Le format de log ne change pas malgré la config | Un `setRequestLogger(...)` programmatique gagne sur la config | L'override est volontaire (last setter wins) — retirer l'appel, ou le régler |
|
|
426
426
|
| Logs d'audit trop volumineux en prod | `stack` sérialisée, ou 100 % des 2xx audités | `includeStack:false` (défaut prod) + `sampleRate` via `setRequestLogger` |
|
package/docs/rate-limit.md
CHANGED
|
@@ -106,7 +106,7 @@ Trois choix structurent l'implémentation, et chacun est un compromis assumé.
|
|
|
106
106
|
|
|
107
107
|
**Désactivé par défaut — opt-in explicite.** En cloud-native, le plafond par IP est souvent mieux placé
|
|
108
108
|
à l'**ingress/gateway** (il voit tout le trafic, tous les pods, et rejette avant le coût TLS). Le module
|
|
109
|
-
laisse donc `rateLimit` désarmé par défaut (`config.ts:
|
|
109
|
+
laisse donc `rateLimit` désarmé par défaut (`config.ts:1065`) : `null` tant qu'on ne l'active pas → **0
|
|
110
110
|
coût** sur le chemin chaud. On l'active quand on n'a **pas** d'edge devant soi (bare-metal, VPS), ou en
|
|
111
111
|
défense en profondeur.
|
|
112
112
|
|
|
@@ -147,7 +147,7 @@ export default defineConfig(() => ({
|
|
|
147
147
|
```
|
|
148
148
|
|
|
149
149
|
Les trois clés `enabled` / `windowS` / `max` sont **éditables à chaud** (`runtimeMutable`) : le kernel
|
|
150
|
-
reconstruit le compteur sans redémarrage (`configureRateLimit()`, `http-kernel.ts:
|
|
150
|
+
reconstruit le compteur sans redémarrage (`configureRateLimit()`, `http-kernel.ts:416`).
|
|
151
151
|
|
|
152
152
|
### 2. Observer le 429 et les en-têtes
|
|
153
153
|
|
|
@@ -214,14 +214,14 @@ Autour de ce cœur, le kernel orchestre le cycle de vie :
|
|
|
214
214
|
|
|
215
215
|
## ⚙️ Configuration
|
|
216
216
|
|
|
217
|
-
Table dérivée de `rateLimitSchema` (`config.ts:
|
|
217
|
+
Table dérivée de `rateLimitSchema` (`config.ts:868`). Tout est optionnel : ce sont les défauts du
|
|
218
218
|
schéma, écrits ici pour les montrer.
|
|
219
219
|
|
|
220
220
|
| Option | Type | Défaut | Effet | Chaud |
|
|
221
221
|
| ------------- | ------------ | --------- | -------------------------------------------------------------------------------- | ----- |
|
|
222
222
|
| `enabled` | bool | `false` | Arme le rate-limit (HTTP **et** handshakes WS, même compteur) (`config.ts:849`). | oui |
|
|
223
223
|
| `windowS` | int (s) | `60` | Largeur de la fenêtre fixe ; le compteur par IP repart à zéro (`config.ts:860`). | oui |
|
|
224
|
-
| `max` | int | `300` | Requêtes/IP/fenêtre ; au-delà `429` + `Retry-After` (`config.ts:
|
|
224
|
+
| `max` | int | `300` | Requêtes/IP/fenêtre ; au-delà `429` + `Retry-After` (`config.ts:892`). | oui |
|
|
225
225
|
| `maxTracked` | int (≥ 1000) | `100 000` | Borne mémoire : IP suivies ; au cap, purge puis éviction FIFO (`config.ts:883`). | non |
|
|
226
226
|
| `gcIntervalS` | int (s) | `300` | Intervalle du balayage de purge des fenêtres expirées, hors hot-path. | non |
|
|
227
227
|
| `gcJitter` | bool | `true` | Étale le tick GC d'un jitter aléatoire (anti-thundering-herd multi-pod). | non |
|
|
@@ -242,7 +242,7 @@ Et un réglage **séparé**, propre au WebSocket, à la racine du module :
|
|
|
242
242
|
Un WebSocket ne peut **pas** recevoir un `429` : au moment où le rate-limit décide, le `101 Switching
|
|
243
243
|
Protocols` est déjà parti sur le fil (émis par la bibliothèque `ws`). Le refoulement se fait donc par
|
|
244
244
|
une **fermeture RFC 6455 `1013 Try Again Later`**, décidée dans `onWebsocketRequest()`
|
|
245
|
-
(`http-kernel.ts:
|
|
245
|
+
(`http-kernel.ts:1540`) — **avant** `enterScope`, l'ALS et le pipeline, comme le `429` HTTP.
|
|
246
246
|
|
|
247
247
|
Deux plafonds distincts, tous deux par IP forwarded-aware :
|
|
248
248
|
|
package/docs/servers.md
CHANGED
|
@@ -128,12 +128,12 @@ Nodefony crée un serveur HTTP/2 sécurisé avec `allowHTTP1: true` (`ServerHttp
|
|
|
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:1540`) —, 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:1035`). 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:1214`), 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:226`, appelées par le kernel — `Kernel.ts:
|
|
267
|
+
(`ServerHttp.showBanner()`, `server-http.ts:226`, appelées par le kernel — `Kernel.ts:545`) :
|
|
268
268
|
|
|
269
269
|
```text
|
|
270
270
|
Server Listen on http://127.0.0.1:5151 Family: IPv4 Protocol : 1.1
|
|
@@ -399,8 +399,8 @@ C'est la distinction la plus utile de cette page, et celle qu'on rate le plus so
|
|
|
399
399
|
|
|
400
400
|
| Question | Où ça se règle | Source |
|
|
401
401
|
| ----------------------------------------- | ------------------------------ | --------------------------------------------------------- |
|
|
402
|
-
| **Quels** serveurs, sur **quels ports** ? | `servers` (config d'app) | `serversSchema` (`src/nodefony/src/config/schema.ts:
|
|
403
|
-
| **Comment** ces serveurs se comportent ? | `use("@nodefony/http", { … })` | `httpConfigSchema` (`config.ts:
|
|
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:953`) |
|
|
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.
|
|
@@ -422,7 +422,7 @@ Défauts matérialisés dans `defaultAppConfig` (`src/nodefony/src/config/defaul
|
|
|
422
422
|
### Niveau 2 — le transport HTTP / HTTPS
|
|
423
423
|
|
|
424
424
|
Table dérivée de `httpServerSchema` (`config.ts:257`) ; la section `https` reprend les mêmes clés et en
|
|
425
|
-
ajoute une (`httpsServerSchema`, `config.ts:
|
|
425
|
+
ajoute une (`httpsServerSchema`, `config.ts:336`).
|
|
426
426
|
|
|
427
427
|
| Option | Type | Défaut | Effet |
|
|
428
428
|
| ---------------------------- | ----- | -------- | -------------------------------------------------------------------------------- |
|
|
@@ -441,7 +441,7 @@ quelle à Node. C'est délibéré — un schéma strict effacerait silencieuseme
|
|
|
441
441
|
|
|
442
442
|
### Niveau 2 — HTTP/2
|
|
443
443
|
|
|
444
|
-
Depuis `http2Schema` (`config.ts:
|
|
444
|
+
Depuis `http2Schema` (`config.ts:353`), appliqué seulement si défini
|
|
445
445
|
(`maxSessionMemory`, `server-https.ts:197`).
|
|
446
446
|
|
|
447
447
|
| Option | Type | Défaut | Effet |
|
|
@@ -451,16 +451,16 @@ Depuis `http2Schema` (`config.ts:332`), appliqué seulement si défini
|
|
|
451
451
|
|
|
452
452
|
### Niveau 2 — WebSocket (`websocket` et `websocketSecure`)
|
|
453
453
|
|
|
454
|
-
Depuis `websocketSchema` (`config.ts:
|
|
455
|
-
lit `websocketSecure` (`config.ts:
|
|
454
|
+
Depuis `websocketSchema` (`config.ts:517`). Les deux sections partagent la forme et les défauts ; le WSS
|
|
455
|
+
lit `websocketSecure` (`config.ts:1043`).
|
|
456
456
|
|
|
457
457
|
| Option | Type | Défaut | Effet |
|
|
458
458
|
| ------------------------ | ------------------- | ------- | ---------------------------------------------------------------------------------- |
|
|
459
459
|
| `keepaliveInterval` | ms | `20000` | Intervalle des pings — détecte les connexions zombies. |
|
|
460
460
|
| `keepaliveGracePeriod` | ms | `10000` | Délai de grâce après un ping sans réponse avant fermeture. |
|
|
461
461
|
| `closeTimeout` | ms | `5000` | Délai de fermeture propre avant destruction de la socket. |
|
|
462
|
-
| `maxPayload` | octets | `1 MiB` | Taille max d'un message entrant → au-delà, **close 1009** (`config.ts:
|
|
463
|
-
| `allowedOrigins` | bool \| str \| list | `false` | Allowlist d'`Origin` au handshake — **anti-CSWSH** (`config.ts:
|
|
462
|
+
| `maxPayload` | octets | `1 MiB` | Taille max d'un message entrant → au-delà, **close 1009** (`config.ts:543`). |
|
|
463
|
+
| `allowedOrigins` | bool \| str \| list | `false` | Allowlist d'`Origin` au handshake — **anti-CSWSH** (`config.ts:552`). |
|
|
464
464
|
| `perMessageDeflate` | bool \| objet | `false` | Compression RFC 7692. Désactivée par défaut : coût CPU/RAM + risque de _zip bomb_. |
|
|
465
465
|
| `skipUTF8Validation` | bool | `false` | Désactive la validation UTF-8 des frames texte (RFC 6455 §8.1). À laisser `false`. |
|
|
466
466
|
| `autoPong` | bool | `true` | Répond automatiquement aux pings entrants (RFC 6455 §5.5.2-3). À laisser `true`. |
|
|
@@ -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:418`).
|
|
541
541
|
|
|
542
542
|
### Les quatre stratégies
|
|
543
543
|
|
|
544
|
-
Réglées par `certificates.strategy` (`certificatesSchema`, `config.ts:
|
|
545
|
-
`Certificate.resolveStrategy()` (`certificates.ts:
|
|
544
|
+
Réglées par `certificates.strategy` (`certificatesSchema`, `config.ts:475`), résolues par
|
|
545
|
+
`Certificate.resolveStrategy()` (`certificates.ts:379`).
|
|
546
546
|
|
|
547
547
|
| Stratégie | Quand l'utiliser | Ce qui se passe |
|
|
548
548
|
| --------------- | ------------------------------------------- | --------------------------------------------------------------------------- |
|
|
@@ -572,13 +572,13 @@ export default defineConfig(() => ({
|
|
|
572
572
|
> [!TIP]
|
|
573
573
|
> Pour un HTTPS de développement **sans avertissement navigateur** (indispensable au HMR cross-origin
|
|
574
574
|
> et au WSS) : `brew install mkcert nss && mkcert -install`. Nodefony le détecte tout seul, sinon il
|
|
575
|
-
> l'annonce et retombe sur l'auto-signé (`certificates.ts:
|
|
575
|
+
> l'annonce et retombe sur l'auto-signé (`certificates.ts:401`).
|
|
576
576
|
|
|
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:227`) : en production avec un certificat
|
|
581
|
+
fourni, elle n'entre jamais dans le processus (`certificates.ts:227`).
|
|
582
582
|
|
|
583
583
|
### Conformité de l'auto-signé
|
|
584
584
|
|
|
@@ -587,16 +587,16 @@ invalid »). Celui de Nodefony respecte les règles qui comptent :
|
|
|
587
587
|
|
|
588
588
|
| Exigence | Norme | Mise en œuvre |
|
|
589
589
|
| --------------------------------------- | -------------------- | --------------------------------------------------------- |
|
|
590
|
-
| Signature SHA-256, **jamais** SHA-1 | RFC 5280, CA/B Forum | `
|
|
591
|
-
| Numéro de série aléatoire 128 bits | RFC 5280 §4.1.2.2 | `Certificate.generateSerialHex()` (`certificates.ts:
|
|
592
|
-
| Le SAN fait foi, pas le CN | RFC 6125 | SAN dérivé du kernel si non fourni (`config.ts:
|
|
593
|
-
| `notBefore` reculé (décalage d'horloge) | pratique | `
|
|
594
|
-
| Clé privée non lisible par tous | hygiène | `privateKeyMode` `0600` (`config.ts:
|
|
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:264`) |
|
|
592
|
+
| Le SAN fait foi, pas le CN | RFC 6125 | SAN dérivé du kernel si non fourni (`config.ts:444`) |
|
|
593
|
+
| `notBefore` reculé (décalage d'horloge) | pratique | `selfSigned.backdateMinutes`, défaut 5 (`config.ts:415`) |
|
|
594
|
+
| Clé privée non lisible par tous | hygiène | `privateKeyMode` `0600` (`config.ts:499`) |
|
|
595
595
|
|
|
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:542`) 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
|
|
|
@@ -608,7 +608,7 @@ Dès qu'un proxy est devant l'application, trois questions se posent — et troi
|
|
|
608
608
|
|
|
609
609
|
Sans barrière, n'importe quel client peut envoyer `X-Forwarded-For: 1.2.3.4` et usurper son IP :
|
|
610
610
|
contournement de rate-limit, journaux d'audit falsifiés. Le défaut est donc **`false` — ces en-têtes
|
|
611
|
-
sont ignorés** (`config.ts:
|
|
611
|
+
sont ignorés** (`config.ts:965`), et l'IP retenue est celle de la socket réelle, non falsifiable.
|
|
612
612
|
|
|
613
613
|
| Valeur | Sens |
|
|
614
614
|
| -------------------------------------------- | ------------------------------------------------------------------ |
|
|
@@ -628,7 +628,7 @@ Barrière testée **avant le routage**, contre l'injection d'en-tête `Host`. Le
|
|
|
628
628
|
kernel est toujours accepté, plus le loopback en développement (`HttpKernel.compileAlias()`,
|
|
629
629
|
`http-kernel.ts:936`). `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
|
-
filtre déjà le `Host` (`config.ts:
|
|
631
|
+
filtre déjà le `Host` (`config.ts:974`).
|
|
632
632
|
|
|
633
633
|
### « Cette page a-t-elle le droit d'ouvrir un WebSocket ? » → `allowedOrigins`
|
|
634
634
|
|
|
@@ -664,7 +664,7 @@ rate-limit**. Un kubelet qui reçoit un `429` croit le pod mort → cascade de r
|
|
|
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:925`). |
|
|
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). |
|
|
@@ -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:1540`). 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
789
|
(`rateLimiter`, `http-kernel.ts:287`), 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:1052`). 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
|
|
|
@@ -817,7 +817,7 @@ par seconde. Les choix visibles dans le code :
|
|
|
817
817
|
- **Fichiers statiques en repli** — depuis la bascule « router d'abord », une requête qui matche une
|
|
818
818
|
route ne paie plus l'appel disque de `serve-static` (**+28 % de requêtes par seconde** mesurés en
|
|
819
819
|
production mono-processus).
|
|
820
|
-
- **`node-forge` jamais chargé en production** avec un certificat fourni (`certificates.ts:
|
|
820
|
+
- **`node-forge` jamais chargé en production** avec un certificat fourni (`certificates.ts:227`).
|
|
821
821
|
|
|
822
822
|
Ordre de grandeur mesuré : un processus Node saturé sur un cœur tient environ 400 requêtes/s en
|
|
823
823
|
boucle locale avec dégradation gracieuse (1600 connexions concurrentes, aucun crash) ; côté WebSocket,
|
|
@@ -836,17 +836,17 @@ demande le backplane realtime.
|
|
|
836
836
|
| ------------------------------------- | ------------------ | -------------------------------------------------------------------------- |
|
|
837
837
|
| HTTP/1.1 (sémantique, message) | RFC 9110, 9112 | `node:http` + pipeline `HttpKernel.onHttpRequest()` (`http-kernel.ts:819`) |
|
|
838
838
|
| HTTP/2 | RFC 9113 | `ServerHttps.createServerH2()` (`server-https.ts:174`) |
|
|
839
|
-
| HTTP/2 Rapid Reset | CVE-2023-44487 | `maxConcurrentStreams` (`config.ts:
|
|
839
|
+
| HTTP/2 Rapid Reset | CVE-2023-44487 | `maxConcurrentStreams` (`config.ts:355`) |
|
|
840
840
|
| En-têtes trop volumineux → 431 | RFC 6585 §5 | `handleClientError()` (`clientError.ts:25`) |
|
|
841
|
-
| WebSocket — protocole | RFC 6455 | `ws@8` + options (`config.ts:
|
|
841
|
+
| WebSocket — protocole | RFC 6455 | `ws@8` + options (`config.ts:496`) |
|
|
842
842
|
| WebSocket — Close 1001 « Going Away » | RFC 6455 §7.4.1 | `Websocket.terminate()` (`server-websocket.ts:134`) |
|
|
843
|
-
| WebSocket — 1009 « Message Too Big » | RFC 6455 §7.4.1 | `maxPayload` (`config.ts:
|
|
844
|
-
| WebSocket — validation UTF-8 | RFC 6455 §8.1 | `skipUTF8Validation` (`config.ts:
|
|
845
|
-
| WebSocket — compression | RFC 7692 | `perMessageDeflate` (`config.ts:
|
|
843
|
+
| WebSocket — 1009 « Message Too Big » | RFC 6455 §7.4.1 | `maxPayload` (`config.ts:543`) |
|
|
844
|
+
| WebSocket — validation UTF-8 | RFC 6455 §8.1 | `skipUTF8Validation` (`config.ts:623`) |
|
|
845
|
+
| WebSocket — compression | RFC 7692 | `perMessageDeflate` (`config.ts:567`) |
|
|
846
846
|
| CSWSH (Origin au handshake) | OWASP WSTG-CLNT-10 | `HttpKernel.checkWebsocketOrigin()` (`http-kernel.ts:599`) |
|
|
847
847
|
| En-têtes forwarded | RFC 7239 | `resolveForwarded()` (`forwarded.ts:253`) |
|
|
848
|
-
| Certificat — série, SAN, extensions | RFC 5280 | `Certificate.generateSerialHex()` (`certificates.ts:
|
|
849
|
-
| Certificat — identité par le SAN | RFC 6125 | `sanSchema` (`config.ts:
|
|
848
|
+
| Certificat — série, SAN, extensions | RFC 5280 | `Certificate.generateSerialHex()` (`certificates.ts:264`) |
|
|
849
|
+
| Certificat — identité par le SAN | RFC 6125 | `sanSchema` (`config.ts:442`) |
|
|
850
850
|
|
|
851
851
|
## ⚠️ Pièges (symptôme → cause → correction)
|
|
852
852
|
|
|
@@ -863,7 +863,7 @@ demande le backplane realtime.
|
|
|
863
863
|
| Cascade de redémarrages sous charge | Sonde de santé soumise au rate-limit | Déjà géré : les probes court-circuitent avant le rate-limit (`http-kernel.ts:848`) |
|
|
864
864
|
| `curl --http2` renvoie du HTTP/1.1 | `servers.https.protocol: "1.1"`, ou client sans ALPN | Passer `protocol: "2.0"` (défaut) et vérifier le client |
|
|
865
865
|
| Avertissement navigateur en HTTPS de développement | Certificat auto-signé (mkcert absent) | `brew install mkcert nss && mkcert -install`, puis redémarrer |
|
|
866
|
-
| Le certificat n'est pas régénéré après un changement de domaine | On croit qu'un fichier présent suffit | Déjà géré : le SAN est vérifié (`certificates.ts:
|
|
866
|
+
| Le certificat n'est pas régénéré après un changement de domaine | On croit qu'un fichier présent suffit | Déjà géré : le SAN est vérifié (`certificates.ts:555`) |
|
|
867
867
|
| `strategy: "explicit"` fait échouer le boot | `key`/`cert` absents de la configuration | Fournir les deux chemins — l'échec est volontaire, jamais un repli silencieux |
|
|
868
868
|
| Une IP falsifiée passe dans les journaux d'audit | `trustProxy` accordé trop largement | Restreindre à l'IP/CIDR du proxy, ou revenir à `false` |
|
|
869
869
|
| Handshake WebSocket refusé en `1008` | `Origin` non autorisée (anti-CSWSH) | Ajouter l'origine dans `websocket.allowedOrigins` |
|
|
@@ -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:853`), 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
|
package/docs/session.md
CHANGED
|
@@ -144,7 +144,7 @@ seule présence d'un paramètre `@Session` — ou si un cookie arrive déjà : c
|
|
|
144
144
|
ni `Set-Cookie`**.
|
|
145
145
|
|
|
146
146
|
**3. Un seul modèle d'état pour le web et le temps réel.** Le même `startSession()` sert
|
|
147
|
-
`HttpKernel.onRequestEnd()` (`http-kernel.ts:
|
|
147
|
+
`HttpKernel.onRequestEnd()` (`http-kernel.ts:1434`) et `HttpKernel.onConnect()` (`http-kernel.ts:1702`) ;
|
|
148
148
|
l'activité HTTP **ou** WS prolonge la même session (`Session.touchIfNeeded()`, `session.ts:421`).
|
|
149
149
|
|
|
150
150
|
**4. L'administration ne voit jamais un identifiant.** Un opérateur manipule une `ref`, HMAC tronqué
|
|
@@ -278,12 +278,12 @@ faute de `Secure` (`Context.getSessionCookieName()`, `Context.ts:714`).
|
|
|
278
278
|
|
|
279
279
|
## ⚙️ Configuration
|
|
280
280
|
|
|
281
|
-
Source unique des défauts : le schéma Zod `sessionSchema` (`config.ts:
|
|
282
|
-
`sessionCookieSchema` (`config.ts:
|
|
281
|
+
Source unique des défauts : le schéma Zod `sessionSchema` (`config.ts:782`) et son sous-schéma
|
|
282
|
+
`sessionCookieSchema` (`config.ts:748`).
|
|
283
283
|
|
|
284
284
|
| Option | Type | Défaut | Effet |
|
|
285
285
|
| ------------------- | ------- | ------------ | --------------------------------------------------------------------------------- |
|
|
286
|
-
| `store` | string | `"auto"` | Backend de persistance — voir la résolution ci-dessous (`config.ts:
|
|
286
|
+
| `store` | string | `"auto"` | Backend de persistance — voir la résolution ci-dessous (`config.ts:795`). |
|
|
287
287
|
| `name` | string | `"nodefony"` | Nom du cookie, préfixé `__Host-` selon `cookie.hostPrefix` (`config.ts:750`). |
|
|
288
288
|
| `strictMode` | bool | `true` | Un identifiant inconnu du store est rejeté → session neuve (anti-fixation). |
|
|
289
289
|
| `idleTimeoutS` | int ≥ 0 | `1800` | Inactivité max (30 min). `0` = pas d'expiration par inactivité (`config.ts:796`). |
|
|
@@ -498,7 +498,7 @@ C'est le différenciateur du framework appliqué à l'état de session : un seul
|
|
|
498
498
|
<!-- prettier-ignore -->
|
|
499
499
|
| Aspect | HTTP | WebSocket |
|
|
500
500
|
| --- | --- | --- |
|
|
501
|
-
| Ouverture | à chaque requête — `startSession()` dans `onRequestEnd()` (`http-kernel.ts:
|
|
501
|
+
| Ouverture | à chaque requête — `startSession()` dans `onRequestEnd()` (`http-kernel.ts:1434`) | **une fois** au handshake — `startSession()` dans `onConnect()` (`http-kernel.ts:1702`) |
|
|
502
502
|
| Lecture du cookie | constructeur du contexte | constructeur, même nom effectif (`WebsocketContext.ts:172`) |
|
|
503
503
|
| Sauvegarde | fin de requête | après **chaque frame** traitée (`WebsocketContext.ts:302`) |
|
|
504
504
|
| Filet de fermeture | — | `once("onFinish")` sauve si non déjà fait (`http-kernel.ts:1185`) |
|
|
@@ -561,13 +561,13 @@ Trois barrières superposées :
|
|
|
561
561
|
**liste blanche** : `ref`, `user`, `authenticated`, `ip`, `ua`, dates. Jamais un `delete` après coup.
|
|
562
562
|
3. La `ref` elle-même est un HMAC tronqué non réversible (`computeSessionRef()`,
|
|
563
563
|
`sessions-service.ts:100`) ; la clé est dérivée du certificat au boot et n'est jamais sérialisée
|
|
564
|
-
(`SessionsService.sessionRef()`, `sessions-service.ts:
|
|
564
|
+
(`SessionsService.sessionRef()`, `sessions-service.ts:528`).
|
|
565
565
|
|
|
566
566
|
### Récapitulatif des défenses actives par défaut
|
|
567
567
|
|
|
568
568
|
| Menace | Défense | Ancrage |
|
|
569
569
|
| --------------------------------- | ------------------------------------------------- | -------------------------------------------------- |
|
|
570
|
-
| Vol par script injecté (XSS) | `HttpOnly` | `sessionCookieSchema` (`config.ts:
|
|
570
|
+
| Vol par script injecté (XSS) | `HttpOnly` | `sessionCookieSchema` (`config.ts:748`) |
|
|
571
571
|
| Interception réseau | `Secure` + `__Host-` sur TLS | `getSessionCookieName()` (`Context.ts:714`) |
|
|
572
572
|
| Requête inter-sites | `SameSite=Lax` par défaut | `defaultCookieOptions` (`cookie.ts:48`) |
|
|
573
573
|
| Fixation (cookie pré-posé) | `strictMode` + régénération au login | `Session.resume()` (`session.ts:189`) |
|
|
@@ -582,7 +582,7 @@ Trois barrières superposées :
|
|
|
582
582
|
|
|
583
583
|
Les signatures vivent dans `.ai/symbols.json` (jamais recopiées ici). Voici les usages réels.
|
|
584
584
|
|
|
585
|
-
**Depuis un contrôleur** — `this.session` est un getter sur le contexte (`Controller.ts:
|
|
585
|
+
**Depuis un contrôleur** — `this.session` est un getter sur le contexte (`Controller.ts:279`) ; un
|
|
586
586
|
paramètre `@Session()` suffit à déclarer l'intent.
|
|
587
587
|
|
|
588
588
|
| Besoin | Appel | Effet |
|
package/docs/upload.md
CHANGED
|
@@ -126,7 +126,7 @@ champs texte restent en mémoire. C'est ce qui rend un endpoint d'upload public
|
|
|
126
126
|
**métadonnée** (`filename`), pas dans le chemin.
|
|
127
127
|
|
|
128
128
|
**Deux budgets, secure-by-default.** Le corps non-multipart est plafonné à **1 MiB** par défaut
|
|
129
|
-
(`maxBodySize`, `http/nodefony/config/config.ts:
|
|
129
|
+
(`maxBodySize`, `http/nodefony/config/config.ts:1026`) — un `POST` JSON géant est rejeté avant d'être bufferisé. Le
|
|
130
130
|
multipart, lui, a ses propres bornes busboy (par fichier, cumul, nombre) qui coupent le flux et
|
|
131
131
|
nettoient les temporaires déjà posés au moindre dépassement (`context/http/Request.ts:481`).
|
|
132
132
|
|
|
@@ -321,8 +321,8 @@ recommandé) et les **getters** de `Controller` (impératif). Les signatures exa
|
|
|
321
321
|
| `@Body() body` | tous les champs parsés (`queryPost`) | `resolveParamArg` `"body"` (`routerDecorators.ts:1178`) |
|
|
322
322
|
| `@Body("label") v` | un seul champ du body | même source, clé (`routerDecorators.ts:1178`) |
|
|
323
323
|
| `@Body({ stream: true }) s: NodeJS.ReadableStream` | le **flux brut**, parse **sauté** | `resolveParamArg` stream (`routerDecorators.ts:1227`) |
|
|
324
|
-
| `this.queryFile` | équivalent getter des fichiers | `Controller.queryFile` (`framework/nodefony/src/Controller.ts:
|
|
325
|
-
| `this.queryPost` | équivalent getter des champs | `Controller.queryPost` (`framework/nodefony/src/Controller.ts:
|
|
324
|
+
| `this.queryFile` | équivalent getter des fichiers | `Controller.queryFile` (`framework/nodefony/src/Controller.ts:239`) |
|
|
325
|
+
| `this.queryPost` | équivalent getter des champs | `Controller.queryPost` (`framework/nodefony/src/Controller.ts:248`) |
|
|
326
326
|
|
|
327
327
|
Les décorateurs `@UploadedFile` / `@UploadedFiles` sont des fabriques de paramètre
|
|
328
328
|
(`routerDecorators.ts:1240`), exportées par `@nodefony/framework` ; leurs interfaces `IUploadedFile` /
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nodefony/http",
|
|
3
|
-
"version": "10.0.0-alpha.
|
|
3
|
+
"version": "10.0.0-alpha.5",
|
|
4
4
|
"description": "Serveurs HTTP, HTTPS, HTTP/2 et WebSocket natifs pour Nodefony : sessions, contextes de requête, certificats TLS",
|
|
5
5
|
"author": "Christophe CAMENSULI <ccamensuli@gmail.com>",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"@types/chai": "5.2.3",
|
|
67
67
|
"@types/mime-types": "3.0.1",
|
|
68
68
|
"@types/ms": "2.1.0",
|
|
69
|
-
"@types/node": "26.
|
|
69
|
+
"@types/node": "26.5.1",
|
|
70
70
|
"@types/node-forge": "1.3.14",
|
|
71
71
|
"@types/qs": "6.15.1",
|
|
72
72
|
"@types/serve-static": "2.2.0",
|
|
@@ -75,17 +75,17 @@
|
|
|
75
75
|
"@types/xml2js": "0.4.14",
|
|
76
76
|
"@vitest/coverage-v8": "5.0.0",
|
|
77
77
|
"chai": "6.2.2",
|
|
78
|
-
"nodefony": "^10.0.0-alpha.
|
|
78
|
+
"nodefony": "^10.0.0-alpha.5",
|
|
79
79
|
"rimraf": "6.1.3",
|
|
80
80
|
"tsx": "4.23.13",
|
|
81
81
|
"vitest": "5.0.0"
|
|
82
82
|
},
|
|
83
|
-
"license": "
|
|
83
|
+
"license": "Apache-2.0",
|
|
84
84
|
"readmeFilename": "README.md",
|
|
85
85
|
"contributors": [],
|
|
86
86
|
"peerDependencies": {
|
|
87
|
-
"nodefony": "^10.0.0-alpha.
|
|
88
|
-
"zod": "^4.
|
|
87
|
+
"nodefony": "^10.0.0-alpha.5",
|
|
88
|
+
"zod": "^4.6.1"
|
|
89
89
|
},
|
|
90
90
|
"files": [
|
|
91
91
|
"dist",
|