@nodefony/http 10.0.0-alpha.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/LICENSE +544 -0
- package/README.md +77 -0
- package/dist/_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorate.js +9 -0
- package/dist/_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorateMetadata.js +6 -0
- package/dist/_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorateParam.js +8 -0
- package/dist/index.js +108 -0
- package/dist/nodefony/command/assetsPublishCommand.js +102 -0
- package/dist/nodefony/command/certificatesCommand.js +47 -0
- package/dist/nodefony/command/networkCommand.js +27 -0
- package/dist/nodefony/command/proxyGenerateCommand.js +66 -0
- package/dist/nodefony/config/config.js +335 -0
- package/dist/nodefony/config/defineModuleConfig.js +93 -0
- package/dist/nodefony/interfaces/IContext.js +1 -0
- package/dist/nodefony/interfaces/ICookie.js +1 -0
- package/dist/nodefony/interfaces/IErrorRenderer.js +1 -0
- package/dist/nodefony/interfaces/IHttpConfig.js +1 -0
- package/dist/nodefony/interfaces/IHttpKernel.js +1 -0
- package/dist/nodefony/interfaces/IRequest.js +1 -0
- package/dist/nodefony/interfaces/IRequestLogger.js +1 -0
- package/dist/nodefony/interfaces/IResponse.js +1 -0
- package/dist/nodefony/interfaces/ISession.js +1 -0
- package/dist/nodefony/interfaces/IUpload.js +1 -0
- package/dist/nodefony/interfaces/index.js +1 -0
- package/dist/nodefony/service/HttpAdminApi.js +376 -0
- package/dist/nodefony/service/ProfilerAdminApi.js +73 -0
- package/dist/nodefony/service/audit-logger.js +159 -0
- package/dist/nodefony/service/certificates.js +545 -0
- package/dist/nodefony/service/error-renderer.js +320 -0
- package/dist/nodefony/service/http-kernel.js +948 -0
- package/dist/nodefony/service/pretty-request-logger.js +72 -0
- package/dist/nodefony/service/request-logger.js +54 -0
- package/dist/nodefony/service/servers/clientError.js +20 -0
- package/dist/nodefony/service/servers/server-http.js +135 -0
- package/dist/nodefony/service/servers/server-https.js +204 -0
- package/dist/nodefony/service/servers/server-static.js +192 -0
- package/dist/nodefony/service/servers/server-websocket-secure.js +104 -0
- package/dist/nodefony/service/servers/server-websocket.js +104 -0
- package/dist/nodefony/service/servers/serverShutdown.js +31 -0
- package/dist/nodefony/service/servers/wsHeartbeat.js +64 -0
- package/dist/nodefony/service/sessions/sessions-service.js +580 -0
- package/dist/nodefony/service/trace.js +72 -0
- package/dist/nodefony/service/upload/upload-service.js +171 -0
- package/dist/nodefony/src/assets/collectAssets.js +34 -0
- package/dist/nodefony/src/assets/prebuiltUi.js +125 -0
- package/dist/nodefony/src/context/Context.js +415 -0
- package/dist/nodefony/src/context/domainMatcher.js +88 -0
- package/dist/nodefony/src/context/forwarded.js +185 -0
- package/dist/nodefony/src/context/http/HttpContext.js +309 -0
- package/dist/nodefony/src/context/http/Request.js +543 -0
- package/dist/nodefony/src/context/http/Response.js +368 -0
- package/dist/nodefony/src/context/http/parser.js +188 -0
- package/dist/nodefony/src/context/http/urlFastPath.js +103 -0
- package/dist/nodefony/src/context/http2/Request.js +29 -0
- package/dist/nodefony/src/context/http2/Response.js +97 -0
- package/dist/nodefony/src/context/metaData.js +47 -0
- package/dist/nodefony/src/context/requestId.js +41 -0
- package/dist/nodefony/src/context/trustProxy.js +167 -0
- package/dist/nodefony/src/context/websocket/Response.js +181 -0
- package/dist/nodefony/src/context/websocket/WebsocketContext.js +389 -0
- package/dist/nodefony/src/context/websocket/wsBackpressure.js +56 -0
- package/dist/nodefony/src/context/websocket/wsLogContent.js +68 -0
- package/dist/nodefony/src/cookies/cookie.js +258 -0
- package/dist/nodefony/src/errors/httpError.js +69 -0
- package/dist/nodefony/src/profiler/FrameProfile.js +95 -0
- package/dist/nodefony/src/profiler/Profiler.js +139 -0
- package/dist/nodefony/src/proxy/generateProxyConfig.js +157 -0
- package/dist/nodefony/src/rateLimit/IRateLimitStore.js +1 -0
- package/dist/nodefony/src/rateLimit/MemoryRateLimitStore.js +146 -0
- package/dist/nodefony/src/rateLimit/WsConnectionCounter.js +64 -0
- package/dist/nodefony/src/rateLimit/rateLimitFilters.js +20 -0
- package/dist/nodefony/src/servers/portBinder.js +114 -0
- package/dist/nodefony/src/session/session.js +390 -0
- package/dist/nodefony/src/session/storage/MemorySessionStorage.js +185 -0
- package/dist/nodefony/src/session/storage/RevocationGuardStorage.js +137 -0
- package/dist/nodefony/src/session/storage/sessionFilters.js +83 -0
- package/dist/nodefony/src/session/storage/sessionSort.js +53 -0
- package/dist/types/index.d.ts +83 -0
- package/dist/types/nodefony/command/assetsPublishCommand.d.ts +23 -0
- package/dist/types/nodefony/command/certificatesCommand.d.ts +17 -0
- package/dist/types/nodefony/command/networkCommand.d.ts +8 -0
- package/dist/types/nodefony/command/proxyGenerateCommand.d.ts +19 -0
- package/dist/types/nodefony/config/config.d.ts +197 -0
- package/dist/types/nodefony/config/defineModuleConfig.d.ts +39 -0
- package/dist/types/nodefony/interfaces/IContext.d.ts +138 -0
- package/dist/types/nodefony/interfaces/ICookie.d.ts +47 -0
- package/dist/types/nodefony/interfaces/IErrorRenderer.d.ts +55 -0
- package/dist/types/nodefony/interfaces/IHttpConfig.d.ts +12 -0
- package/dist/types/nodefony/interfaces/IHttpKernel.d.ts +10 -0
- package/dist/types/nodefony/interfaces/IRequest.d.ts +35 -0
- package/dist/types/nodefony/interfaces/IRequestLogger.d.ts +31 -0
- package/dist/types/nodefony/interfaces/IResponse.d.ts +39 -0
- package/dist/types/nodefony/interfaces/ISession.d.ts +283 -0
- package/dist/types/nodefony/interfaces/IUpload.d.ts +66 -0
- package/dist/types/nodefony/interfaces/index.d.ts +7 -0
- package/dist/types/nodefony/service/HttpAdminApi.d.ts +18 -0
- package/dist/types/nodefony/service/ProfilerAdminApi.d.ts +23 -0
- package/dist/types/nodefony/service/audit-logger.d.ts +143 -0
- package/dist/types/nodefony/service/certificates.d.ts +246 -0
- package/dist/types/nodefony/service/error-renderer.d.ts +74 -0
- package/dist/types/nodefony/service/http-kernel.d.ts +377 -0
- package/dist/types/nodefony/service/pretty-request-logger.d.ts +25 -0
- package/dist/types/nodefony/service/request-logger.d.ts +18 -0
- package/dist/types/nodefony/service/servers/clientError.d.ts +14 -0
- package/dist/types/nodefony/service/servers/server-http.d.ts +42 -0
- package/dist/types/nodefony/service/servers/server-https.d.ts +41 -0
- package/dist/types/nodefony/service/servers/server-static.d.ts +62 -0
- package/dist/types/nodefony/service/servers/server-websocket-secure.d.ts +29 -0
- package/dist/types/nodefony/service/servers/server-websocket.d.ts +29 -0
- package/dist/types/nodefony/service/servers/serverShutdown.d.ts +27 -0
- package/dist/types/nodefony/service/servers/wsHeartbeat.d.ts +46 -0
- package/dist/types/nodefony/service/sessions/sessions-service.d.ts +218 -0
- package/dist/types/nodefony/service/trace.d.ts +39 -0
- package/dist/types/nodefony/service/upload/upload-service.d.ts +61 -0
- package/dist/types/nodefony/src/assets/collectAssets.d.ts +35 -0
- package/dist/types/nodefony/src/assets/prebuiltUi.d.ts +99 -0
- package/dist/types/nodefony/src/context/Context.d.ts +195 -0
- package/dist/types/nodefony/src/context/domainMatcher.d.ts +67 -0
- package/dist/types/nodefony/src/context/forwarded.d.ts +95 -0
- package/dist/types/nodefony/src/context/http/HttpContext.d.ts +85 -0
- package/dist/types/nodefony/src/context/http/Request.d.ts +203 -0
- package/dist/types/nodefony/src/context/http/Response.d.ts +68 -0
- package/dist/types/nodefony/src/context/http/parser.d.ts +65 -0
- package/dist/types/nodefony/src/context/http/urlFastPath.d.ts +52 -0
- package/dist/types/nodefony/src/context/http2/Request.d.ts +14 -0
- package/dist/types/nodefony/src/context/http2/Response.d.ts +20 -0
- package/dist/types/nodefony/src/context/metaData.d.ts +58 -0
- package/dist/types/nodefony/src/context/requestId.d.ts +28 -0
- package/dist/types/nodefony/src/context/trustProxy.d.ts +77 -0
- package/dist/types/nodefony/src/context/websocket/Response.d.ts +53 -0
- package/dist/types/nodefony/src/context/websocket/WebsocketContext.d.ts +125 -0
- package/dist/types/nodefony/src/context/websocket/wsBackpressure.d.ts +73 -0
- package/dist/types/nodefony/src/context/websocket/wsLogContent.d.ts +37 -0
- package/dist/types/nodefony/src/cookies/cookie.d.ts +88 -0
- package/dist/types/nodefony/src/errors/httpError.d.ts +15 -0
- package/dist/types/nodefony/src/profiler/FrameProfile.d.ts +110 -0
- package/dist/types/nodefony/src/profiler/Profiler.d.ts +192 -0
- package/dist/types/nodefony/src/proxy/generateProxyConfig.d.ts +76 -0
- package/dist/types/nodefony/src/rateLimit/IRateLimitStore.d.ts +98 -0
- package/dist/types/nodefony/src/rateLimit/MemoryRateLimitStore.d.ts +40 -0
- package/dist/types/nodefony/src/rateLimit/WsConnectionCounter.d.ts +37 -0
- package/dist/types/nodefony/src/rateLimit/rateLimitFilters.d.ts +18 -0
- package/dist/types/nodefony/src/servers/portBinder.d.ts +102 -0
- package/dist/types/nodefony/src/session/session.d.ts +171 -0
- package/dist/types/nodefony/src/session/storage/MemorySessionStorage.d.ts +77 -0
- package/dist/types/nodefony/src/session/storage/RevocationGuardStorage.d.ts +81 -0
- package/dist/types/nodefony/src/session/storage/sessionFilters.d.ts +102 -0
- package/dist/types/nodefony/src/session/storage/sessionSort.d.ts +45 -0
- package/docs/cookies.md +365 -0
- package/docs/index.md +163 -0
- package/docs/observabilite.md +460 -0
- package/docs/rate-limit.md +372 -0
- package/docs/servers.md +935 -0
- package/docs/session.md +768 -0
- package/docs/upload.md +460 -0
- package/package.json +101 -0
package/docs/cookies.md
ADDED
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Cookies â attributs, signature, parsing, HTTP et WebSocket"
|
|
3
|
+
navTitle: Cookies
|
|
4
|
+
lang: fr
|
|
5
|
+
module: "@nodefony/http"
|
|
6
|
+
topic: cookies
|
|
7
|
+
section: "CĆur runtime"
|
|
8
|
+
audience: [developer]
|
|
9
|
+
tags:
|
|
10
|
+
[
|
|
11
|
+
cookies,
|
|
12
|
+
set-cookie,
|
|
13
|
+
samesite,
|
|
14
|
+
secure,
|
|
15
|
+
httponly,
|
|
16
|
+
__host-,
|
|
17
|
+
signature,
|
|
18
|
+
hmac,
|
|
19
|
+
rfc6265bis,
|
|
20
|
+
websocket,
|
|
21
|
+
]
|
|
22
|
+
version: "doc"
|
|
23
|
+
status: stable
|
|
24
|
+
updated: 2026-07-21
|
|
25
|
+
source: "src/packages/@nodefony/http/docs/cookies.md"
|
|
26
|
+
coverageModule: http
|
|
27
|
+
coverageFiles: cookies/cookie.ts,context/Context.ts,context/http/Response.ts,context/websocket/Response.ts
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
# Cookies â attributs, signature, parsing, HTTP et WebSocket
|
|
31
|
+
|
|
32
|
+
> Un cookie est le seul état que le serveur peut coller au navigateur du visiteur : une petite étiquette
|
|
33
|
+
> renvoyĂ©e Ă chaque requĂȘte. Cette page dĂ©crit la classe `Cookie` de Nodefony â comment on la construit,
|
|
34
|
+
> quels attributs de sécurité elle applique (et **force**), comment les cookies entrants sont lus, comment
|
|
35
|
+
> on lit et écrit un cookie depuis un contrÎleur, et ce qui change cÎté WebSocket. Le cookie **de session**
|
|
36
|
+
> a sa propre page : [Sessions](session.md). Chaque fait est ancré sur le code.
|
|
37
|
+
|
|
38
|
+
đ [Documentation](../../../../../docs/index.md) âș [@nodefony/http](index.md) âș **Cookies**
|
|
39
|
+
|
|
40
|
+
## đ§ Le modĂšle mental â deux sens, une Ă©tiquette
|
|
41
|
+
|
|
42
|
+
Un cookie voyage dans **deux en-tĂȘtes diffĂ©rents**, et Nodefony traite les deux sens sĂ©parĂ©ment :
|
|
43
|
+
|
|
44
|
+
- **Entrant** â le navigateur renvoie ses cookies dans l'en-tĂȘte `Cookie:`. Le pipeline le parse une fois
|
|
45
|
+
et range chaque cookie dans `context.cookies` (lecture seule cÎté contrÎleur).
|
|
46
|
+
- **Sortant** â le contrĂŽleur crĂ©e un `Cookie`, le pose sur la rĂ©ponse ; Ă l'envoi, chaque cookie est
|
|
47
|
+
**sérialisé** en une ligne `Set-Cookie:` avec ses attributs.
|
|
48
|
+
|
|
49
|
+
```mermaid
|
|
50
|
+
flowchart TD
|
|
51
|
+
REQ["RequĂȘte<br/>en-tĂȘte Cookie: a=1; b=2"] --> PARSE["cookiesParser(context)<br/>parse + un Cookie par entrĂ©e"]
|
|
52
|
+
PARSE --> STORE["context.cookies<br/>Record<nom, Cookie> (lecture)"]
|
|
53
|
+
STORE --> CTRL["ContrĂŽleur<br/>context.getRequestCookies(nom)"]
|
|
54
|
+
CTRL --> NEW["new Cookie(nom, valeur, options)"]
|
|
55
|
+
NEW --> SET["context.setCookie(cookie)<br/>â response.addCookie"]
|
|
56
|
+
SET --> SER["Cookie.serialize()<br/>attributs + préfixes forcés"]
|
|
57
|
+
SER --> OUT["RĂ©ponse<br/>en-tĂȘte Set-Cookie: âŠ"]
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Trois idées portent tout le reste :
|
|
61
|
+
|
|
62
|
+
1. **Lire et écrire ne sont pas symétriques.** On lit dans `context.cookies` (rempli par le parseur) ; on
|
|
63
|
+
écrit en posant un `Cookie` sur la **réponse**. Un cookie entrant modifié en mémoire ne repart pas tout
|
|
64
|
+
seul â il faut le (re)poser sur la rĂ©ponse.
|
|
65
|
+
2. **La classe applique des défauts sûrs, et les fait respecter.** `Secure`, `HttpOnly`, `SameSite=Lax` par
|
|
66
|
+
défaut ; les préfixes `__Host-`/`__Secure-` **imposent** leurs contraintes à la sérialisation.
|
|
67
|
+
3. **Le cookie de session est un cas particulier**, gĂ©rĂ© par le gestionnaire de sessions â dĂ©crit dans
|
|
68
|
+
[Sessions](session.md), pas ici.
|
|
69
|
+
|
|
70
|
+
## đ Lexique
|
|
71
|
+
|
|
72
|
+
| Terme | Sens |
|
|
73
|
+
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
|
74
|
+
| `Cookie:` (en-tĂȘte) | En-tĂȘte **de requĂȘte** : le navigateur y renvoie tous les cookies qu'il dĂ©tient pour le domaine. |
|
|
75
|
+
| `Set-Cookie:` | En-tĂȘte **de rĂ©ponse** : le serveur y dĂ©pose un cookie (nom, valeur, attributs). Une ligne par cookie. |
|
|
76
|
+
| `HttpOnly` | Attribut : le cookie est **invisible** Ă `document.cookie` (JavaScript) â un XSS ne peut pas le voler. |
|
|
77
|
+
| `Secure` | Attribut : le cookie n'est renvoyé que sur une connexion **chiffrée** (HTTPS/WSS). |
|
|
78
|
+
| `SameSite` | Attribut anti-CSRF : `Strict` / `Lax` / `None` â dit si le cookie part sur une requĂȘte **inter-site**. |
|
|
79
|
+
| `Path` / `Domain` | Portée du cookie : le préfixe d'URL et le domaine pour lesquels le navigateur le renvoie. |
|
|
80
|
+
| `Max-Age` / `Expires` | Durée de vie : en secondes (`Max-Age`) ou date absolue (`Expires`). Absents = **cookie de session** (mort à la fermeture). |
|
|
81
|
+
| `__Host-` | PrĂ©fixe RFC 6265bis : le navigateur **exige** `Secure` + `Path=/` + **aucun** `Domain` â sinon il rejette le cookie. |
|
|
82
|
+
| `__Secure-` | Préfixe RFC 6265bis : le navigateur exige seulement `Secure`. |
|
|
83
|
+
| Cookie **signĂ©** | Cookie dont la valeur porte un HMAC â le serveur dĂ©tecte toute altĂ©ration cĂŽtĂ© client (intĂ©gritĂ©, pas confidentialitĂ©). |
|
|
84
|
+
| HMAC | _Hash-based Message Authentication Code_ : empreinte clĂ©-dĂ©pendante (ici `HMAC-SHA256`) â infalsifiable sans le secret. |
|
|
85
|
+
| base64url | Variante base64 **sûre en URL/cookie** (pas de `+`, `/`, `=`). |
|
|
86
|
+
| Timing-safe | Comparaison à temps constant (`crypto.timingSafeEqual`) : ne fuit pas la signature attendue par la durée de la comparaison. |
|
|
87
|
+
| XSS | _Cross-Site Scripting_ : du JS injectĂ© s'exĂ©cute dans la page victime â voler un cookie non `HttpOnly` en est le premier but. |
|
|
88
|
+
| CSRF | _Cross-Site Request Forgery_ : un site tiers dĂ©clenche une requĂȘte authentifiĂ©e Ă l'insu de la victime â bloquĂ© par `SameSite`. |
|
|
89
|
+
| Session fixation | L'attaquant impose un identifiant de session connu de lui Ă la victime â `__Host-` empĂȘche l'injection cross-sous-domaine. |
|
|
90
|
+
|
|
91
|
+
## Qu'est-ce qu'un cookie, ici ?
|
|
92
|
+
|
|
93
|
+
Un cookie, c'est un **badge vestiaire** : le serveur remet un ticket au navigateur, qui le reprĂ©sente Ă
|
|
94
|
+
chaque passage. Le serveur reconnaĂźt le porteur sans rien retenir de coĂ»teux â juste la valeur du ticket.
|
|
95
|
+
Le badge peut porter des mentions : « ne pas montrer à un script » (`HttpOnly`), « seulement au guichet
|
|
96
|
+
sécurisé » (`Secure`), « pas valable si un autre site t'envoie » (`SameSite`).
|
|
97
|
+
|
|
98
|
+
Un cookie n'est pas neutre : c'est une **surface d'attaque**. Les attributs sont lĂ pour la refermer.
|
|
99
|
+
|
|
100
|
+
- **`HttpOnly` bloque le vol par XSS.** Sans lui, un script injecté lit `document.cookie` et exfiltre le
|
|
101
|
+
cookie de session. Avec lui, le cookie est hors de portée du JavaScript de la page.
|
|
102
|
+
- **`SameSite` bloque le CSRF.** Sans lui, une balise image (ou un formulaire) hébergée sur un site pirate
|
|
103
|
+
dĂ©clenche une requĂȘte vers ta banque qui part **avec** le cookie de la victime. `Lax` (le dĂ©faut
|
|
104
|
+
Nodefony) coupe cet envoi sur les requĂȘtes inter-site dangereuses.
|
|
105
|
+
- **`__Host-` bloque la session fixation cross-sous-domaine.** Un sous-domaine compromis
|
|
106
|
+
(`evil.example.com`) ne peut pas écrire un cookie qui remonterait vers `example.com` : le préfixe interdit
|
|
107
|
+
`Domain` et impose `Path=/`.
|
|
108
|
+
|
|
109
|
+
## La vision Nodefony
|
|
110
|
+
|
|
111
|
+
Nodefony ne se contente pas de proposer ces attributs : il **choisit des défauts sûrs** et **fait respecter
|
|
112
|
+
les invariants** que le navigateur exigerait de toute façon â pour que l'erreur ne parte pas sur le fil.
|
|
113
|
+
|
|
114
|
+
**Le défaut est fermé.** Un cookie créé sans options est `Secure` + `HttpOnly` + `SameSite=Lax`
|
|
115
|
+
(`cookieDefaultSettings`, `cookie.ts:43`). Il faut **choisir** d'ouvrir (ex. `httpOnly: false` pour un
|
|
116
|
+
cookie lu en JS), jamais choisir de fermer.
|
|
117
|
+
|
|
118
|
+
**Les prĂ©fixes sont forcĂ©s, pas espĂ©rĂ©s.** Nommer un cookie `__Host-âŠ` ne suffit pas : `serialize()`
|
|
119
|
+
(`cookie.ts:383`) **réécrit** la sortie â `Secure` ajoutĂ©, `Path=/` imposĂ©, `Domain` retirĂ© â pour que le
|
|
120
|
+
navigateur ne rejette jamais le cookie en silence. Idem `__Secure-` (Secure imposé) et `SameSite=None` (qui
|
|
121
|
+
impose `Secure`, `cookie.ts:393`).
|
|
122
|
+
|
|
123
|
+
**La signature refuse le secret prévisible.** Un cookie `signed: true` sans secret configuré **jette**
|
|
124
|
+
(`setValue()`, `cookie.ts:211`) : signer avec le secret public par défaut ne protégerait rien
|
|
125
|
+
(fail-closed). La vĂ©rification est **timing-safe** (`unsign()` â `crypto.timingSafeEqual`, `cookie.ts:380`).
|
|
126
|
+
|
|
127
|
+
**`SameSite` retombe toujours sur `Lax`, jamais sur `None`.** Toute valeur inconnue est normalisée vers
|
|
128
|
+
`Lax` (`setSameSite()`, `cookie.ts:250`) : `None` désactive la protection CSRF, ce n'est jamais un défaut.
|
|
129
|
+
|
|
130
|
+
**HTTP et WebSocket lisent les mĂȘmes cookies.** Le parseur tourne pour les deux transports ; en revanche la
|
|
131
|
+
poignĂ©e de main WebSocket **ne peut pas** Ă©crire de cookie (limite de la bibliothĂšque `ws`) â voir plus bas.
|
|
132
|
+
|
|
133
|
+
Liens utiles : [RFC 6265bis](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis) ·
|
|
134
|
+
cookie de session â [Sessions](session.md) · en-tĂȘtes de sĂ©curitĂ© â [En-tĂȘtes](../../security/docs/headers.md).
|
|
135
|
+
|
|
136
|
+
## đ DĂ©marrage rapide
|
|
137
|
+
|
|
138
|
+
Dans une application générée par `nodefony create app`, il n'y a **rien à configurer** pour les cookies
|
|
139
|
+
applicatifs : on construit un `Cookie`, on le lit ou on le pose depuis le contexte du contrĂŽleur. Le
|
|
140
|
+
pipeline a déjà parsé les cookies entrants avant que ton action ne s'exécute.
|
|
141
|
+
|
|
142
|
+
### Un contrÎleur qui lit et écrit un cookie
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
// nodefony/controller/PreferencesController.ts â complet, compile tel quel
|
|
146
|
+
import { Controller, controller, Get } from "@nodefony/framework";
|
|
147
|
+
import { Cookie } from "@nodefony/http";
|
|
148
|
+
import type { Context } from "@nodefony/http";
|
|
149
|
+
|
|
150
|
+
@controller("/prefs")
|
|
151
|
+
class PreferencesController extends Controller {
|
|
152
|
+
constructor(context: Context) {
|
|
153
|
+
super("PreferencesController", context);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// LECTURE â le pipeline a dĂ©jĂ parsĂ© l'en-tĂȘte `Cookie:` dans context.cookies.
|
|
157
|
+
@Get("/")
|
|
158
|
+
async read() {
|
|
159
|
+
const theme = this.context?.getRequestCookies("theme");
|
|
160
|
+
return this.renderJson({
|
|
161
|
+
theme: theme instanceof Cookie ? theme.value : null,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// ĂCRITURE â construire puis poser sur la rĂ©ponse ; l'attribut Secure/HttpOnly
|
|
166
|
+
// /SameSite=Lax est appliqué par défaut. Ici on OUVRE volontairement en JS.
|
|
167
|
+
@Get("/set")
|
|
168
|
+
async write() {
|
|
169
|
+
const cookie = new Cookie("theme", "dark", {
|
|
170
|
+
maxAge: 30 * 24 * 60 * 60, // 30 jours, EN SECONDES
|
|
171
|
+
sameSite: "Lax",
|
|
172
|
+
httpOnly: false, // prĂ©fĂ©rence non sensible â lisible cĂŽtĂ© client
|
|
173
|
+
});
|
|
174
|
+
this.context?.setCookie(cookie);
|
|
175
|
+
return this.renderJson({ set: cookie.serialize() });
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export default PreferencesController;
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Ce qu'on observe au terminal
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
# Poser le cookie â la ligne Set-Cookie porte les attributs sĂ©rialisĂ©s
|
|
186
|
+
curl -si http://127.0.0.1:5151/prefs/set | grep -i set-cookie
|
|
187
|
+
# set-cookie: theme=dark; Max-Age=2592000; Path=/; SameSite=Lax; Expires=âŠ
|
|
188
|
+
|
|
189
|
+
# Le renvoyer et le lire
|
|
190
|
+
curl -s --cookie "theme=dark" http://127.0.0.1:5151/prefs
|
|
191
|
+
# {"theme":"dark"}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
> [!NOTE]
|
|
195
|
+
> Le défaut `Secure` fait qu'un cookie posé en HTTP clair (`5151`) n'est stocké par le navigateur **que**
|
|
196
|
+
> sur `localhost` (tolérance des navigateurs). En production, on sert en HTTPS et `Secure` est correct.
|
|
197
|
+
|
|
198
|
+
### Un cookie signé (intégrité)
|
|
199
|
+
|
|
200
|
+
Pour qu'une valeur ne puisse pas ĂȘtre **falsifiĂ©e** cĂŽtĂ© client (sans la chiffrer), signe-la â il faut un
|
|
201
|
+
secret **configuré** (le secret par défaut est refusé) :
|
|
202
|
+
|
|
203
|
+
```typescript ignore
|
|
204
|
+
// fragment â un secret prĂ©visible est refusĂ© (fail-closed)
|
|
205
|
+
const c = new Cookie("pref", "v1", {
|
|
206
|
+
signed: true,
|
|
207
|
+
secret: process.env.COOKIE_SECRET!,
|
|
208
|
+
});
|
|
209
|
+
// c.value === "s:v1.<hmac base64url>" ; c.unsign() renvoie "v1" ou false si altéré
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## đ§° API publique
|
|
213
|
+
|
|
214
|
+
Les signatures exactes vivent dans `.ai/symbols.json` et les types gĂ©nĂ©rĂ©s â jamais recopiĂ©es ici. Ce qui
|
|
215
|
+
suit montre l'**usage réel**. La classe s'importe `import { Cookie } from "@nodefony/http"` ; les interfaces
|
|
216
|
+
`ICookie`, `ICookieOptions`, `IWsCookie`, `SameSiteType` en `import type`.
|
|
217
|
+
|
|
218
|
+
### Construire un cookie et ses attributs
|
|
219
|
+
|
|
220
|
+
Le constructeur accepte `(nom, valeur, options?)` ou **un cookie Ă copier** (surcharge,
|
|
221
|
+
`cookie.ts:149`). Les options fusionnent avec les défauts sûrs.
|
|
222
|
+
|
|
223
|
+
| Option | Type | Défaut | Effet |
|
|
224
|
+
| ---------- | -------------------------- | ------------ | ---------------------------------------------------------------------- |
|
|
225
|
+
| `maxAge` | `number` (secondes) | `0` | Durée de vie. `0` = cookie de session (ni `Max-Age` ni `Expires`). |
|
|
226
|
+
| `expires` | `Date \| string \| number` | â | Date d'expiration absolue (`setExpires()`, `cookie.ts:264`). |
|
|
227
|
+
| `path` | `string` | `/` | Préfixe d'URL de portée. |
|
|
228
|
+
| `domain` | `string` | `undefined` | Domaine de portée (retiré si nom `__Host-`). |
|
|
229
|
+
| `secure` | `boolean` | `true` | HTTPS only. Forcé si `SameSite=None` ou préfixe `__Host-`/`__Secure-`. |
|
|
230
|
+
| `httpOnly` | `boolean` | `true` | Invisible Ă `document.cookie` (anti-XSS). |
|
|
231
|
+
| `sameSite` | `SameSiteType` | `Lax` | `Strict`/`Lax`/`None`. Toute autre valeur retombe sur `Lax`. |
|
|
232
|
+
| `signed` | `boolean` | `false` | Signe la valeur (HMAC). Exige `secret` configuré, sinon **jette**. |
|
|
233
|
+
| `secret` | `string` | (par défaut) | Clé HMAC. Le secret par défaut est **refusé** pour signer. |
|
|
234
|
+
| `priority` | `High \| Medium \| Low` | `undefined` | Attribut `Priority` (`setPriority()`, `cookie.ts:297`). |
|
|
235
|
+
|
|
236
|
+
Les défauts sont matérialisés dans `cookieDefaultSettings` (`cookie.ts:43`) ; la fusion se fait dans le
|
|
237
|
+
constructeur de `Cookie` (`cookie.ts:130`).
|
|
238
|
+
|
|
239
|
+
### Sérialiser : `serialize()` et `serializeWebSocket()`
|
|
240
|
+
|
|
241
|
+
- `serialize()` (`cookie.ts:383`) produit la **ligne `Set-Cookie`** complĂšte (attributs dans l'ordre,
|
|
242
|
+
préfixes forcés, `Max-Age` seulement s'il est positif).
|
|
243
|
+
- `serializeWebSocket()` (`cookie.ts:425`) produit un **objet** `IWsCookie` (mĂȘmes invariants de sĂ©curitĂ©)
|
|
244
|
+
â utilisĂ© quand un cookie doit ĂȘtre dĂ©crit hors en-tĂȘte HTTP.
|
|
245
|
+
- `toString()` (`cookie.ts:317`) ne rend que `nom=valeurEncodée` (sans attributs).
|
|
246
|
+
|
|
247
|
+
### Signer / vérifier : `sign()` et `unsign()`
|
|
248
|
+
|
|
249
|
+
- `sign(val, secret)` (`cookie.ts:331`) â `val.base64url(HMAC-SHA256)` â la valeur d'origine est
|
|
250
|
+
**préservée** (récupérable), la signature garantit l'intégrité.
|
|
251
|
+
- `unsign(val?, secret?)` (`cookie.ts:355`) â la valeur en clair si la signature est valide, sinon `false`.
|
|
252
|
+
Comparaison **timing-safe** (`cookie.ts:380`) ; tolÚre le préfixe marqueur `s:`.
|
|
253
|
+
|
|
254
|
+
### Lire et écrire depuis le contexte
|
|
255
|
+
|
|
256
|
+
| Besoin | Appel | OĂč |
|
|
257
|
+
| ------------------------------ | ----------------------------------------------------- | ---------------------------------------- |
|
|
258
|
+
| Lire un cookie entrant | `context.getRequestCookies("nom")` â `Cookie \| null` | `getRequestCookies()` (`Context.ts:660`) |
|
|
259
|
+
| Lire tous les cookies entrants | `context.cookies` â `Record<string, Cookie>` | `cookies` (`Context.ts:193`) |
|
|
260
|
+
| Ăcrire un cookie sortant | `context.setCookie(new Cookie(âŠ))` | `setCookie()` (`Context.ts:667`) |
|
|
261
|
+
| Supprimer un cookie sortant | `response.deleteCookieByName("nom")` | `http/Response.ts:119` |
|
|
262
|
+
|
|
263
|
+
CÎté réponse HTTP, `addCookie()` (`http/Response.ts:101`) enregistre le cookie, et `setCookies()`
|
|
264
|
+
(`http/Response.ts:107`) Ă©met **une ligne `Set-Cookie` par cookie** â un tableau passĂ© Ă Node, jamais une
|
|
265
|
+
boucle de `setHeader` (qui écraserait tout sauf le dernier). Pour expirer un cookie chez le client :
|
|
266
|
+
`clearCookie()` (`cookie.ts:198`) recule `Expires` à l'époque.
|
|
267
|
+
|
|
268
|
+
### Parsing des cookies entrants
|
|
269
|
+
|
|
270
|
+
`cookiesParser(context)` (`cookie.ts:91`) lit l'en-tĂȘte `Cookie:` (via la bibliothĂšque `cookie`,
|
|
271
|
+
`parser()` `cookie.ts:54`), crée un `Cookie` par entrée et l'ajoute au contexte avec `addRequestCookie()`
|
|
272
|
+
(`Context.ts:650`). Il est dĂ©clenchĂ© automatiquement par le pipeline : `parseCookies()` est appelĂ© Ă
|
|
273
|
+
l'initialisation du contexte HTTP (`HttpContext.ts:190`) **et** WebSocket (`WebsocketContext.ts:170`).
|
|
274
|
+
|
|
275
|
+
### CĂŽtĂ© WebSocket â lecture oui, Ă©criture non
|
|
276
|
+
|
|
277
|
+
Les cookies **entrants** sont lus au handshake, exactement comme en HTTP (mĂȘme parseur). Mais la **poignĂ©e
|
|
278
|
+
de main WebSocket ne peut pas poser de cookie** : `setCookie()` et `setCookies()` de la réponse WS sont des
|
|
279
|
+
**no-op** (`websocket/Response.ts:295`), une limite de la bibliothĂšque `ws`. Le cookie de session, lui, est
|
|
280
|
+
posé pendant la **phase HTTP** qui précÚde l'upgrade. La forme d'un cookie décrit pour le WS est
|
|
281
|
+
`IWsCookie` (`ICookie.ts:19`), produite par `serializeWebSocket()`.
|
|
282
|
+
|
|
283
|
+
## âïž Configuration
|
|
284
|
+
|
|
285
|
+
Les cookies **applicatifs** ne se configurent pas par schéma : on les construit dans le code, avec les
|
|
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:727`), avec notamment `hostPrefix`
|
|
288
|
+
(`config.ts:730`) qui décide du préfixe `__Host-`. Tout cela est documenté dans [Sessions](session.md) :
|
|
289
|
+
cette page ne le duplique pas.
|
|
290
|
+
|
|
291
|
+
Le nom effectif du cookie de session (avec ou sans `__Host-` selon le transport) est calculé par
|
|
292
|
+
`getSessionCookieName()` (`Context.ts:714`) â encore un dĂ©tail qui appartient Ă la page Sessions.
|
|
293
|
+
|
|
294
|
+
## đĄïž DĂ©fenses par attribut
|
|
295
|
+
|
|
296
|
+
| Attribut / mécanisme | Faille bloquée | Comment Nodefony l'applique |
|
|
297
|
+
| -------------------------- | --------------------------------------- | ------------------------------------------------------------------------ |
|
|
298
|
+
| `HttpOnly` (défaut `true`) | Vol de cookie par **XSS** | Défaut fermé (`cookieDefaultSettings`, `cookie.ts:43`) |
|
|
299
|
+
| `SameSite=Lax` (défaut) | **CSRF** inter-site | Fallback toujours `Lax`, jamais `None` (`setSameSite()` `cookie.ts:250`) |
|
|
300
|
+
| `Secure` (défaut `true`) | Interception en clair | Forcé aussi par `None`/préfixes (`serialize()` `cookie.ts:393`) |
|
|
301
|
+
| Préfixe `__Host-` | **Session fixation** cross-sous-domaine | `Domain` retiré + `Path=/` imposés (`serialize()` `cookie.ts:399`) |
|
|
302
|
+
| Cookie signé (HMAC) | Altération de la valeur cÎté client | `sign()`/`unsign()` timing-safe (`cookie.ts:331`, `cookie.ts:380`) |
|
|
303
|
+
| Refus du secret par défaut | Signature « fantÎme » sans protection | Fail-closed à la signature (`setValue()` `cookie.ts:199`) |
|
|
304
|
+
|
|
305
|
+
## đ Normes appliquĂ©es
|
|
306
|
+
|
|
307
|
+
| Domaine | Norme | Ancrage |
|
|
308
|
+
| ---------------------------------- | -------------------- | ------------------------------------------------------------------ |
|
|
309
|
+
| Cookies â syntaxe `Set-Cookie` | RFC 6265bis | `serialize()` (`cookie.ts:383`) |
|
|
310
|
+
| `SameSite` â 3 valeurs canoniques | RFC 6265bis §5.4.7 | `SameSiteType` (`ICookie.ts:3`), `setSameSite()` (`cookie.ts:250`) |
|
|
311
|
+
| Préfixes `__Host-` / `__Secure-` | RFC 6265bis §4.1.3 | `serialize()` force les contraintes (`cookie.ts:390`) |
|
|
312
|
+
| `SameSite=None` impose `Secure` | RFC 6265bis | `serialize()` (`cookie.ts:393`) |
|
|
313
|
+
| IntĂ©gritĂ© â HMAC-SHA256, base64url | RFC 2104 / RFC 4648 | `sign()` (`cookie.ts:331`) |
|
|
314
|
+
| VĂ©rification Ă temps constant | bonne pratique OWASP | `unsign()` â `timingSafeEqual` (`cookie.ts:380`) |
|
|
315
|
+
|
|
316
|
+
## â ïž PiĂšges (symptĂŽme â cause â correction)
|
|
317
|
+
|
|
318
|
+
| SymptĂŽme | Cause | Correction |
|
|
319
|
+
| --------------------------------------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
|
|
320
|
+
| Le cookie posĂ© en HTTP clair n'est pas stockĂ© | DĂ©faut `Secure: true` â le navigateur exige HTTPS (sauf `localhost`) | Servir en HTTPS, ou `secure: false` en dev hors localhost |
|
|
321
|
+
| Un cookie `__Host-` ignore mon `Domain`/`Path` | `serialize()` **force** les contraintes du prĂ©fixe | Attendu (RFC 6265bis) â renommer sans prĂ©fixe si tu veux un `Domain` |
|
|
322
|
+
| `new Cookie(..., { signed: true })` **jette** | Aucun `secret` configurĂ© â refus du secret prĂ©visible (fail-closed) | Passer un `secret` rĂ©el (`{ signed: true, secret: ⊠}`) |
|
|
323
|
+
| Modifier un cookie entrant ne change rien cĂŽtĂ© client | Lecture (`context.cookies`) et Ă©criture (rĂ©ponse) ne sont pas symĂ©triques | (Re)poser le cookie sur la rĂ©ponse : `context.setCookie(new Cookie(âŠ))` |
|
|
324
|
+
| Le cookie WS posé au handshake n'arrive jamais | `setCookie`/`setCookies` de la réponse WS sont des no-op (`ws`) | Poser le cookie pendant la phase HTTP avant l'upgrade (cf session) |
|
|
325
|
+
| Deux `Set-Cookie` s'écrasent, un seul survit | Un `setHeader('Set-Cookie', str)` remplace le précédent | Déjà géré : `setCookies()` passe un **tableau** (`http/Response.ts:117`) |
|
|
326
|
+
| `SameSite` mal orthographiĂ© devient `Lax` silencieusement | Fallback fail-safe sur `Lax` | Attendu â vĂ©rifier la casse ; `Strict`/`Lax`/`None` seulement |
|
|
327
|
+
| `maxAge` interprété en millisecondes | `maxAge` est en **secondes** (comme `Set-Cookie` Max-Age) | Passer des secondes (`30*24*60*60`), pas des ms |
|
|
328
|
+
|
|
329
|
+
## đĄ ObservabilitĂ©
|
|
330
|
+
|
|
331
|
+
Il n'y a pas d'écran Studio dédié aux cookies applicatifs (le cookie **de session** est surfacé dans
|
|
332
|
+
l'écran **Sessions**). En développement, chaque écriture de cookie est journalisée en `DEBUG` par la
|
|
333
|
+
rĂ©ponse HTTP (`ADD COOKIE ==> âŠ`, `setCookie()` `http/Response.ts:126`) â visible via le skill
|
|
334
|
+
`nodefony-tail-error-logs` ou le Suivi de requĂȘte. Sur le fil, un `curl -i` montre les lignes `Set-Cookie`.
|
|
335
|
+
|
|
336
|
+
## đ§Ș Tests & couverture
|
|
337
|
+
|
|
338
|
+
Les chiffres exacts vivent dans la carte de tests de cette page (régénérée depuis vitest, jamais figés dans
|
|
339
|
+
le Markdown).
|
|
340
|
+
|
|
341
|
+
| Type | OĂč |
|
|
342
|
+
| ---------------------- | ----------------------------------------------------------------------------------------------------- |
|
|
343
|
+
| Unitaire (dĂ©diĂ©) | `tests/unit/Cookie.test.ts` â constructeur, copie, `toString`, `serialize`, `clearCookie`, `setValue` |
|
|
344
|
+
| Unitaire â RFC 6265bis | `Cookie.test.ts` â `SameSite`/`__Host-`/`__Secure-`, `None â Secure`, `serializeWebSocket` |
|
|
345
|
+
| Unitaire â signature | `Cookie.test.ts` â `sign`/`unsign` (round-trip, altĂ©ration, mauvais secret, `s:`, fail-closed) |
|
|
346
|
+
| Unitaire â rĂ©gression | `Cookie.test.ts` â overflow `Max-Age`/`Expires` (`maxAge=0/3600/86400/undefined`) |
|
|
347
|
+
| Adjacent (rĂ©ponse) | `tests/unit/Response.test.ts` â `addCookie`/`setCookies` cĂŽtĂ© `HttpResponse` |
|
|
348
|
+
|
|
349
|
+
Ce qui **manque** aujourd'hui : le round-trip `Set-Cookie` â navigateur â `Cookie:` d'un cookie
|
|
350
|
+
**applicatif** arbitraire n'a pas de test d'intégration dédié (il est exercé indirectement par les tests de
|
|
351
|
+
session et de CSRF, qui posent et relisent des cookies sur serveur réel) ; et il n'y a pas de test
|
|
352
|
+
d'attaque `*.attack.test.ts` centré cookies (l'altération de valeur signée est couverte unitairement).
|
|
353
|
+
|
|
354
|
+
Suites : `npm test` (unitaires â la classe `Cookie` s'y teste sans serveur). Couverture : `npm run
|
|
355
|
+
coverage` dans `@nodefony/http` â le pourcentage vit dans le rapport vitest, jamais figĂ© ici. Skill
|
|
356
|
+
associé : `nodefony-security-review` (revue des attributs de sécurité d'un cookie dans un diff).
|
|
357
|
+
|
|
358
|
+
## đ Pour aller plus loin
|
|
359
|
+
|
|
360
|
+
- âŹïž **Retour au hub** : [@nodefony/http â vue du module](index.md) · [Toute la documentation](../../../../../docs/index.md)
|
|
361
|
+
- đ§ **Pages sĆurs** : [Sessions](session.md) (le cookie de session, sa config `hostPrefix`, sa rĂ©vocation) · [Serveurs](servers.md)
|
|
362
|
+
- Le trajet complet d'une requĂȘte (oĂč le parsing des cookies s'insĂšre) â [pipeline-requete](../../../../../docs/architecture/pipeline-requete.md)
|
|
363
|
+
- En-tĂȘtes de sĂ©curitĂ© applicatifs (CSP, Referrer-PolicyâŠ) â [En-tĂȘtes](../../security/docs/headers.md)
|
|
364
|
+
- Le pare-feu et CSRF par-dessus le transport â [Firewall](../../security/docs/firewall.md)
|
|
365
|
+
- Configuration d'application (`defineConfig`, `use`, env) â [configuration](../../../../../docs/guides/configuration.md)
|
package/docs/index.md
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "@nodefony/http â la couche transport"
|
|
3
|
+
navTitle: "@nodefony/http"
|
|
4
|
+
lang: fr
|
|
5
|
+
module: "@nodefony/http"
|
|
6
|
+
topic: http
|
|
7
|
+
section: "CĆur runtime"
|
|
8
|
+
audience: [developer, devops]
|
|
9
|
+
tags:
|
|
10
|
+
[http, https, http2, websocket, wss, context, serveurs, sessions, transport]
|
|
11
|
+
version: "doc"
|
|
12
|
+
status: stable
|
|
13
|
+
updated: 2026-07-19
|
|
14
|
+
source: "src/packages/@nodefony/http/docs/index.md"
|
|
15
|
+
coverageModule: http
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
# @nodefony/http â la couche transport
|
|
19
|
+
|
|
20
|
+
> Les portes d'entrĂ©e du processus. Ce module ouvre les sockets, accepte les connexions â web **et**
|
|
21
|
+
> temps rĂ©el â et construit le **contexte de requĂȘte** que tout le reste du framework consomme. Sa
|
|
22
|
+
> particularité tient en une phrase : HTTP et WebSocket ne sont pas deux mondes, ce sont **deux entrées
|
|
23
|
+
> du mĂȘme pipeline**. C'est de lĂ que vient le diffĂ©renciateur de Nodefony.
|
|
24
|
+
|
|
25
|
+
đ [Documentation](../../../../../docs/index.md) âș **@nodefony/http**
|
|
26
|
+
|
|
27
|
+
## đ§ Par oĂč commencer
|
|
28
|
+
|
|
29
|
+
Trois parcours selon ce que tu viens faire. L'ordre compte : chaque étape suppose la précédente.
|
|
30
|
+
|
|
31
|
+
**Je dĂ©couvre le module** â comprendre avant de configurer.
|
|
32
|
+
|
|
33
|
+
1. [Serveurs](servers.md) â ce qui Ă©coute, sur quels ports, et comment ça dĂ©marre et s'arrĂȘte.
|
|
34
|
+
2. [Pipeline de requĂȘte](../../../../../docs/architecture/pipeline-requete.md) â le trajet complet
|
|
35
|
+
d'une requĂȘte, du socket jusqu'Ă ton contrĂŽleur. **La page qui relie tout.**
|
|
36
|
+
3. [Sessions](session.md) â le premier Ă©tat serveur que rencontre une application rĂ©elle.
|
|
37
|
+
4. [Routage et contrĂŽleurs](../../framework/docs/index.md) â la suite du voyage, dans `@nodefony/framework`.
|
|
38
|
+
|
|
39
|
+
**Je mets en production** â ce qu'un serveur exposĂ© doit tenir.
|
|
40
|
+
|
|
41
|
+
1. [Serveurs](servers.md) â TLS, certificats, politique de port, arrĂȘt gracieux, sondes de vie.
|
|
42
|
+
2. [Sessions](session.md) â choisir un store partagĂ© : sans lui, deux pods ne partagent aucune session.
|
|
43
|
+
3. [Pipeline de requĂȘte](../../../../../docs/architecture/pipeline-requete.md) â oĂč se branchent
|
|
44
|
+
rate-limit, en-tĂȘtes et firewall.
|
|
45
|
+
4. [Rate-limit](rate-limit.md) â plafonner le dĂ©bit par client avant que la charge n'atteigne le contrĂŽleur.
|
|
46
|
+
5. [ObservabilitĂ©](observabilite.md) â corrĂ©ler les logs par `requestId` pour diagnostiquer Ă chaud.
|
|
47
|
+
6. [SĂ©curitĂ©](../../security/docs/index.md) â le pare-feu applicatif se pose par-dessus ce module.
|
|
48
|
+
|
|
49
|
+
**Je fais du temps rĂ©el** â WebSocket dans le mĂȘme contexte que le web.
|
|
50
|
+
|
|
51
|
+
1. [Serveurs](servers.md) â le WS n'a pas de port Ă lui : il se greffe sur son porteur HTTP.
|
|
52
|
+
2. [Sessions](session.md) â la session cĂŽtĂ© WebSocket, et pourquoi elle passe par l'ALS.
|
|
53
|
+
3. [La socket Nodefony](../../realtime/docs/index.md) â la couche au-dessus,
|
|
54
|
+
qui multiplexe N canaux sur une connexion.
|
|
55
|
+
|
|
56
|
+
## đïž Les briques du module
|
|
57
|
+
|
|
58
|
+
Le tableau pour choisir vite ; les cards en dessous pour savoir ce qu'on y trouve.
|
|
59
|
+
|
|
60
|
+
<!-- prettier-ignore -->
|
|
61
|
+
| Brique | Ce qu'elle résout | Tu en as besoin quand⊠|
|
|
62
|
+
| --- | --- | --- |
|
|
63
|
+
| [Serveurs](servers.md) | ouvrir, rĂ©gler, transmettre, fermer proprement | toujours â c'est la fondation |
|
|
64
|
+
| [Sessions](session.md) | de l'état serveur rattaché à un visiteur | login, panier, préférences, WS authentifié |
|
|
65
|
+
| [Cookies](cookies.md) | lire/écrire des cookies sûrs (SameSite, signés) | tu poses un état cÎté client hors session |
|
|
66
|
+
| [Upload & corps](upload.md) | parser le corps et recevoir des fichiers | formulaires, imports multipart, API JSON |
|
|
67
|
+
| [Rate-limit](rate-limit.md) | plafonner le débit par client (429) | protéger une API d'un flood ou d'un abus |
|
|
68
|
+
| [ObservabilitĂ©](observabilite.md) | tracer et journaliser chaque requĂȘte | dĂ©bugger en prod, corrĂ©ler des logs |
|
|
69
|
+
| [Pipeline de requĂȘte](../../../../../docs/architecture/pipeline-requete.md) | l'ordre exact des Ă©tapes, HTTP comme WS | tu dĂ©bugges « pourquoi ça passe / ça bloque ici » |
|
|
70
|
+
|
|
71
|
+
```nodefony-cards
|
|
72
|
+
[
|
|
73
|
+
{ "icon": "đ", "title": "servers", "href": "servers.md",
|
|
74
|
+
"desc": "Deux ports, quatre serveurs, un seul pipeline : politique de port, certificats et TLS de dĂ©veloppement, rĂ©glage du transport, sondes de liveness/readiness, arrĂȘt gracieux, dĂ©fenses de bordure (slow-loris, floods, zombies WebSocket).",
|
|
75
|
+
"meta": "commence ici â tout le reste suppose un serveur qui Ă©coute" },
|
|
76
|
+
{ "icon": "đïž", "title": "session", "href": "session.md",
|
|
77
|
+
"desc": "Cycle de vie complet, cookie opaque, les quatre stores (memory, drizzle, redis, mongoose) et comment auto en choisit un, les délais NIST, la révocation, la session cÎté WebSocket.",
|
|
78
|
+
"meta": "la brique oĂč un choix de dev (memory) devient un bug de prod" },
|
|
79
|
+
{ "icon": "đȘ", "title": "cookies", "href": "cookies.md",
|
|
80
|
+
"desc": "Lire et écrire des cookies : attributs SameSite, Secure, HttpOnly, Path, Domain, Max-Age/Expires, parsing des cookies entrants, signature HMAC, cookies cÎté WebSocket. Le cookie de session a sa propre page.",
|
|
81
|
+
"meta": "dÚs que tu poses un état cÎté client hors session" },
|
|
82
|
+
{ "icon": "đ", "title": "upload", "href": "upload.md",
|
|
83
|
+
"desc": "RĂ©ception du corps de requĂȘte (JSON, urlencoded, multipart, brut) et upload de fichiers : accĂšs aux champs et fichiers, API UploadedFile (taille, type, move), bornes de payload (413) et sĂ»retĂ© du nom de fichier.",
|
|
84
|
+
"meta": "formulaires, imports de fichiers, API JSON" },
|
|
85
|
+
{ "icon": "âł", "title": "rate-limit", "href": "rate-limit.md",
|
|
86
|
+
"desc": "Limiter le dĂ©bit par client : fenĂȘtre et quota configurables, rĂ©ponse 429 avec en-tĂȘtes X-RateLimit-* et Retry-After, store pluggable, limites cĂŽtĂ© WebSocket (handshake + connexions concurrentes), introspection admin.",
|
|
87
|
+
"meta": "protéger une API d'un flood ou d'un abus" },
|
|
88
|
+
{ "icon": "đ", "title": "observabilite", "href": "observabilite.md",
|
|
89
|
+
"desc": "Observer les requĂȘtes : lignes de log (pretty ou JSON), requestId de corrĂ©lation, W3C Trace Context, trace des frames WebSocket, redaction et sampling d'audit. OĂč partent les logs est traitĂ© par la page Syslog du cĆur.",
|
|
90
|
+
"meta": "dĂ©bugger en prod, corrĂ©ler les logs par requĂȘte" },
|
|
91
|
+
{ "icon": "đ", "title": "pipeline-requete", "href": "../../../../../docs/architecture/pipeline-requete.md",
|
|
92
|
+
"desc": "OĂč ce module s'arrĂȘte et oĂč le framework prend le relais, et dans quel ordre s'enchaĂźnent contexte, rate-limit, routage, session, CSRF et firewall.",
|
|
93
|
+
"meta": "page transverse â celle qui relie tout" }
|
|
94
|
+
]
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
> [!NOTE]
|
|
98
|
+
> **Deux briques n'ont pas encore leur page dédiée** : les fichiers statiques et les certificats. Ils
|
|
99
|
+
> sont implémentés et testés ; en attendant, leur configuration vit dans les blocs Zod de
|
|
100
|
+
> `nodefony/config/config.ts` et leur comportement est décrit dans la page [Serveurs](servers.md)
|
|
101
|
+
> (repli statique, stratégies de certificats TLS).
|
|
102
|
+
|
|
103
|
+
## đïž Place dans le framework
|
|
104
|
+
|
|
105
|
+
```mermaid
|
|
106
|
+
flowchart TD
|
|
107
|
+
N["node:http · node:https · node:http2 · ws"] --> SRV["serveurs<br/>http · https/h2 · ws · wss"]
|
|
108
|
+
SRV --> HK["HttpKernel<br/>orchestrateur du pipeline"]
|
|
109
|
+
HK --> CTX["Context<br/>HttpContext · WebsocketContext"]
|
|
110
|
+
CTX --> SESS["sessions · cookies · upload"]
|
|
111
|
+
CTX --> FW["@nodefony/security<br/>firewall, CSRF, CORS"]
|
|
112
|
+
FW --> FRW["@nodefony/framework<br/>routage â contrĂŽleur"]
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
`@nodefony/http` ne connaĂźt ni les routes ni les contrĂŽleurs â il ne peut pas importer
|
|
116
|
+
`@nodefony/framework` (ce serait un cycle). Il expose un contexte ; le framework s'y branche.
|
|
117
|
+
|
|
118
|
+
## đ§° Surface publique
|
|
119
|
+
|
|
120
|
+
Depuis une application : `Context`, `HttpContext`, `WebsocketContext`, `Session`, `SessionsService`,
|
|
121
|
+
les services de serveurs, `cookie`, `httpError`, le profiler. Les signatures exactes vivent dans
|
|
122
|
+
`.ai/symbols.json` et dans les types gĂ©nĂ©rĂ©s â jamais recopiĂ©es Ă la main dans cette page, oĂč elles
|
|
123
|
+
se périmeraient en silence.
|
|
124
|
+
|
|
125
|
+
## âïž Configuration
|
|
126
|
+
|
|
127
|
+
Tout se déclare dans `nodefony.config.ts` via `use("@nodefony/http", { ⊠})`. Les blocs Zod
|
|
128
|
+
(`nodefony/config/config.ts`) couvrent : `servers` (ports, transport, TLS, HTTP/2), `session` et
|
|
129
|
+
`cookie`, `trustProxy`, `certificates`, `upload`, le rate-limit et les fichiers statiques. Chaque page
|
|
130
|
+
de brique détaille son bloc et ses défauts réels.
|
|
131
|
+
|
|
132
|
+
## đ Normes appliquĂ©es
|
|
133
|
+
|
|
134
|
+
RFC 9110/9111/9112 (sémantique HTTP, cache, HTTP/1.1), RFC 9113 (HTTP/2), RFC 6455 (WebSocket et ses
|
|
135
|
+
codes de fermeture), RFC 6265bis (cookies), RFC 6585 (429), RFC 6125 (identité des certificats),
|
|
136
|
+
WHATWG Fetch (CORS), W3C Trace Context (`traceparent`).
|
|
137
|
+
|
|
138
|
+
## đĄ ObservabilitĂ© â Studio
|
|
139
|
+
|
|
140
|
+
Le profiler mesure les phases d'une requĂȘte et alimente le data plane admin (`HttpAdminApi`). Les
|
|
141
|
+
sessions sont surfacées dans l'écran **Sessions** (`/nodefony/sessions`), les corrélations par
|
|
142
|
+
`traceparent` dans l'écran **Traces** (`/nodefony/logs/trace/{requestId}`), et l'état des serveurs dans la carte du module.
|
|
143
|
+
|
|
144
|
+
## đ§Ș Tests & couverture
|
|
145
|
+
|
|
146
|
+
Le module porte la plus grosse couverture du dĂ©pĂŽt â les chiffres exacts vivent dans la carte de
|
|
147
|
+
l'aperçu, régénérée depuis vitest, jamais figés dans la prose.
|
|
148
|
+
|
|
149
|
+
| Type | OĂč | Ce qui est prouvĂ© |
|
|
150
|
+
| ---------------- | ------------------------------------- | ----------------------------------------------------- |
|
|
151
|
+
| Unitaire | `tests/unit/**` | cookies, session, erreurs, trust-proxy, requestId |
|
|
152
|
+
| Intégration | `tests/{http,integration,routing}/**` | pipeline réel sur serveur vivant, TLS, statiques |
|
|
153
|
+
| WebSocket | `tests/websockets/**` | handshake, protocoles, binaire, broadcast, sessions |
|
|
154
|
+
| Contrat | `tests/support/*Contract.ts` | un store tiers respecte le contrat attendu |
|
|
155
|
+
| Charge / mémoire | `tests/load/**` + `memory.test.ts` | seuils de heap, connexions soutenues, débit de frames |
|
|
156
|
+
|
|
157
|
+
## đ Pour aller plus loin
|
|
158
|
+
|
|
159
|
+
- Le trajet d'une requĂȘte â [pipeline-requete](../../../../../docs/architecture/pipeline-requete.md)
|
|
160
|
+
- Routage et contrĂŽleurs â [@nodefony/framework](../../framework/docs/index.md)
|
|
161
|
+
- Le pare-feu par-dessus â [@nodefony/security](../../security/docs/index.md)
|
|
162
|
+
- Choisir un store de sessions â [session-storage](../../../../../docs/guides/session-storage.md)
|
|
163
|
+
- Vue d'ensemble du framework â [vue-ensemble](../../../../../docs/architecture/vue-ensemble.md)
|