@nodefony/http 10.0.0-beta.1 → 10.0.0-beta.3
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 +5 -3
- package/dist/_virtual/{_@oxc-project_runtime@0.151.0 → _@oxc-project_runtime@0.153.0}/helpers/esm/decorate.js +1 -1
- package/dist/_virtual/{_@oxc-project_runtime@0.151.0 → _@oxc-project_runtime@0.153.0}/helpers/esm/decorateMetadata.js +1 -1
- package/dist/_virtual/{_@oxc-project_runtime@0.151.0 → _@oxc-project_runtime@0.153.0}/helpers/esm/decorateParam.js +1 -1
- package/dist/index.js +11 -5
- package/dist/nodefony/command/certificatesCommand.js +0 -1
- package/dist/nodefony/config/config.js +48 -7
- package/dist/nodefony/config/defineModuleConfig.js +1 -1
- package/dist/nodefony/interfaces/IReverseProxy.js +1 -0
- package/dist/nodefony/service/HttpAdminApi.js +0 -1
- package/dist/nodefony/service/audit-logger.js +2 -3
- package/dist/nodefony/service/certificates.js +214 -158
- package/dist/nodefony/service/error-renderer.js +1 -1
- package/dist/nodefony/service/http-kernel.js +221 -47
- package/dist/nodefony/service/reverse-proxy.js +336 -0
- 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-static.js +4 -7
- package/dist/nodefony/service/servers/server-websocket-secure.js +12 -4
- package/dist/nodefony/service/servers/server-websocket.js +12 -4
- package/dist/nodefony/service/servers/wsHeartbeat.js +31 -15
- package/dist/nodefony/service/sessions/sessions-service.js +30 -9
- package/dist/nodefony/service/trace.js +2 -1
- package/dist/nodefony/service/upload/upload-service.js +3 -3
- package/dist/nodefony/service/x509.js +324 -0
- package/dist/nodefony/src/context/Context.js +28 -1
- package/dist/nodefony/src/context/domainMatcher.js +20 -2
- package/dist/nodefony/src/context/forwarded.js +2 -2
- package/dist/nodefony/src/context/http/HttpContext.js +91 -40
- package/dist/nodefony/src/context/http/Request.js +9 -4
- package/dist/nodefony/src/context/http/Response.js +46 -7
- package/dist/nodefony/src/context/http/SseStream.js +350 -0
- package/dist/nodefony/src/context/http/urlFastPath.js +35 -10
- package/dist/nodefony/src/context/responseEnded.js +27 -0
- package/dist/nodefony/src/context/websocket/Response.js +9 -0
- package/dist/nodefony/src/context/websocket/WebsocketContext.js +2 -3
- package/dist/nodefony/src/context/websocket/websocketUrl.js +21 -0
- package/dist/nodefony/src/profiler/Profiler.js +6 -10
- package/dist/nodefony/src/proxy/forward.js +476 -0
- package/dist/nodefony/src/proxy/rules.js +182 -0
- package/dist/nodefony/src/servers/portBinder.js +57 -2
- package/dist/nodefony/src/servers/upgradeDispatch.js +49 -0
- package/dist/nodefony/src/session/session.js +28 -12
- package/dist/types/index.d.ts +5 -0
- package/dist/types/nodefony/config/config.d.ts +17 -0
- package/dist/types/nodefony/interfaces/IContext.d.ts +2 -0
- package/dist/types/nodefony/interfaces/IErrorRenderer.d.ts +1 -1
- package/dist/types/nodefony/interfaces/IReverseProxy.d.ts +67 -0
- package/dist/types/nodefony/interfaces/IRouting.d.ts +16 -0
- package/dist/types/nodefony/interfaces/ISecurity.d.ts +6 -0
- package/dist/types/nodefony/interfaces/index.d.ts +1 -0
- package/dist/types/nodefony/service/audit-logger.d.ts +2 -1
- package/dist/types/nodefony/service/certificates.d.ts +126 -50
- package/dist/types/nodefony/service/error-renderer.d.ts +1 -1
- package/dist/types/nodefony/service/http-kernel.d.ts +111 -5
- package/dist/types/nodefony/service/reverse-proxy.d.ts +156 -0
- package/dist/types/nodefony/service/servers/server-websocket-secure.d.ts +1 -0
- package/dist/types/nodefony/service/servers/server-websocket.d.ts +1 -0
- package/dist/types/nodefony/service/servers/wsHeartbeat.d.ts +26 -0
- package/dist/types/nodefony/service/sessions/sessions-service.d.ts +19 -1
- package/dist/types/nodefony/service/x509.d.ts +86 -0
- package/dist/types/nodefony/src/context/Context.d.ts +22 -2
- package/dist/types/nodefony/src/context/domainMatcher.d.ts +11 -1
- package/dist/types/nodefony/src/context/http/HttpContext.d.ts +47 -0
- package/dist/types/nodefony/src/context/http/Response.d.ts +3 -0
- package/dist/types/nodefony/src/context/http/SseStream.d.ts +155 -0
- package/dist/types/nodefony/src/context/responseEnded.d.ts +20 -0
- package/dist/types/nodefony/src/context/websocket/websocketUrl.d.ts +16 -0
- package/dist/types/nodefony/src/profiler/Profiler.d.ts +1 -0
- package/dist/types/nodefony/src/proxy/forward.d.ts +156 -0
- package/dist/types/nodefony/src/proxy/rules.d.ts +85 -0
- package/dist/types/nodefony/src/servers/portBinder.d.ts +10 -46
- package/dist/types/nodefony/src/servers/upgradeDispatch.d.ts +39 -0
- package/dist/types/nodefony/src/session/session.d.ts +2 -0
- package/docs/cookies.md +13 -13
- package/docs/index.md +9 -3
- package/docs/observabilite.md +18 -18
- package/docs/rate-limit.md +30 -30
- package/docs/reverse-proxy.md +522 -0
- package/docs/servers.md +115 -78
- package/docs/session.md +61 -61
- package/docs/sse.md +283 -0
- package/docs/upload.md +37 -37
- package/package.json +10 -13
|
@@ -1,18 +1,99 @@
|
|
|
1
|
-
import type pkg from "node-forge";
|
|
2
1
|
import { Service, Module } from "nodefony";
|
|
2
|
+
import { type KeyObject } from "node:crypto";
|
|
3
|
+
import { type ICertificateAttribute } from "./x509.js";
|
|
3
4
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* pour la valeur chargée dynamiquement (node-forge est un module `export =`).
|
|
5
|
+
* Accès à la liste d'ancres TLS par défaut du processus — injectable pour
|
|
6
|
+
* éprouver la décision sans toucher la confiance du processus de test.
|
|
7
7
|
*/
|
|
8
|
-
|
|
8
|
+
export interface ITlsTrustStore {
|
|
9
|
+
getCACertificates?: (type?: "default") => string[];
|
|
10
|
+
setDefaultCACertificates?: (certs: ReadonlyArray<string>) => void;
|
|
11
|
+
}
|
|
12
|
+
/** Issue de {@link addDefaultCaCertificate}, CONSTATÉE à l'exécution. */
|
|
13
|
+
export type CaTrustVerdict = "added" | "present" | "unsupported";
|
|
14
|
+
/**
|
|
15
|
+
* Ajoute une autorité aux ancres TLS par défaut du processus (requêtes sortantes
|
|
16
|
+
* `fetch`, `https`, `tls.connect`), sans en retirer aucune.
|
|
17
|
+
*
|
|
18
|
+
* C'est l'équivalent en cours d'exécution de `NODE_EXTRA_CA_CERTS`, qui n'est lu
|
|
19
|
+
* qu'au lancement du processus — trop tôt pour une CA que le boot vient de
|
|
20
|
+
* générer, et que seul un script de banc posait. La capacité se CONSTATE
|
|
21
|
+
* (`tls.setDefaultCACertificates` n'existe qu'à partir de Node 24.5), elle ne se
|
|
22
|
+
* déduit pas d'un numéro de version. Idempotent : une autorité déjà présente
|
|
23
|
+
* n'est pas ajoutée deux fois.
|
|
24
|
+
*
|
|
25
|
+
* @param pem - autorité au format PEM.
|
|
26
|
+
* @param store - accès aux ancres ; `node:tls` par défaut.
|
|
27
|
+
* @returns `added`, `present` (déjà de confiance), ou `unsupported`.
|
|
28
|
+
*/
|
|
29
|
+
export declare function addDefaultCaCertificate(pem: string, store?: ITlsTrustStore): CaTrustVerdict;
|
|
30
|
+
/**
|
|
31
|
+
* Découpe un fichier PEM en ses certificats — une chaîne d'entreprise est
|
|
32
|
+
* souvent livrée en un seul fichier (racine + intermédiaires).
|
|
33
|
+
*
|
|
34
|
+
* Chaque certificat est ajouté SÉPARÉMENT : c'est ce qui rend l'ajout
|
|
35
|
+
* idempotent certificat par certificat, la liste par défaut du processus
|
|
36
|
+
* n'étant faite que de certificats isolés.
|
|
37
|
+
*
|
|
38
|
+
* @param pem - contenu d'un fichier PEM (un ou plusieurs certificats)
|
|
39
|
+
* @returns les certificats, dans l'ordre du fichier ; vide s'il n'y en a aucun
|
|
40
|
+
*/
|
|
41
|
+
export declare function splitPemCertificates(pem: string): string[];
|
|
42
|
+
/** Bilan de {@link trustCaFile}. */
|
|
43
|
+
export interface ICaFileTrust {
|
|
44
|
+
/** Fichier lu, chemin absolu. */
|
|
45
|
+
readonly file: string;
|
|
46
|
+
/** Certificats ajoutés aux ancres du processus. */
|
|
47
|
+
readonly added: number;
|
|
48
|
+
/** Certificats déjà de confiance. */
|
|
49
|
+
readonly present: number;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Ajoute les autorités d'un fichier PEM aux ancres TLS du processus
|
|
53
|
+
* (`certificates.extraCaFiles`) — l'équivalent, porté par l'APPLICATION, de
|
|
54
|
+
* `NODE_EXTRA_CA_CERTS`.
|
|
55
|
+
*
|
|
56
|
+
* `NODE_EXTRA_CA_CERTS` n'est lu qu'au lancement de Node, et se pose dans la
|
|
57
|
+
* syntaxe du shell — `VAR=… npm run dev` n'existe pas sous Windows. Une autorité
|
|
58
|
+
* d'entreprise (Keycloak interne, proxy sortant qui ré-signe) se déclare donc
|
|
59
|
+
* dans la configuration, et sert à TOUTES les requêtes sortantes du processus :
|
|
60
|
+
* découverte OpenID, point de jeton, jeu de clés, appels d'administration.
|
|
61
|
+
*
|
|
62
|
+
* @param file - chemin du fichier, absolu ou relatif à `root`
|
|
63
|
+
* @param root - racine de l'application
|
|
64
|
+
* @param store - accès aux ancres ; `node:tls` par défaut
|
|
65
|
+
* @returns le bilan de l'ajout
|
|
66
|
+
* @throws BootConfigurationError - fichier illisible, sans certificat, ou Node
|
|
67
|
+
* incapable d'élargir ses ancres en cours d'exécution
|
|
68
|
+
*/
|
|
69
|
+
export declare function trustCaFile(file: string, root: string, store?: ITlsTrustStore): Promise<ICaFileTrust>;
|
|
70
|
+
/**
|
|
71
|
+
* Ajoute aux ancres TLS du processus les autorités déclarées
|
|
72
|
+
* (`certificates.extraCaFiles`), au plus tôt : appelé à l'enregistrement du
|
|
73
|
+
* module, avant tout service — toute requête sortante, commande console
|
|
74
|
+
* comprise, les trouve en place.
|
|
75
|
+
*
|
|
76
|
+
* La CA de DÉVELOPPEMENT n'y figure pas : le service de certificats la pose
|
|
77
|
+
* lui-même ({@link Certificate.generateServerCertificates}).
|
|
78
|
+
*
|
|
79
|
+
* @param files - `certificates.extraCaFiles`
|
|
80
|
+
* @param root - racine de l'application
|
|
81
|
+
* @param log - journal du module
|
|
82
|
+
* @throws BootConfigurationError - cf {@link trustCaFile}
|
|
83
|
+
*/
|
|
84
|
+
export declare function trustOutboundAuthorities(files: readonly string[], root: string, log: (message: string, severity: "INFO") => void): Promise<void>;
|
|
85
|
+
/** Paire de clés RSA du certificat auto-signé. */
|
|
86
|
+
export interface IRsaKeyPair {
|
|
87
|
+
privateKey: KeyObject;
|
|
88
|
+
publicKey: KeyObject;
|
|
89
|
+
}
|
|
9
90
|
/** Algorithmes de hachage de signature autorisés (SHA-1 banni). */
|
|
10
91
|
export type CertHash = "sha256" | "sha384" | "sha512";
|
|
11
92
|
/** Stratégie de fourniture du certificat exposée en configuration. */
|
|
12
93
|
export type CertStrategyConfig = "auto" | "mkcert" | "selfsigned" | "explicit";
|
|
13
94
|
/**
|
|
14
|
-
* Options de génération du certificat AUTO-SIGNÉ (node
|
|
15
|
-
*
|
|
95
|
+
* Options de génération du certificat AUTO-SIGNÉ (`node:crypto` — aucun
|
|
96
|
+
* binaire externe ni aucune dépendance tierce).
|
|
16
97
|
*
|
|
17
98
|
* ⚠️ Portée : ces réglages ne valent QUE pour la stratégie `selfsigned`. Sous
|
|
18
99
|
* `mkcert` (le défaut en développement) ils sont ignorés — mkcert ne reçoit que
|
|
@@ -29,7 +110,7 @@ export interface SelfSignedOptions {
|
|
|
29
110
|
/** Recul de `notBefore` (minutes) — tolérance au décalage d'horloge client. */
|
|
30
111
|
backdateMinutes: number;
|
|
31
112
|
/** Attributs du sujet/issuer (commonName, organizationName…). */
|
|
32
|
-
attrs:
|
|
113
|
+
attrs: ICertificateAttribute[];
|
|
33
114
|
}
|
|
34
115
|
/** Options de génération du certificat TLS en mode développement. */
|
|
35
116
|
export interface CertificateDevOptions {
|
|
@@ -37,7 +118,7 @@ export interface CertificateDevOptions {
|
|
|
37
118
|
* Préférer `mkcert` (CA locale ajoutée au trust store système) pour générer
|
|
38
119
|
* le certificat de dev. Donne un HTTPS sans erreur navigateur — indispensable
|
|
39
120
|
* pour les sous-ressources cross-origin (Vite) et le WSS du HMR.
|
|
40
|
-
* Si `mkcert` est absent, on retombe sur un auto-signé
|
|
121
|
+
* Si `mkcert` est absent, on retombe sur un auto-signé (non trusté).
|
|
41
122
|
* Ignoré hors `development`.
|
|
42
123
|
*/
|
|
43
124
|
useMkcert: boolean;
|
|
@@ -96,36 +177,26 @@ export interface CertificateInfo {
|
|
|
96
177
|
*
|
|
97
178
|
* Trois stratégies : `explicit` (certificat fourni en config — le cas de
|
|
98
179
|
* PRODUCTION), `mkcert` (CA locale trustée, confort de dev) et `selfsigned`
|
|
99
|
-
* (auto-
|
|
180
|
+
* (auto-signé, secours). La génération est réservée au
|
|
100
181
|
* DÉVELOPPEMENT : en production sans certificat fourni, le service crie un
|
|
101
182
|
* avertissement (Nodefony n'est pas une autorité de certification).
|
|
102
183
|
*
|
|
103
184
|
* Conformité (génération auto-signée) : signature SHA-256 (jamais SHA-1,
|
|
104
185
|
* RFC 5280 / CA-B Forum), numéro de série aléatoire 128 bits unique
|
|
105
|
-
* (RFC 5280 §4.1.2.2), SAN qui fait foi (RFC
|
|
186
|
+
* (RFC 5280 §4.1.2.2), SAN qui fait foi (RFC 9525), `notBefore` reculé,
|
|
106
187
|
* clé privée écrite en `0600`.
|
|
107
188
|
*/
|
|
108
189
|
declare class Certificate extends Service {
|
|
109
190
|
module: Module;
|
|
110
191
|
files: filesCertType[];
|
|
111
|
-
keysPair:
|
|
112
|
-
certForge: pkg.pki.Certificate | null;
|
|
192
|
+
keysPair: IRsaKeyPair | null;
|
|
113
193
|
ca: Buffer | string | null;
|
|
114
194
|
key: Buffer | string | null;
|
|
115
195
|
cert: Buffer | string | null;
|
|
116
196
|
fullchainPem: Buffer | string | null;
|
|
117
197
|
publicKeyPem: Buffer | string | null;
|
|
118
|
-
rootCertPem: Buffer | string | null;
|
|
119
|
-
intermediateCertPem: Buffer | string | null;
|
|
120
198
|
/** CAROOT résolu de mkcert (null tant que non détecté / indisponible). */
|
|
121
199
|
private mkcertCaRoot;
|
|
122
|
-
/**
|
|
123
|
-
* Backend crypto node-forge, chargé PARESSEUSEMENT. La génération de
|
|
124
|
-
* certificat est un confort de DÉVELOPPEMENT : en production avec un
|
|
125
|
-
* certificat fourni (`strategy: "explicit"`), cette grosse dépendance n'est
|
|
126
|
-
* jamais importée (règle perf-mémoire — pas de dep chargée « au cas où »).
|
|
127
|
-
*/
|
|
128
|
-
private forge;
|
|
129
200
|
path: string;
|
|
130
201
|
serverPath: string;
|
|
131
202
|
caPath: string;
|
|
@@ -138,14 +209,6 @@ declare class Certificate extends Service {
|
|
|
138
209
|
private get certOptions();
|
|
139
210
|
/** Vrai en environnement de développement (mkcert réservé à ce mode). */
|
|
140
211
|
private isDev;
|
|
141
|
-
/**
|
|
142
|
-
* Charge node-forge à la demande (idempotent) — uniquement sur le chemin de
|
|
143
|
-
* GÉNÉRATION (selfsigned / mkcert / inspection). Le chemin `explicit` (prod)
|
|
144
|
-
* ne l'appelle jamais.
|
|
145
|
-
*/
|
|
146
|
-
loadForge(): Promise<ForgeModule>;
|
|
147
|
-
/** Accès au backend forge déjà chargé (lève si `loadForge` n'a pas été appelé). */
|
|
148
|
-
private get forgeLib();
|
|
149
212
|
/**
|
|
150
213
|
* Fabrique-t-on un certificat au démarrage ?
|
|
151
214
|
*
|
|
@@ -182,20 +245,32 @@ declare class Certificate extends Service {
|
|
|
182
245
|
/**
|
|
183
246
|
* Génère (ou recharge) le certificat serveur selon la stratégie résolue :
|
|
184
247
|
* `explicit` (fourni en config / prod), `mkcert` (dev, CA trustée) ou
|
|
185
|
-
* `selfsigned` (auto-
|
|
248
|
+
* `selfsigned` (auto-signé, secours). Régénère automatiquement si
|
|
186
249
|
* le certificat présent sur disque n'est pas adéquat pour la stratégie active
|
|
187
250
|
* (expiré, SHA-1, SAN incomplet).
|
|
188
251
|
*
|
|
189
252
|
* @param force - forcer la régénération même si un certificat valide existe
|
|
190
253
|
*/
|
|
191
254
|
generateServerCertificates(force?: boolean): Promise<this>;
|
|
255
|
+
/**
|
|
256
|
+
* En développement, fait confiance à la CA que Nodefony vient de générer ou de
|
|
257
|
+
* relire, pour les requêtes SORTANTES du processus.
|
|
258
|
+
*
|
|
259
|
+
* Sans elle, un service du décor qui sert le certificat de l'application
|
|
260
|
+
* (Keycloak, l'auto-vérification d'un jeton MCP) est injoignable depuis
|
|
261
|
+
* `nodefony development` — `fetch failed` / `SELF_SIGNED_CERT_IN_CHAIN` — alors
|
|
262
|
+
* que `curl -k` répond : la confiance n'existait que sous le script de banc.
|
|
263
|
+
* Réservé au DÉVELOPPEMENT et aux certificats que Nodefony fabrique : un
|
|
264
|
+
* certificat `explicit` (production) n'élargit jamais la confiance du processus.
|
|
265
|
+
*/
|
|
266
|
+
private trustDevelopmentAuthority;
|
|
192
267
|
/**
|
|
193
268
|
* Résout la stratégie effective à partir de `certificates.strategy` :
|
|
194
269
|
* - `explicit` : `key` + `cert` fournis (prod, Let's Encrypt…). Forcé →
|
|
195
270
|
* erreur si absents.
|
|
196
271
|
* - `mkcert` : dev + binaire mkcert + CA locale présents. Forcé hors dev →
|
|
197
272
|
* retombe sur `selfsigned` avec avertissement.
|
|
198
|
-
* - `selfsigned` : auto-signé
|
|
273
|
+
* - `selfsigned` : auto-signé (fallback). En PRODUCTION, crie un
|
|
199
274
|
* avertissement : la génération n'est pas le rôle d'un serveur de prod.
|
|
200
275
|
*/
|
|
201
276
|
private resolveStrategy;
|
|
@@ -209,7 +284,7 @@ declare class Certificate extends Service {
|
|
|
209
284
|
/**
|
|
210
285
|
* SAN effectif : config explicite si fournie, sinon dérivé du kernel
|
|
211
286
|
* (localhost + domain en DNS ; loopback en IP). Une IP littérale (ex. domain
|
|
212
|
-
* `127.0.0.1` en dev) est classée en `ip`, pas en `dns` (RFC
|
|
287
|
+
* `127.0.0.1` en dev) est classée en `ip`, pas en `dns` (RFC 9525).
|
|
213
288
|
*/
|
|
214
289
|
private derivedSan;
|
|
215
290
|
/** Hostnames DNS couverts par le SAN. */
|
|
@@ -233,7 +308,7 @@ declare class Certificate extends Service {
|
|
|
233
308
|
* @returns false si absent, illisible ou inadéquat → déclenche la régénération.
|
|
234
309
|
*/
|
|
235
310
|
private isCertAdequate;
|
|
236
|
-
/** Le SAN présent couvre-t-il tous les noms DNS requis (RFC
|
|
311
|
+
/** Le SAN présent couvre-t-il tous les noms DNS requis (RFC 9525) ? */
|
|
237
312
|
private sanCovers;
|
|
238
313
|
/** Charge un certificat fourni en config (chemin fichier ou Buffer). */
|
|
239
314
|
private loadExplicitCert;
|
|
@@ -248,30 +323,31 @@ declare class Certificate extends Service {
|
|
|
248
323
|
* par mkcert passe aussi par ici). Échec silencieux hors POSIX.
|
|
249
324
|
*/
|
|
250
325
|
private restrictPrivateKey;
|
|
251
|
-
|
|
326
|
+
/** Génère la paire RSA (taille configurée) hors de la boucle d'événements. */
|
|
327
|
+
generateKeys(): Promise<IRsaKeyPair>;
|
|
328
|
+
/** Clé privée en PEM PKCS#1 (`BEGIN RSA PRIVATE KEY`). */
|
|
252
329
|
generatePrivateKeyPem(): Buffer;
|
|
330
|
+
/** Clé publique en PEM SPKI (`BEGIN PUBLIC KEY`). */
|
|
253
331
|
generatePublickeyPem(): Buffer;
|
|
254
|
-
generateCertPem(): Buffer;
|
|
255
|
-
createCertificate(): pkg.pki.Certificate;
|
|
256
|
-
/** subjectAltName de l'auto-signé : DNS + IP (config explicite sinon dérivé). */
|
|
257
|
-
private altNames;
|
|
258
|
-
setExtension(): void;
|
|
259
332
|
/**
|
|
260
|
-
*
|
|
261
|
-
*
|
|
262
|
-
*
|
|
263
|
-
*
|
|
333
|
+
* Fabrique et signe le certificat auto-signé avec la paire courante.
|
|
334
|
+
*
|
|
335
|
+
* Série aléatoire unique (RFC 5280 §4.1.2.2), `notBefore` reculé (décalage
|
|
336
|
+
* d'horloge du client), SAN DNS + IP (config explicite sinon dérivé), signé
|
|
337
|
+
* avec le hachage configuré — jamais SHA-1 (collision SHAttered 2017,
|
|
338
|
+
* CA/Browser Forum depuis 2016).
|
|
339
|
+
*
|
|
340
|
+
* @returns le certificat en PEM
|
|
341
|
+
* @throws Si la paire de clés n'a pas été générée
|
|
264
342
|
*/
|
|
265
|
-
|
|
343
|
+
createCertificate(): Promise<Buffer>;
|
|
266
344
|
/**
|
|
267
345
|
* Résumé introspectable du certificat serveur courant — réutilisé par la
|
|
268
346
|
* commande CLI `certificates` et un futur endpoint d'admin Studio (parité
|
|
269
|
-
* CLI ↔ Web).
|
|
347
|
+
* CLI ↔ Web).
|
|
270
348
|
*/
|
|
271
349
|
describe(): Promise<CertificateInfo>;
|
|
272
|
-
/**
|
|
273
|
-
private
|
|
274
|
-
/** Digest node-forge correspondant au hachage configuré (jamais SHA-1). */
|
|
275
|
-
private digestFor;
|
|
350
|
+
/** Hachage configuré, ramené à SHA-256 s'il sort du contrat (jamais SHA-1). */
|
|
351
|
+
private allowedHash;
|
|
276
352
|
}
|
|
277
353
|
export default Certificate;
|
|
@@ -69,7 +69,7 @@ export declare function schemaMismatchOf(error: unknown): SchemaMismatchConfiden
|
|
|
69
69
|
* reason: error.message
|
|
70
70
|
*
|
|
71
71
|
* Stateless singleton — zero per-request allocation. Override via
|
|
72
|
-
* `httpKernel.setErrorRenderer(custom)` for prod hardening or RFC
|
|
72
|
+
* `httpKernel.setErrorRenderer(custom)` for prod hardening or RFC 9457.
|
|
73
73
|
*/
|
|
74
74
|
declare class DefaultErrorRenderer implements IErrorRenderer {
|
|
75
75
|
renderHttp(error: Error, context: IHttpContext): IErrorHttpResult;
|
|
@@ -7,13 +7,14 @@ import type { IRateLimitStore } from "../src/rateLimit/IRateLimitStore.js";
|
|
|
7
7
|
import { type ITrustedHostsConfig } from "../src/context/domainMatcher.js";
|
|
8
8
|
import http from "node:http";
|
|
9
9
|
import http2 from "node:http2";
|
|
10
|
-
import type { IncomingMessage } from "node:http";
|
|
10
|
+
import type { IncomingHttpHeaders, IncomingMessage } from "node:http";
|
|
11
11
|
import Ws from "ws";
|
|
12
12
|
import httpServer from "../service/servers/server-http.js";
|
|
13
13
|
import httpsServer from "../service/servers/server-https.js";
|
|
14
14
|
import websocketServer from "../service/servers/server-websocket.js";
|
|
15
15
|
import websocketSecureServer from "../service/servers/server-websocket-secure.js";
|
|
16
16
|
import Statics from "./servers/server-static.js";
|
|
17
|
+
import type ReverseProxy from "./reverse-proxy.js";
|
|
17
18
|
import WebsocketContext from "../src/context/websocket/WebsocketContext.js";
|
|
18
19
|
import HttpContext from "../src/context/http/HttpContext.js";
|
|
19
20
|
import Context, { HTTPMethod, WebSocketState } from "../src/context/Context.js";
|
|
@@ -25,9 +26,21 @@ import type { IErrorRenderer } from "../interfaces/IErrorRenderer.js";
|
|
|
25
26
|
import type { IRequestLogger } from "../interfaces/IRequestLogger.js";
|
|
26
27
|
import type { DefaultOptionsService } from "nodefony";
|
|
27
28
|
import type { IHttpConfig } from "../config/config.js";
|
|
29
|
+
/**
|
|
30
|
+
* Config interne (shape déclarée par `config/config.ts`). Locale au module —
|
|
31
|
+
* pas exportée tant que la surface publique n'a pas vocation à être étendue.
|
|
32
|
+
*/
|
|
33
|
+
/** Ce qu'il faut d'une requête pour en compter la connexion par IP. */
|
|
34
|
+
export interface IConnectionRequest {
|
|
35
|
+
readonly headers: IncomingHttpHeaders;
|
|
36
|
+
readonly socket: {
|
|
37
|
+
readonly remoteAddress?: string | undefined;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
28
40
|
export type ProtocolType = "1.1" | "2.0" | "3.0";
|
|
29
41
|
export type httpRequest = http.IncomingMessage | http2.Http2ServerRequest;
|
|
30
42
|
export type httpResponse = http.ServerResponse | http2.Http2ServerResponse;
|
|
43
|
+
export { responseEnded } from "../src/context/responseEnded.js";
|
|
31
44
|
export type ContextType = WebsocketContext | HttpContext | Context;
|
|
32
45
|
export type ServerType = "http" | "https" | "http2" | "http3" | "websocket" | "websocket-secure";
|
|
33
46
|
export type responseTimeoutType = "http" | "https" | "http2" | "http3";
|
|
@@ -81,13 +94,28 @@ declare class HttpKernel extends Service implements IHttpKernelInterface {
|
|
|
81
94
|
cert: string;
|
|
82
95
|
ca: string;
|
|
83
96
|
serverStatic: Statics | null;
|
|
97
|
+
/**
|
|
98
|
+
* Proxy inverse (`reverse-proxy`) — lu à chaque requête avant le routage ;
|
|
99
|
+
* `mounts === null` tant que personne n'a monté de préfixe.
|
|
100
|
+
*/
|
|
101
|
+
reverseProxy: ReverseProxy | null;
|
|
84
102
|
domain: string;
|
|
85
103
|
trustedHosts?: ITrustedHostsConfig | undefined;
|
|
86
|
-
|
|
104
|
+
/**
|
|
105
|
+
* Contrôle de domaine actif (`domainCheck` de la config d'application) —
|
|
106
|
+
* lu à la source, pour le HTTP comme pour le handshake WebSocket.
|
|
107
|
+
*/
|
|
108
|
+
get domainCheck(): boolean;
|
|
87
109
|
regAlias: RegExp[];
|
|
88
110
|
module: Module;
|
|
89
|
-
|
|
90
|
-
|
|
111
|
+
/**
|
|
112
|
+
* Port du serveur HTTPS effectivement lié, `undefined` s'il est inactif —
|
|
113
|
+
* lu sur le serveur, jamais recopié (lu par `HttpContext.redirectHttps`).
|
|
114
|
+
*/
|
|
115
|
+
get httpsPort(): number | undefined;
|
|
116
|
+
/** Port du serveur HTTP effectivement lié, `undefined` s'il est inactif. */
|
|
117
|
+
get httpPort(): number | undefined;
|
|
118
|
+
private boundPort;
|
|
91
119
|
responseTimeout: {
|
|
92
120
|
http: number;
|
|
93
121
|
https: number;
|
|
@@ -196,6 +224,22 @@ declare class HttpKernel extends Service implements IHttpKernelInterface {
|
|
|
196
224
|
* @returns la politique mémoïsée (0 alloc après le 1er handshake).
|
|
197
225
|
*/
|
|
198
226
|
private getWsOriginPolicy;
|
|
227
|
+
/**
|
|
228
|
+
* L'`Origin` d'un handshake WebSocket est-elle acceptée ? Règle UNIQUE,
|
|
229
|
+
* interrogeable SANS contexte : le serveur WebSocket de Nodefony
|
|
230
|
+
* ({@link checkWebsocketOrigin}) et le proxy inverse, qui relaie un upgrade
|
|
231
|
+
* avant qu'aucun contexte n'existe, décident par elle.
|
|
232
|
+
*
|
|
233
|
+
* Same-origin (nom de l'`Origin` = nom servi, port ignoré), loopback toléré
|
|
234
|
+
* en development, allowlist `allowedOrigins` ; sans `Origin` (client
|
|
235
|
+
* non-navigateur) → acceptée. Doctrine complète : {@link checkWebsocketOrigin}.
|
|
236
|
+
*
|
|
237
|
+
* @param origin - valeur brute de l'en-tête `Origin`
|
|
238
|
+
* @param hostname - nom d'hôte servi, sans port (`Context.domain`)
|
|
239
|
+
* @param secure - handshake reçu en TLS (politique `websocketSecure`)
|
|
240
|
+
* @returns `true` si le handshake peut se poursuivre
|
|
241
|
+
*/
|
|
242
|
+
isWebsocketOriginAllowed(origin: string | undefined, hostname: string, secure: boolean): boolean;
|
|
199
243
|
/**
|
|
200
244
|
* B4 — Validation d'`Origin` au handshake WebSocket (anti-CSWSH, OWASP
|
|
201
245
|
* WSTG-CLNT-10). Les navigateurs n'appliquent PAS CORS aux WebSockets : sans
|
|
@@ -304,7 +348,7 @@ declare class HttpKernel extends Service implements IHttpKernelInterface {
|
|
|
304
348
|
*/
|
|
305
349
|
/**
|
|
306
350
|
* Override the default error renderer — e.g. to hide stack traces in prod
|
|
307
|
-
* or emit RFC
|
|
351
|
+
* or emit RFC 9457 problem+json. Stateless singleton expected.
|
|
308
352
|
*/
|
|
309
353
|
setErrorRenderer(renderer: IErrorRenderer): void;
|
|
310
354
|
getErrorRenderer(): IErrorRenderer;
|
|
@@ -527,5 +571,67 @@ declare class HttpKernel extends Service implements IHttpKernelInterface {
|
|
|
527
571
|
* couvre ce nom.
|
|
528
572
|
*/
|
|
529
573
|
isTrustedHostname(hostname: string): boolean;
|
|
574
|
+
/**
|
|
575
|
+
* Refus d'un upgrade WebSocket AVANT le `101`, par un statut HTTP
|
|
576
|
+
* (RFC 6455 §4.2.2) — appelé par le répartiteur d'upgrade, après le proxy
|
|
577
|
+
* inverse et avant le serveur `ws`.
|
|
578
|
+
*
|
|
579
|
+
* Pourquoi avant : accepter puis fermer fait croire au client que le serveur
|
|
580
|
+
* a répondu. Le client Vite sonde « le serveur est-il revenu ? » en ouvrant
|
|
581
|
+
* une socket ; une ouverture suivie d'une fermeture `4004` lui faisait
|
|
582
|
+
* recharger la page avant que Vite soit prêt — page blanche.
|
|
583
|
+
*
|
|
584
|
+
* Ne juge que l'existence d'une route WebSocket pour le chemin
|
|
585
|
+
* ({@link IRequestRouter.servesWebsocket}) : hôte, origine, sous-protocole,
|
|
586
|
+
* quotas et pare-feu restent jugés après l'ouverture, comme avant. Un refus
|
|
587
|
+
* compte au limiteur de débit comme une requête HTTP refusée — sans quoi un
|
|
588
|
+
* flot d'upgrades vers des chemins inexistants y échapperait.
|
|
589
|
+
*
|
|
590
|
+
* @param req - requête d'upgrade
|
|
591
|
+
* @returns le statut du refus — `404` chemin sans route WebSocket, `400` URL
|
|
592
|
+
* inanalysable, `429` débit dépassé, `503` routeur pas encore posé (boot) —
|
|
593
|
+
* ou `null` si l'upgrade est admis
|
|
594
|
+
*/
|
|
595
|
+
websocketUpgradeRefusal(req: IncomingMessage): 400 | 404 | 429 | 503 | null;
|
|
596
|
+
/**
|
|
597
|
+
* Bornes par IP d'un upgrade WebSocket : débit de handshakes (le MÊME
|
|
598
|
+
* compteur que les requêtes HTTP — un upgrade EST un GET) puis connexions
|
|
599
|
+
* simultanées (`wsMaxConnectionsPerIp`). Seule implémentation, appelée par
|
|
600
|
+
* le serveur WebSocket de Nodefony ET par le proxy inverse pour un upgrade
|
|
601
|
+
* qu'il relaie : un tunnel relayé n'échappe pas aux quotas d'une connexion
|
|
602
|
+
* servie.
|
|
603
|
+
*
|
|
604
|
+
* L'IP est résolue comme en HTTP (RFC 7239 + `trustProxy`), et seulement si
|
|
605
|
+
* un limiteur est armé — 0 coût quand tout est désactivé (défaut). Aucun log
|
|
606
|
+
* par refus : sous un flood, il serait lui-même un amplificateur.
|
|
607
|
+
*
|
|
608
|
+
* @param req - requête d'upgrade
|
|
609
|
+
* @param connection - ce dont la fermeture libère la place comptée (le `ws`
|
|
610
|
+
* accepté, ou le socket du client pour un tunnel relayé)
|
|
611
|
+
* @returns `null` si l'upgrade est admis — sa place est alors rendue à la
|
|
612
|
+
* fermeture de `connection` —, sinon la raison du refus
|
|
613
|
+
*/
|
|
614
|
+
websocketQuotaRefusal(req: IncomingMessage, connection: {
|
|
615
|
+
once(event: "close", listener: () => void): unknown;
|
|
616
|
+
}): "rate limit" | "too many connections" | null;
|
|
617
|
+
/**
|
|
618
|
+
* Prend une place de connexion LONGUE pour l'IP de la requête — le plafond
|
|
619
|
+
* `wsMaxConnectionsPerIp`, partagé par les sockets WebSocket ET les flux SSE :
|
|
620
|
+
* un seul budget par IP, sinon N sockets plus N flux doubleraient le plafond.
|
|
621
|
+
* Le débit n'est pas recompté ici : la requête qui ouvre un flux l'a déjà été
|
|
622
|
+
* à l'entrée du pipeline.
|
|
623
|
+
*
|
|
624
|
+
* @param req - la requête qui ouvre le flux (en-têtes + socket du pair)
|
|
625
|
+
* @param connection - ce dont la fermeture rend la place (la réponse)
|
|
626
|
+
* @returns `true` si la place est prise — ou si aucun plafond n'est armé
|
|
627
|
+
*/
|
|
628
|
+
acquireConnectionSlot(req: IConnectionRequest, connection: {
|
|
629
|
+
once(event: "close", listener: () => void): unknown;
|
|
630
|
+
}): boolean;
|
|
631
|
+
/**
|
|
632
|
+
* Re-valide tous les flux SSE et ferme ceux dont l'identité est morte.
|
|
633
|
+
* Publique pour qu'un banc la déclenche sans attendre le tick.
|
|
634
|
+
*/
|
|
635
|
+
revalidateStreams(): Promise<void>;
|
|
530
636
|
}
|
|
531
637
|
export default HttpKernel;
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import http from "node:http";
|
|
2
|
+
import type { Duplex } from "node:stream";
|
|
3
|
+
import { Service, Module } from "nodefony";
|
|
4
|
+
import type { IProxyMount, IProxyMountOptions } from "../interfaces/IReverseProxy.js";
|
|
5
|
+
import { type ProxiedRequest, type ProxiedResponse } from "../src/proxy/forward.js";
|
|
6
|
+
/** Un montage tel que le service le garde : la dernière cible analysée y est mise en cache. */
|
|
7
|
+
interface IMountRecord extends IProxyMount {
|
|
8
|
+
/** D'où vient le montage : la configuration de l'application, ou un module. */
|
|
9
|
+
readonly source: "config" | "module";
|
|
10
|
+
/** Dernière origine rendue par `target`, et son analyse. */
|
|
11
|
+
lastOrigin: string | undefined;
|
|
12
|
+
lastUrl: URL | undefined;
|
|
13
|
+
/** Pool de connexions vers l'amont, créé au premier relais (scheme de la cible). */
|
|
14
|
+
agent: http.Agent | null;
|
|
15
|
+
/** Tunnels WebSocket ouverts vers l'amont — bornés par `maxSockets`, hors pool. */
|
|
16
|
+
tunnels: number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Proxy inverse de Nodefony : relaie un préfixe d'URL vers un autre serveur
|
|
20
|
+
* HTTP — requêtes ET, sur demande, upgrades WebSocket — sur la MÊME origine
|
|
21
|
+
* que la page. Le client ne voit qu'un serveur, un certificat ; l'amont peut
|
|
22
|
+
* rester sur la boucle locale.
|
|
23
|
+
*
|
|
24
|
+
* Deux sources de montages, mêmes règles (`src/proxy/rules.ts`) : la section
|
|
25
|
+
* `proxy.mounts` de la configuration, lue à la construction, et
|
|
26
|
+
* {@link ReverseProxy.mount}, appelé par un module (`@nodefony/frontend` y
|
|
27
|
+
* relaie `/_vite/<famille>/` vers ses serveurs Vite de développement). Un
|
|
28
|
+
* module se résout ce service par NOM (`container.get("reverse-proxy")`) :
|
|
29
|
+
* il n'a pas à importer `@nodefony/http`.
|
|
30
|
+
*
|
|
31
|
+
* Consulté AVANT le routage : une route attrape-tout de l'application ne peut
|
|
32
|
+
* pas avaler un préfixe relayé. Sans montage, le pipeline ne paie qu'une
|
|
33
|
+
* lecture de champ (`mounts === null`) — c'est le cas de toute application
|
|
34
|
+
* qui ne s'en sert pas.
|
|
35
|
+
*
|
|
36
|
+
* Règles communes à tout montage : en-têtes de connexion jamais transmis
|
|
37
|
+
* (RFC 9110 §7.6.1), `X-Forwarded-For/-Proto/-Host` et `Via` posés par le
|
|
38
|
+
* proxy, Host hors `trustedHosts` jamais relayé (même barrière, même 421 que
|
|
39
|
+
* toute requête), amont injoignable → 502, amont muet → 504, boucle → 508.
|
|
40
|
+
*/
|
|
41
|
+
declare class ReverseProxy extends Service {
|
|
42
|
+
#private;
|
|
43
|
+
module: Module;
|
|
44
|
+
/**
|
|
45
|
+
* Montages actifs, du préfixe le PLUS LONG au plus court : le premier qui
|
|
46
|
+
* couvre une URL est le plus spécifique. `null` tant qu'il n'y en a aucun.
|
|
47
|
+
*/
|
|
48
|
+
mounts: IMountRecord[] | null;
|
|
49
|
+
/** Délai d'inactivité appliqué aux montages qui n'en fixent pas (`proxy.timeoutMs`). */
|
|
50
|
+
readonly defaultTimeoutMs: number;
|
|
51
|
+
/** Délai d'établissement de la connexion amont (`proxy.connectTimeoutMs`). */
|
|
52
|
+
readonly connectTimeoutMs: number;
|
|
53
|
+
/** Connexions simultanées par amont (`proxy.maxSockets`) : pool ET tunnels WebSocket. */
|
|
54
|
+
readonly maxSockets: number;
|
|
55
|
+
/**
|
|
56
|
+
* Nom de CE processus dans `Via` (RFC 9110 §7.6.3) : tiré au hasard au
|
|
57
|
+
* démarrage, il ne dit rien de la machine et distingue deux instances —
|
|
58
|
+
* un message qui le porte déjà est une boucle (508).
|
|
59
|
+
*/
|
|
60
|
+
readonly pseudonym: string;
|
|
61
|
+
constructor(module: Module);
|
|
62
|
+
/**
|
|
63
|
+
* Relaie un préfixe d'URL vers un amont. Idempotent pour un même module :
|
|
64
|
+
* un même préfixe est remplacé. Un module qui recouvre un préfixe de la
|
|
65
|
+
* CONFIGURATION le remplace aussi, mais le dit — l'auteur de l'application
|
|
66
|
+
* doit pouvoir lire pourquoi son montage ne sert plus.
|
|
67
|
+
*
|
|
68
|
+
* @param prefix - préfixe d'URL (normalisé : `/` en tête et en fin)
|
|
69
|
+
* @param options - cible et réglages du montage
|
|
70
|
+
* @throws Error qui nomme chaque refus (préfixe `/` ou réservé, cible qui
|
|
71
|
+
* n'est pas une origine http(s) nue, `secure: false` hors boucle locale…)
|
|
72
|
+
*/
|
|
73
|
+
mount(prefix: string, options: IProxyMountOptions): void;
|
|
74
|
+
/**
|
|
75
|
+
* Retire un montage. Le dernier retiré rend `mounts` à `null` : le pipeline
|
|
76
|
+
* cesse alors de le consulter.
|
|
77
|
+
*
|
|
78
|
+
* @param prefix - préfixe tel que monté (normalisé de la même façon)
|
|
79
|
+
*/
|
|
80
|
+
unmount(prefix: string): void;
|
|
81
|
+
/**
|
|
82
|
+
* Ferme tous les pools (arrêt du noyau). Les montages restent déclarés ; un
|
|
83
|
+
* relais encore en vol part ensuite sur une connexion à usage unique.
|
|
84
|
+
*/
|
|
85
|
+
closeAll(): void;
|
|
86
|
+
/**
|
|
87
|
+
* Montage le plus spécifique qui couvre une URL, s'il y en a un.
|
|
88
|
+
*
|
|
89
|
+
* @param url - cible brute de la requête (chemin + requête)
|
|
90
|
+
* @returns le montage au préfixe le plus long qui couvre l'URL
|
|
91
|
+
*/
|
|
92
|
+
match(url: string | undefined): IProxyMount | undefined;
|
|
93
|
+
/**
|
|
94
|
+
* Origine courante d'un montage, analysée — mise en cache tant que la cible
|
|
95
|
+
* rend la même chaîne.
|
|
96
|
+
*
|
|
97
|
+
* @returns l'URL de l'amont, ou `undefined` si rien n'est relayable
|
|
98
|
+
*/
|
|
99
|
+
private targetOf;
|
|
100
|
+
/**
|
|
101
|
+
* Pool de connexions d'un montage, créé au premier relais et recréé si la
|
|
102
|
+
* cible change de scheme.
|
|
103
|
+
*
|
|
104
|
+
* Un `Agent` DÉDIÉ, jamais l'agent global : sous `NODE_USE_ENV_PROXY`, cas
|
|
105
|
+
* courant d'un poste d'entreprise, l'agent global envoie tout vers
|
|
106
|
+
* `HTTP_PROXY` — un amont sur la boucle locale serait demandé au proxy de
|
|
107
|
+
* l'entreprise. Un agent construit ici n'a pas de `proxyEnv` : il va droit
|
|
108
|
+
* à la cible. Keep-alive borné (`maxSockets`), `lifo` : le socket le plus
|
|
109
|
+
* récent, le moins susceptible d'avoir été fermé par l'amont.
|
|
110
|
+
*/
|
|
111
|
+
private agentFor;
|
|
112
|
+
/** Réglages de relais d'un montage. */
|
|
113
|
+
private relayFor;
|
|
114
|
+
/** Règles du noyau HTTP (résolues par nom), `undefined` hors serveur. */
|
|
115
|
+
private rules;
|
|
116
|
+
/** Ce que le proxy annonce à l'amont sur le client. */
|
|
117
|
+
private fromOf;
|
|
118
|
+
/**
|
|
119
|
+
* Relaie une requête HTTP si un montage la couvre — méthode comprise. Cible
|
|
120
|
+
* pas encore connue → `503` + `Retry-After`, sans aller jusqu'au routage.
|
|
121
|
+
*
|
|
122
|
+
* L'appelant a déjà tranché la barrière d'hôte : c'est le pipeline HTTP, qui
|
|
123
|
+
* la calcule pour toute requête (`Context.validDomain`). Un chemin ambigu
|
|
124
|
+
* sous un préfixe monté rend 400, sans contacter l'amont.
|
|
125
|
+
*
|
|
126
|
+
* ⚠️ Un préfixe monté est servi AVANT le pare-feu, la CSRF et les en-têtes
|
|
127
|
+
* de sécurité applicatifs : c'est une surface publique pour qui atteint
|
|
128
|
+
* Nodefony. L'amont porte sa propre politique d'accès.
|
|
129
|
+
*
|
|
130
|
+
* @param req - requête du client
|
|
131
|
+
* @param res - réponse au client
|
|
132
|
+
* @param scheme - scheme par lequel le client est arrivé
|
|
133
|
+
* @returns la promesse de fin d'échange, ou `undefined` si la requête n'est
|
|
134
|
+
* pas relayée (hors préfixe ou méthode non relayée : elle suit alors son
|
|
135
|
+
* chemin normal)
|
|
136
|
+
*/
|
|
137
|
+
forward(req: ProxiedRequest, res: ProxiedResponse, scheme: string): Promise<void> | undefined;
|
|
138
|
+
/**
|
|
139
|
+
* Relaie un upgrade WebSocket si un montage `websocket` couvre son URL.
|
|
140
|
+
* Appelé par le répartiteur d'`upgrade` des serveurs WebSocket, AVANT le
|
|
141
|
+
* serveur WebSocket de Nodefony.
|
|
142
|
+
*
|
|
143
|
+
* Contrôles, dans l'ordre, chacun avec la réponse que rendrait le serveur
|
|
144
|
+
* WebSocket de Nodefony : cible pas encore connue → 503 ; `Host` hors `trustedHosts` (si `domainCheck`) →
|
|
145
|
+
* 421 ; chemin ambigu ou handshake non conforme (RFC 6455 §4.2.1 — seul
|
|
146
|
+
* `Upgrade: websocket` est relayé, jamais un tunnel `h2c`) → 400 ;
|
|
147
|
+
* `Origin` refusée par la MÊME règle que le serveur WebSocket (anti-CSWSH,
|
|
148
|
+
* RFC 6455 §10.2) → 403 ; `maxSockets` tunnels déjà ouverts vers l'amont →
|
|
149
|
+
* 503 ; quotas par IP du noyau (débit de handshakes, connexions
|
|
150
|
+
* simultanées) → 429. Le socket est alors consommé.
|
|
151
|
+
*
|
|
152
|
+
* @returns `true` si le proxy a pris le socket en charge
|
|
153
|
+
*/
|
|
154
|
+
handleUpgrade(req: http.IncomingMessage, socket: Duplex, head: Buffer): boolean;
|
|
155
|
+
}
|
|
156
|
+
export default ReverseProxy;
|
|
@@ -7,6 +7,7 @@ import { AddressInfo } from "node:net";
|
|
|
7
7
|
import type { IncomingMessage } from "node:http";
|
|
8
8
|
import httpsServers from "./server-https.js";
|
|
9
9
|
declare class WebsocketSecure extends Service {
|
|
10
|
+
#private;
|
|
10
11
|
private readonly httpKernel;
|
|
11
12
|
options: IHttpConfig["websocketSecure"] & DefaultOptionsService;
|
|
12
13
|
module: Module;
|
|
@@ -7,6 +7,7 @@ import { AddressInfo } from "node:net";
|
|
|
7
7
|
import type { IncomingMessage } from "node:http";
|
|
8
8
|
import httpServer from "./server-http.js";
|
|
9
9
|
declare class Websocket extends Service {
|
|
10
|
+
#private;
|
|
10
11
|
private readonly httpKernel;
|
|
11
12
|
options: IHttpConfig["websocket"] & DefaultOptionsService;
|
|
12
13
|
module: Module;
|