@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
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
import { Service, Module, Scope, EnvironmentType, DebugType, nodefonyError } from "nodefony";
|
|
2
|
+
import type { Resolver, Router } from "@nodefony/framework";
|
|
3
|
+
import type { Controller } from "@nodefony/framework";
|
|
4
|
+
import HttpError from "../src/errors/httpError.js";
|
|
5
|
+
import { type TrustProxyChecker } from "../src/context/trustProxy.js";
|
|
6
|
+
import type { IRateLimitStore } from "../src/rateLimit/IRateLimitStore.js";
|
|
7
|
+
import { type TrustedHostsConfig } from "../src/context/domainMatcher.js";
|
|
8
|
+
import http from "node:http";
|
|
9
|
+
import http2 from "node:http2";
|
|
10
|
+
import type { IncomingMessage } from "node:http";
|
|
11
|
+
import Ws from "ws";
|
|
12
|
+
import httpServer from "../service/servers/server-http.js";
|
|
13
|
+
import httpsServer from "../service/servers/server-https.js";
|
|
14
|
+
import websocketServer from "../service/servers/server-websocket.js";
|
|
15
|
+
import websocketSecureServer from "../service/servers/server-websocket-secure.js";
|
|
16
|
+
import Statics from "./servers/server-static.js";
|
|
17
|
+
import WebsocketContext from "../src/context/websocket/WebsocketContext.js";
|
|
18
|
+
import HttpContext from "../src/context/http/HttpContext.js";
|
|
19
|
+
import Context, { HTTPMethod, WebSocketState } from "../src/context/Context.js";
|
|
20
|
+
import Certicates from "./certificates.js";
|
|
21
|
+
import SessionsService from "./sessions/sessions-service.js";
|
|
22
|
+
import Session from "../src/session/session.js";
|
|
23
|
+
import { Firewall } from "@nodefony/security";
|
|
24
|
+
import type { IErrorRenderer } from "../interfaces/IErrorRenderer.js";
|
|
25
|
+
import type { IRequestLogger } from "../interfaces/IRequestLogger.js";
|
|
26
|
+
export type ProtocolType = "1.1" | "2.0" | "3.0";
|
|
27
|
+
export type httpRequest = http.IncomingMessage | http2.Http2ServerRequest;
|
|
28
|
+
export type httpResponse = http.ServerResponse | http2.Http2ServerResponse;
|
|
29
|
+
export type ContextType = WebsocketContext | HttpContext | Context;
|
|
30
|
+
export type ServerType = "http" | "https" | "http2" | "http3" | "websocket" | "websocket-secure";
|
|
31
|
+
export type responseTimeoutType = "http" | "https" | "http2" | "http3";
|
|
32
|
+
export type SchemeType = "http" | "https" | "ws" | "wss";
|
|
33
|
+
export interface WsMetaData {
|
|
34
|
+
type?: "message" | "handshake";
|
|
35
|
+
state?: WebSocketState;
|
|
36
|
+
messageType?: "utf8" | "binary";
|
|
37
|
+
protocol?: string | null;
|
|
38
|
+
id?: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Vue minimale d'une route exposée dans l'enveloppe `nodefony.route` (frame WS
|
|
42
|
+
* + réponse JSON). Volontairement découplée de la classe `Route` de
|
|
43
|
+
* `@nodefony/framework` : on ne diffuse PAS l'instance partagée (statique) —
|
|
44
|
+
* seulement un snapshot per-requête. Casse aussi l'import valeur runtime
|
|
45
|
+
* `http → framework` (cycle).
|
|
46
|
+
*/
|
|
47
|
+
export interface RouteMetaData {
|
|
48
|
+
name: string;
|
|
49
|
+
path?: string;
|
|
50
|
+
variablesMap: Record<string, unknown>;
|
|
51
|
+
}
|
|
52
|
+
export interface MetaData {
|
|
53
|
+
name?: string;
|
|
54
|
+
version?: string;
|
|
55
|
+
url?: URL | string;
|
|
56
|
+
environment?: EnvironmentType;
|
|
57
|
+
debug?: DebugType;
|
|
58
|
+
token?: string;
|
|
59
|
+
method?: HTTPMethod;
|
|
60
|
+
scheme?: SchemeType;
|
|
61
|
+
requestId?: string;
|
|
62
|
+
websocket?: WsMetaData;
|
|
63
|
+
route?: RouteMetaData;
|
|
64
|
+
}
|
|
65
|
+
export interface Data {
|
|
66
|
+
error?: Error;
|
|
67
|
+
nodefony: MetaData;
|
|
68
|
+
message?: unknown;
|
|
69
|
+
code?: number;
|
|
70
|
+
result: unknown;
|
|
71
|
+
}
|
|
72
|
+
import type { IHttpKernel as IHttpKernelInterface } from "../interfaces/IHttpKernel.js";
|
|
73
|
+
declare class HttpKernel extends Service implements IHttpKernelInterface {
|
|
74
|
+
#private;
|
|
75
|
+
certificates: unknown;
|
|
76
|
+
serviceCerticats: Certicates | null;
|
|
77
|
+
key: string;
|
|
78
|
+
cert: string;
|
|
79
|
+
ca: string;
|
|
80
|
+
serverStatic: Statics | null;
|
|
81
|
+
domain: string;
|
|
82
|
+
trustedHosts?: TrustedHostsConfig;
|
|
83
|
+
domainCheck: boolean;
|
|
84
|
+
regAlias: RegExp[];
|
|
85
|
+
module: Module;
|
|
86
|
+
httpsPort?: number;
|
|
87
|
+
httpPort?: number;
|
|
88
|
+
responseTimeout: {
|
|
89
|
+
http: number;
|
|
90
|
+
https: number;
|
|
91
|
+
http2: number;
|
|
92
|
+
http3: number;
|
|
93
|
+
};
|
|
94
|
+
closeTimeOutWs: {
|
|
95
|
+
ws: number;
|
|
96
|
+
wss: number;
|
|
97
|
+
};
|
|
98
|
+
private secContentTypeOptions;
|
|
99
|
+
private secFrameOptions;
|
|
100
|
+
private secHsts;
|
|
101
|
+
sessionService?: SessionsService | null;
|
|
102
|
+
router?: Router | null;
|
|
103
|
+
firewall?: Firewall | null;
|
|
104
|
+
private errorRenderer;
|
|
105
|
+
private requestLogger;
|
|
106
|
+
private profiler;
|
|
107
|
+
private _trustProxyChecker;
|
|
108
|
+
private _wsOriginPolicy;
|
|
109
|
+
private rateLimiter;
|
|
110
|
+
private rateLimitGc;
|
|
111
|
+
constructor(module: Module);
|
|
112
|
+
/**
|
|
113
|
+
* (Re)calcule les caches dérivés des en-têtes de sécurité depuis `this.options`
|
|
114
|
+
* — au boot ET à chaque édition live ({@link onConfigChanged}). Lus tels quels
|
|
115
|
+
* dans `onHttpRequest` (0 alloc/concat par requête).
|
|
116
|
+
*/
|
|
117
|
+
private computeSecurityHeaderCaches;
|
|
118
|
+
/**
|
|
119
|
+
* Ce que ce kernel ÉMET réellement comme en-têtes de sécurité transport.
|
|
120
|
+
*
|
|
121
|
+
* Ces trois en-têtes (HSTS, X-Frame-Options, X-Content-Type-Options) sont posés
|
|
122
|
+
* ici, à l'entrée brute, pour couvrir aussi les statiques et les pages d'erreur
|
|
123
|
+
* — pas par `@nodefony/security`, dont les clés homonymes sont inertes. Toute
|
|
124
|
+
* console d'administration doit donc lire CE point, sinon elle affiche une
|
|
125
|
+
* intention de configuration au lieu de l'état appliqué.
|
|
126
|
+
*
|
|
127
|
+
* Lu depuis les caches recalculés au boot ET à chaque édition live
|
|
128
|
+
* ({@link onConfigChanged}) : la valeur rendue suit la config à chaud.
|
|
129
|
+
*
|
|
130
|
+
* @returns les valeurs d'en-tête, `null` quand l'en-tête n'est pas émis.
|
|
131
|
+
*/
|
|
132
|
+
describeTransportSecurityHeaders(): {
|
|
133
|
+
strictTransportSecurity: string | null;
|
|
134
|
+
frameOptions: string | null;
|
|
135
|
+
contentTypeOptions: string | null;
|
|
136
|
+
};
|
|
137
|
+
/**
|
|
138
|
+
* Hook appelé par le data plane admin APRÈS une édition LIVE de la config
|
|
139
|
+
* (`PATCH /nodefony/kernel/api/config/http`, dev only). Recompute les valeurs
|
|
140
|
+
* DÉRIVÉES mises en cache (en-têtes sécurité, checker trust-proxy, trustedHosts) :
|
|
141
|
+
* sans lui, l'édition d'un champ `runtimeMutable` porté par un cache serait
|
|
142
|
+
* ignorée. Les champs lus DIRECTEMENT depuis `options` (`headerServer`,
|
|
143
|
+
* `maxBodySize`) n'en ont pas besoin — l'appel reste idempotent et bon marché.
|
|
144
|
+
*/
|
|
145
|
+
onConfigChanged(): void;
|
|
146
|
+
/**
|
|
147
|
+
* (Re)configure le rate-limit général par IP depuis `options.rateLimit` — au
|
|
148
|
+
* boot (`onReady`) ET à chaque édition live ({@link onConfigChanged}). Désarme
|
|
149
|
+
* le GC précédent puis reconstruit compteur + scheduler UNIQUEMENT si
|
|
150
|
+
* `enabled` ; sinon `rateLimiter` reste `null` → 0 coût sur le hot path.
|
|
151
|
+
*/
|
|
152
|
+
/**
|
|
153
|
+
* Compteur de rate-limit actif, ou `null` s'il est désactivé — surface de
|
|
154
|
+
* LECTURE pour le data plane admin (introspection des IP suivies). `null` est
|
|
155
|
+
* l'information « fonction désarmée », pas une erreur : l'appelant l'affiche
|
|
156
|
+
* tel quel plutôt que d'inventer un état.
|
|
157
|
+
*/
|
|
158
|
+
get rateLimitStore(): IRateLimitStore | null;
|
|
159
|
+
private configureRateLimit;
|
|
160
|
+
/**
|
|
161
|
+
* (Re)configure le backstop OPT-IN de connexions WS concurrentes par IP (F6c)
|
|
162
|
+
* depuis `options.wsMaxConnectionsPerIp` — au boot ET à l'édition live. `null` (ou
|
|
163
|
+
* ≤ 0) = désactivé → `#wsConnCounter` reste `null` (0 alloc / 0 tracking, cas
|
|
164
|
+
* cloud-native par défaut : le cap concurrent est délégué à l'ingress). Reconstruit
|
|
165
|
+
* un compteur neuf : les sockets déjà ouvertes décrémentent l'ancienne instance
|
|
166
|
+
* (capturée dans leur closure de fermeture) → pas de dérive sur la nouvelle.
|
|
167
|
+
*/
|
|
168
|
+
private configureWsConnectionLimit;
|
|
169
|
+
/**
|
|
170
|
+
* (Re)configure les probes de santé `/livez` + `/readyz` depuis
|
|
171
|
+
* `options.health` — au boot (`onReady`) ET à l'édition live. `enabled:false`
|
|
172
|
+
* → `#healthPaths` reste `null` (0 coût sur le hot path).
|
|
173
|
+
*/
|
|
174
|
+
private configureHealth;
|
|
175
|
+
init(): Promise<this>;
|
|
176
|
+
/**
|
|
177
|
+
* Checker de confiance reverse-proxy, compilé une seule fois depuis
|
|
178
|
+
* `options.trustProxy` (lazy — pas de `BlockList` allouée par requête).
|
|
179
|
+
* Consommé par `HttpRequest`/`HttpContext` pour décider de l'adoption des
|
|
180
|
+
* en-têtes `X-Forwarded-*`.
|
|
181
|
+
*
|
|
182
|
+
* @returns le {@link TrustProxyChecker} partagé.
|
|
183
|
+
*/
|
|
184
|
+
getTrustProxyChecker(): TrustProxyChecker;
|
|
185
|
+
/**
|
|
186
|
+
* Politique d'Origin WS (B4) compilée paresseusement pour un type de serveur
|
|
187
|
+
* (`websocket` / `websocketSecure`), depuis `options.<type>.allowedOrigins` :
|
|
188
|
+
* - `true` → contrôle désactivé (`disabled`) ;
|
|
189
|
+
* - `false` (défaut) → same-origin seul (`extra` vide) ;
|
|
190
|
+
* - string/liste → Origins cross-origin additionnelles (compilées en RegExp).
|
|
191
|
+
*
|
|
192
|
+
* @param cfgKey - `"websocket"` ou `"websocketSecure"`.
|
|
193
|
+
* @returns la politique mémoïsée (0 alloc après le 1er handshake).
|
|
194
|
+
*/
|
|
195
|
+
private getWsOriginPolicy;
|
|
196
|
+
/**
|
|
197
|
+
* B4 — Validation d'`Origin` au handshake WebSocket (anti-CSWSH, OWASP
|
|
198
|
+
* WSTG-CLNT-10). Les navigateurs n'appliquent PAS CORS aux WebSockets : sans
|
|
199
|
+
* ce contrôle, une page tierce peut ouvrir un WS **authentifié par le cookie de
|
|
200
|
+
* session de la victime** (reprise L1). On exige donc, par défaut, que
|
|
201
|
+
* l'`Origin` du handshake corresponde au `Host` servi (same-origin), avec
|
|
202
|
+
* tolérance loopback en development (Studio Vite cross-port) et allowlist
|
|
203
|
+
* explicite pour les SPA cross-origin (`allowedOrigins`).
|
|
204
|
+
*
|
|
205
|
+
* Une requête SANS `Origin` (client non-navigateur) est acceptée : un attaquant
|
|
206
|
+
* non-navigateur n'a pas besoin de CSWSH, il se connecte directement.
|
|
207
|
+
*
|
|
208
|
+
* @param context - contexte WebSocket au handshake (avant `connect()`).
|
|
209
|
+
* @throws {HttpError} code WS 1008 (Policy Violation) si l'Origin est refusée.
|
|
210
|
+
*/
|
|
211
|
+
checkWebsocketOrigin(context: WebsocketContext): void;
|
|
212
|
+
/**
|
|
213
|
+
* Reads the **kernel-level** syslog config (`kernel.options.log`) and swaps
|
|
214
|
+
* the request logger accordingly. The decision belongs to syslog (not to
|
|
215
|
+
* the http module) because EVERY request log flows through syslog, and
|
|
216
|
+
* the format choice is an operator concern shared across all transports.
|
|
217
|
+
*
|
|
218
|
+
* Config lue : `kernel.options.log.requestFormat`
|
|
219
|
+
* "auto" → dev=pretty, production=json, autre=default (DEFAULT)
|
|
220
|
+
* "default" → DefaultRequestLogger (legacy verbeux)
|
|
221
|
+
* "pretty" → PrettyRequestLogger (1 ligne colorée, P3.2)
|
|
222
|
+
* "json" → JsonAuditLogger (PDU canonique, P3.1)
|
|
223
|
+
*
|
|
224
|
+
* Programmatic override via `setRequestLogger(custom)` reste possible et
|
|
225
|
+
* gagne toujours sur la config (idempotent — last setter wins).
|
|
226
|
+
*/
|
|
227
|
+
private applyRequestLoggerFromConfig;
|
|
228
|
+
handle(request: httpRequest, response: httpResponse | null, type: ServerType): Promise<HttpContext>;
|
|
229
|
+
/**
|
|
230
|
+
* Arme le contexte pour l'exécution : zone firewall (`context.secure`) et
|
|
231
|
+
* résolveur de route — **sans instancier le controller**.
|
|
232
|
+
*
|
|
233
|
+
* Séparé de {@link handleFrontController} parce que les deux gestes n'ont pas
|
|
234
|
+
* le même moment légitime. Le MATCH doit être précoce : il pose
|
|
235
|
+
* `context.sessionIntent` (depuis `@UseSession`) et `resolver.bypassFirewall`,
|
|
236
|
+
* que le point session unique et le firewall lisent juste après. L'INSTANCE,
|
|
237
|
+
* elle, exécute du code utilisateur (`initialize()`) et résout des dépendances
|
|
238
|
+
* DI : elle n'a rien à faire avant que la requête soit autorisée.
|
|
239
|
+
*
|
|
240
|
+
* @param checkFirewall - calcule `context.secure` (zone protégée) ; laissé à
|
|
241
|
+
* `false` par un appelant qui l'a déjà tranché.
|
|
242
|
+
* @throws HttpError 404 quand aucune route ne matche, ou l'exception portée
|
|
243
|
+
* par le résolveur (405…) — inchangé, et toujours avant le firewall : une
|
|
244
|
+
* route inexistante ne devient pas un 401.
|
|
245
|
+
*/
|
|
246
|
+
prepareFrontController(context: ContextType, checkFirewall?: boolean): Promise<Resolver>;
|
|
247
|
+
/**
|
|
248
|
+
* {@link prepareFrontController} + instanciation immédiate du controller
|
|
249
|
+
* (DI + hook `initialize()`).
|
|
250
|
+
*
|
|
251
|
+
* @remarks Réservé au **WebSocket** : le controller doit exister avant
|
|
252
|
+
* `context.connect()`, puisqu'il porte le protocole négocié et peut encore
|
|
253
|
+
* toucher la réponse du handshake (cookies, en-têtes) — après l'accept, il
|
|
254
|
+
* est trop tard. Le HTTP, lui, arme la route ici et laisse
|
|
255
|
+
* `Resolver.executeAction` créer l'instance une fois la requête autorisée.
|
|
256
|
+
*/
|
|
257
|
+
handleFrontController(context: ContextType, checkFirewall?: boolean): Promise<Controller>;
|
|
258
|
+
/**
|
|
259
|
+
* Handles errors by setting appropriate response codes and messages.
|
|
260
|
+
*
|
|
261
|
+
* @param error - The error object, which can be of type Error, HttpError, or nodefonyError.
|
|
262
|
+
* @param context - Optional context parameter, which can be of type ContextType.
|
|
263
|
+
* @param extraHeaders - Optional additional headers to be included in the response.
|
|
264
|
+
* @returns A promise that resolves to either HttpContext or WebsocketContext.
|
|
265
|
+
*
|
|
266
|
+
* WebSocket error codes and their meanings:
|
|
267
|
+
* - code >= 1000 && code <= 2999:
|
|
268
|
+
* - 1000: Normal connection closure
|
|
269
|
+
* - 1001: Remote peer is going away
|
|
270
|
+
* - 1002: Protocol error
|
|
271
|
+
* - 1003: Unprocessable input
|
|
272
|
+
* - 1004: Reserved
|
|
273
|
+
* - 1005: Reason not provided
|
|
274
|
+
* - 1006: Abnormal closure, no further detail available
|
|
275
|
+
* - 1007: Invalid data received
|
|
276
|
+
* - 1008: Policy violation
|
|
277
|
+
* - 1009: Message too big
|
|
278
|
+
* - 1010: Extension requested by client is required
|
|
279
|
+
* - 1011: Internal Server Error
|
|
280
|
+
* - 1015: TLS Handshake Failed
|
|
281
|
+
* - code >= 3000 && code <= 3999:
|
|
282
|
+
* Reserved for use by libraries, frameworks, and applications.
|
|
283
|
+
* Should be registered with IANA. Interpretation of these codes is
|
|
284
|
+
* undefined by the WebSocket protocol.
|
|
285
|
+
* - code >= 4000 && code <= 4999:
|
|
286
|
+
* Reserved for private use. Interpretation of these codes is
|
|
287
|
+
* undefined by the WebSocket protocol.
|
|
288
|
+
*/
|
|
289
|
+
/**
|
|
290
|
+
* Override the default error renderer — e.g. to hide stack traces in prod
|
|
291
|
+
* or emit RFC 7807 problem+json. Stateless singleton expected.
|
|
292
|
+
*/
|
|
293
|
+
setErrorRenderer(renderer: IErrorRenderer): void;
|
|
294
|
+
getErrorRenderer(): IErrorRenderer;
|
|
295
|
+
/**
|
|
296
|
+
* Override the default per-request logger — JSON access log, NCSA combined,
|
|
297
|
+
* pretty single-line formatter, etc. Stateless singleton expected.
|
|
298
|
+
*/
|
|
299
|
+
setRequestLogger(logger: IRequestLogger): void;
|
|
300
|
+
getRequestLogger(): IRequestLogger;
|
|
301
|
+
onError(error: Error | HttpError | nodefonyError, context?: ContextType, _extraHeaders?: Record<string, unknown> | object): Promise<HttpContext | WebsocketContext>;
|
|
302
|
+
compileAlias(): RegExp[];
|
|
303
|
+
onHttpRequest(request: httpRequest, response: httpResponse, type: ServerType): Promise<unknown>;
|
|
304
|
+
initServers(): Promise<(httpServer | httpsServer | websocketServer | websocketSecureServer)[]>;
|
|
305
|
+
/**
|
|
306
|
+
* Publie les ports EFFECTIFS du runtime (state file), une fois l'écoute établie.
|
|
307
|
+
*
|
|
308
|
+
* C'est le canal qui rend `servers.portPolicy: "auto"` utilisable : `nodefony
|
|
309
|
+
* status`, `nodefony stop` et la readiness `--detach` sondaient `[5151, 5152]`
|
|
310
|
+
* **en dur**. Dès qu'un port glisse, cette convention ment — ils sonderaient un
|
|
311
|
+
* port que personne n'écoute et conclueraient « serveur down » sur un serveur
|
|
312
|
+
* parfaitement vivant. Ici, la topologie réelle est écrite noir sur blanc.
|
|
313
|
+
*
|
|
314
|
+
* Le port DÉSIRÉ est publié à côté du port obtenu : un outil peut ainsi dire
|
|
315
|
+
* « tu voulais 5151, tu écoutes sur 5153 » sans deviner.
|
|
316
|
+
*
|
|
317
|
+
* **Publié dans TOUS les environnements, production comprise.** Le glissement de
|
|
318
|
+
* port (`auto`) n'est pas la seule façon de sortir de la convention : une app qui
|
|
319
|
+
* déclare son port (PaaS `PORT`, ingress, `servers.http.port`) écoute ailleurs
|
|
320
|
+
* que `[5151, 5152]` **en `strict` aussi**. Sans ce canal, `production --detach
|
|
321
|
+
* --wait` sonderait la convention, ne verrait rien, et group-killerait au bout de
|
|
322
|
+
* son plafond un serveur qui écoutait parfaitement (faux négatif — même famille
|
|
323
|
+
* que « une liste de ports sondés est une CONVENTION, pas la topologie »).
|
|
324
|
+
* L'écriture est best-effort : un `node_modules` en lecture seule (image
|
|
325
|
+
* conteneur) ne fait jamais tomber un serveur qui, lui, écoute très bien.
|
|
326
|
+
*
|
|
327
|
+
* En **cluster**, le pid publié est celui du MASTER, pas du worker qui écrit :
|
|
328
|
+
* les workers sont recyclés (respawn), et un pid mort fait purger le canal par
|
|
329
|
+
* {@link readRuntimeState} — `status`/`stop` redeviendraient aveugles alors que
|
|
330
|
+
* l'app écoute toujours. Le master, lui, est le propriétaire du runtime.
|
|
331
|
+
*/
|
|
332
|
+
private publishRuntimePorts;
|
|
333
|
+
/**
|
|
334
|
+
* Point d'activation UNIQUE de session — symétrique HTTP **et** WS. Ouvre une
|
|
335
|
+
* session si, et seulement si, la route la déclare (`context.sessionIntent`,
|
|
336
|
+
* posé par le Resolver depuis `@UseSession` / un paramètre `@Session`) **ou**
|
|
337
|
+
* si un cookie de session entrant existe déjà (reprise — L1). Sinon : aucune
|
|
338
|
+
* session (lazy). Remplace l'ancien `sessionAutoStart` global « démarre sur
|
|
339
|
+
* toutes les routes » (le moteur du ×23).
|
|
340
|
+
*
|
|
341
|
+
* @param context - contexte HTTP/HTTP2/WS courant.
|
|
342
|
+
* @returns la session active, ou `null` si la requête n'en requiert aucune.
|
|
343
|
+
*/
|
|
344
|
+
startSession(context: WebsocketContext | HttpContext): Promise<Session | null>;
|
|
345
|
+
/**
|
|
346
|
+
* Teardown post-réponse — fire-and-forget depuis le `once("close")` posé par
|
|
347
|
+
* `createHttpContext` (un seul fire possible : once auto-détaché). Loggue la
|
|
348
|
+
* requête, draine les hooks afterResponse/onFinish, libère le scope DI.
|
|
349
|
+
*/
|
|
350
|
+
private teardownHttp;
|
|
351
|
+
createHttpContext(scope: Scope, request: httpRequest, response: httpResponse, type: ServerType): HttpContext;
|
|
352
|
+
handleHttp(scope: Scope, request: httpRequest, response: httpResponse, type: ServerType): Promise<HttpContext>;
|
|
353
|
+
onRequestEnd(context: HttpContext, error?: Error | null | undefined): Promise<HttpContext | number>;
|
|
354
|
+
createWebsocketContext(scope: Scope, req: IncomingMessage, ws: Ws, type: ServerType): WebsocketContext;
|
|
355
|
+
onWebsocketRequest(ws: Ws, req: IncomingMessage, type: ServerType): Promise<unknown>;
|
|
356
|
+
handleWebsocket(scope: Scope, ws: Ws, req: IncomingMessage, type: ServerType): Promise<unknown>;
|
|
357
|
+
private releaseOrphanWsScope;
|
|
358
|
+
onConnect(context: WebsocketContext, error?: null | undefined | unknown): Promise<Ws | number>;
|
|
359
|
+
checkValidDomain(context: ContextType): number;
|
|
360
|
+
isValidDomain(context: ContextType): boolean;
|
|
361
|
+
/**
|
|
362
|
+
* Ce nom d'hôte franchit-il la barrière `trustedHosts` ? Même liste compilée
|
|
363
|
+
* que `isValidDomain`, mais interrogeable **sans contexte** — un module qui
|
|
364
|
+
* doit décider si un `Host` reçu est légitime (ex. `@nodefony/frontend`, qui
|
|
365
|
+
* en dérive l'origine des assets Vite) appelle CETTE méthode plutôt que de
|
|
366
|
+
* recompiler les motifs de son côté : deux copies divergeraient en silence.
|
|
367
|
+
*
|
|
368
|
+
* Résolution par NOM (`container.get("HttpKernel")`) chez l'appelant — aucun
|
|
369
|
+
* import de `@nodefony/http`, donc aucun cycle.
|
|
370
|
+
*
|
|
371
|
+
* @param hostname - nom d'hôte SANS port (`Context.domain`).
|
|
372
|
+
* @returns `true` si un motif de `trustedHosts` (ou le domaine canonique)
|
|
373
|
+
* couvre ce nom.
|
|
374
|
+
*/
|
|
375
|
+
isTrustedHostname(hostname: string): boolean;
|
|
376
|
+
}
|
|
377
|
+
export default HttpKernel;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { IRequestLogger, IRequestLogEntry } from "../interfaces/IRequestLogger.js";
|
|
2
|
+
import type { IHttpContext, IWebsocketContext } from "../interfaces/IContext.js";
|
|
3
|
+
/**
|
|
4
|
+
* Pretty single-line logger for dev (P3.2) — the biggest gain for humans.
|
|
5
|
+
*
|
|
6
|
+
* Format (no padding, ANSI-colored):
|
|
7
|
+
* `GET 200 /api/test 12.3ms 127.0.0.1 [a1b2c3d4]`
|
|
8
|
+
*
|
|
9
|
+
* - method : cyan
|
|
10
|
+
* - status : green (2xx) / yellow (3xx) / yellow-bold (4xx) / red (5xx)
|
|
11
|
+
* - url : default
|
|
12
|
+
* - duration : dim
|
|
13
|
+
* - remote : dim
|
|
14
|
+
* - requestId : magenta-dim, truncated to first 8 chars
|
|
15
|
+
*
|
|
16
|
+
* Activate with `httpKernel.setRequestLogger(new PrettyRequestLogger())`.
|
|
17
|
+
*
|
|
18
|
+
* Stateless singleton. Allocates a few strings per request (terminal log
|
|
19
|
+
* path) — zero overhead when not activated.
|
|
20
|
+
*/
|
|
21
|
+
declare class PrettyRequestLogger implements IRequestLogger {
|
|
22
|
+
renderHttp(context: IHttpContext, error?: Error | null): IRequestLogEntry;
|
|
23
|
+
renderWebsocket(context: IWebsocketContext, error?: Error | null, acceptedProtocol?: string | null): IRequestLogEntry;
|
|
24
|
+
}
|
|
25
|
+
export default PrettyRequestLogger;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { IRequestLogger, IRequestLogEntry } from "../interfaces/IRequestLogger.js";
|
|
2
|
+
import type { IHttpContext, IWebsocketContext } from "../interfaces/IContext.js";
|
|
3
|
+
/**
|
|
4
|
+
* Default Nodefony per-request logger — preserves the legacy colored format.
|
|
5
|
+
*
|
|
6
|
+
* HTTP success: `URL : <url> FROM : <remote> ORIGIN : <host> ID : <uuid>`
|
|
7
|
+
* HTTP error: adds the error toString (in dev), single-line in prod
|
|
8
|
+
* WebSocket: adds `Accept-Protocol : <proto>` on success
|
|
9
|
+
*
|
|
10
|
+
* Stateless singleton — zero per-request allocation. Override via
|
|
11
|
+
* `httpKernel.setRequestLogger(custom)` for JSON access logs (P3.1),
|
|
12
|
+
* pretty formatter (P3.2), or NCSA combined format (P3.10).
|
|
13
|
+
*/
|
|
14
|
+
declare class DefaultRequestLogger implements IRequestLogger {
|
|
15
|
+
renderHttp(context: IHttpContext, error?: Error | null): IRequestLogEntry;
|
|
16
|
+
renderWebsocket(context: IWebsocketContext, error?: Error | null, acceptedProtocol?: string | null): IRequestLogEntry;
|
|
17
|
+
}
|
|
18
|
+
export default DefaultRequestLogger;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Duplex } from "node:stream";
|
|
2
|
+
/**
|
|
3
|
+
* Gère l'event `clientError` d'un `http.Server` / `https.Server`.
|
|
4
|
+
*
|
|
5
|
+
* ⚠️ Doc Node (`http.Server` event `'clientError'`) : dès qu'un listener
|
|
6
|
+
* `clientError` est attaché, Node **cesse de fermer le socket automatiquement**
|
|
7
|
+
* (le défaut aurait répondu `400` + détruit le socket). Sans fermeture
|
|
8
|
+
* explicite, un socket malformé reste ouvert → fuite de socket/FD = DoS sur
|
|
9
|
+
* requête mal formée. On répond donc une réponse minimale et on ferme.
|
|
10
|
+
*
|
|
11
|
+
* @param error - erreur émise (codes `llhttp` : `HPE_*`, ou `ECONNRESET`…).
|
|
12
|
+
* @param socket - flux client brut (l'event `clientError` fournit un `Duplex`).
|
|
13
|
+
*/
|
|
14
|
+
export declare function handleClientError(error: NodeJS.ErrnoException, socket: Duplex): void;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Service, Module, FamilyType } from "nodefony";
|
|
2
|
+
import HttpKernel, { ProtocolType, ServerType, SchemeType } from "../http-kernel.js";
|
|
3
|
+
import http from "node:http";
|
|
4
|
+
import http2 from "node:http2";
|
|
5
|
+
import { AddressInfo } from "node:net";
|
|
6
|
+
import { HttpTerminator } from "./serverShutdown.js";
|
|
7
|
+
declare class ServerHttp extends Service {
|
|
8
|
+
private httpKernel;
|
|
9
|
+
module: Module;
|
|
10
|
+
server: http.Server | http2.Http2Server | null;
|
|
11
|
+
httpTerminator: HttpTerminator | null;
|
|
12
|
+
port: number;
|
|
13
|
+
protocol: ProtocolType;
|
|
14
|
+
ready: boolean;
|
|
15
|
+
type: ServerType;
|
|
16
|
+
domain: string;
|
|
17
|
+
scheme: SchemeType;
|
|
18
|
+
address: string | null;
|
|
19
|
+
family: FamilyType | null;
|
|
20
|
+
active: boolean;
|
|
21
|
+
infos: AddressInfo | null;
|
|
22
|
+
constructor(module: Module, httpKernel: HttpKernel);
|
|
23
|
+
setPort(): number;
|
|
24
|
+
createServer(): Promise<http.Server | http2.Http2Server>;
|
|
25
|
+
/**
|
|
26
|
+
* Handler d'erreur DURABLE — posé une fois le serveur en écoute.
|
|
27
|
+
*
|
|
28
|
+
* Il ne traite donc plus `EADDRINUSE` au bind (le binder s'en charge, avec ou
|
|
29
|
+
* sans repli) : il couvre les erreurs de la VIE du serveur.
|
|
30
|
+
*/
|
|
31
|
+
private attachErrorHandler;
|
|
32
|
+
/**
|
|
33
|
+
* Le bind a échoué pour de bon — soit `portPolicy: "strict"`, soit tous les
|
|
34
|
+
* ports de repli étaient pris, soit une erreur qui n'est pas un conflit de port.
|
|
35
|
+
*
|
|
36
|
+
* Reste FATAL (contrat inchangé) : un serveur qui n'écoute pas ne doit jamais
|
|
37
|
+
* laisser le process traîner en se croyant démarré.
|
|
38
|
+
*/
|
|
39
|
+
private reportBindError;
|
|
40
|
+
showBanner(): void;
|
|
41
|
+
}
|
|
42
|
+
export default ServerHttp;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Service, Module, FamilyType } from "nodefony";
|
|
2
|
+
import HttpKernel, { ProtocolType, ServerType, SchemeType } from "../http-kernel.js";
|
|
3
|
+
import https from "node:https";
|
|
4
|
+
import http2 from "node:http2";
|
|
5
|
+
import { HttpTerminator } from "./serverShutdown.js";
|
|
6
|
+
import { AddressInfo } from "node:net";
|
|
7
|
+
declare class ServerHttps extends Service {
|
|
8
|
+
private httpKernel;
|
|
9
|
+
httpTerminator: HttpTerminator | null;
|
|
10
|
+
module: Module;
|
|
11
|
+
server: https.Server | http2.Http2SecureServer | null;
|
|
12
|
+
port: number;
|
|
13
|
+
protocol: ProtocolType;
|
|
14
|
+
ready: boolean;
|
|
15
|
+
type: ServerType;
|
|
16
|
+
domain: string;
|
|
17
|
+
scheme: SchemeType;
|
|
18
|
+
address: string | null;
|
|
19
|
+
family: FamilyType | null;
|
|
20
|
+
active: boolean;
|
|
21
|
+
infos: AddressInfo | null;
|
|
22
|
+
constructor(module: Module, httpKernel: HttpKernel);
|
|
23
|
+
terminator(): HttpTerminator;
|
|
24
|
+
setPort(): number;
|
|
25
|
+
createServer(): Promise<https.Server | http2.Http2SecureServer>;
|
|
26
|
+
createServerH2(): Promise<http2.Http2SecureServer>;
|
|
27
|
+
/**
|
|
28
|
+
* Écoute selon `servers.portPolicy`, commune aux deux branches TLS (HTTP/1.1 et
|
|
29
|
+
* HTTP/2) — leur seule différence est le type du serveur, pas la politique.
|
|
30
|
+
*
|
|
31
|
+
* Pose le handler d'erreur durable une fois en écoute (donc APRÈS les éventuels
|
|
32
|
+
* `EADDRINUSE` de repli, qui ne sont pas des pannes).
|
|
33
|
+
*/
|
|
34
|
+
private listenWithPolicy;
|
|
35
|
+
/** Erreurs de la VIE du serveur (le bind est déjà passé — cf server-http.ts). */
|
|
36
|
+
private attachErrorHandler;
|
|
37
|
+
/** Bind définitivement impossible → FATAL (contrat inchangé). */
|
|
38
|
+
private reportBindError;
|
|
39
|
+
showBanner(): void;
|
|
40
|
+
}
|
|
41
|
+
export default ServerHttps;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import serveStatic from "serve-static";
|
|
2
|
+
import http from "node:http";
|
|
3
|
+
import http2 from "node:http2";
|
|
4
|
+
import { Service, Module } from "nodefony";
|
|
5
|
+
type serveStaticType = serveStatic.RequestHandler<http.ServerResponse>;
|
|
6
|
+
type ServersStatic = Record<string, serveStaticType>;
|
|
7
|
+
/** Un dossier monté sous un préfixe public (`/_assets/x/` → dir). */
|
|
8
|
+
type StaticMount = {
|
|
9
|
+
prefix: string;
|
|
10
|
+
server: serveStaticType;
|
|
11
|
+
dir: string;
|
|
12
|
+
};
|
|
13
|
+
declare class Statics extends Service {
|
|
14
|
+
module: Module;
|
|
15
|
+
servers: ServersStatic;
|
|
16
|
+
/** Montages préfixés (prod frontend). Lazy : `[]` rempli à `addMount`. */
|
|
17
|
+
mounts: StaticMount[];
|
|
18
|
+
/**
|
|
19
|
+
* Serveur statique config-driven actif. `false` (config `statics.enabled`) =
|
|
20
|
+
* aucun montage `web`/`assets`, 0 listener — quand un reverse-proxy/CDN sert
|
|
21
|
+
* les statiques. Ne gate PAS les `addMount()` programmatiques (frontend prod).
|
|
22
|
+
*/
|
|
23
|
+
enabled: boolean;
|
|
24
|
+
defaultOptions: serveStatic.ServeStaticOptions;
|
|
25
|
+
constructor(module: Module);
|
|
26
|
+
/**
|
|
27
|
+
* Auto-monte le `public/` de chaque module applicatif sous le préfixe natif
|
|
28
|
+
* `/<module>/` (basename du nom — `@nodefony/test` → `/test/`). Appelé une fois
|
|
29
|
+
* à `onReady`. Idempotent via {@link addMount} (un même préfixe est remplacé).
|
|
30
|
+
*
|
|
31
|
+
* Exclusions :
|
|
32
|
+
* - **app root** (`isApp`) — son `public/` est servi à la racine `/` par la
|
|
33
|
+
* racine statique `web` (favicon…), sans préfixe.
|
|
34
|
+
* - **modules frontend-managed** (présents dans `frontend.listEntries()`) —
|
|
35
|
+
* leurs assets buildés sont servis sous `/_assets/<name>/` par
|
|
36
|
+
* `@nodefony/frontend` ; on ne double-sert pas leur `public/dist`.
|
|
37
|
+
* - **modules sans `public/`** — rien à monter (http, framework, security…).
|
|
38
|
+
*
|
|
39
|
+
* Enregistré dans {@link mounts} quel que soit `enabled` → la carte
|
|
40
|
+
* préfixe→dossier reste lisible par `proxy:generate` même statiques désactivés
|
|
41
|
+
* (reverse-proxy/CDN en prod).
|
|
42
|
+
*/
|
|
43
|
+
mountModulePublics(): void;
|
|
44
|
+
initStaticFiles(): void;
|
|
45
|
+
/**
|
|
46
|
+
* Monte un dossier sous un préfixe public (ex `/_assets/studio/` → outDir).
|
|
47
|
+
* Le préfixe est normalisé (leading + trailing `/`). Idempotent : un même
|
|
48
|
+
* préfixe est remplacé. Consommé par `@nodefony/frontend` en prod (assets
|
|
49
|
+
* Vite buildés) — résolu par nom via le Container, sans import croisé.
|
|
50
|
+
*
|
|
51
|
+
* @param prefix préfixe d'URL public
|
|
52
|
+
* @param dir dossier absolu à servir
|
|
53
|
+
*/
|
|
54
|
+
addMount(prefix: string, dir: string): void;
|
|
55
|
+
/** `true` si au moins un montage préfixé est actif (gate du pipeline). */
|
|
56
|
+
hasMounts(): boolean;
|
|
57
|
+
addDirectory(Path: string, options: serveStatic.ServeStaticOptions): serveStatic.RequestHandler<http.ServerResponse<http.IncomingMessage>>;
|
|
58
|
+
getStatic(server: serveStaticType, request: http.IncomingMessage | http2.Http2ServerRequest, response: http.ServerResponse | http2.Http2ServerResponse): Promise<http.ServerResponse | http2.Http2ServerResponse>;
|
|
59
|
+
getUrl(request: http.IncomingMessage | http2.Http2ServerRequest): string;
|
|
60
|
+
handle(request: http.IncomingMessage | http2.Http2ServerRequest, response: http.ServerResponse | http2.Http2ServerResponse): Promise<http.ServerResponse | http2.Http2ServerResponse>;
|
|
61
|
+
}
|
|
62
|
+
export default Statics;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import Ws, { WebSocketServer } from "ws";
|
|
2
|
+
import { Service, Module, FamilyType } from "nodefony";
|
|
3
|
+
import HttpKernel, { ProtocolType, ServerType, SchemeType } from "../http-kernel.js";
|
|
4
|
+
import { AddressInfo } from "node:net";
|
|
5
|
+
import type { IncomingMessage } from "node:http";
|
|
6
|
+
import httpsServers from "./server-https.js";
|
|
7
|
+
declare class WebsocketSecure extends Service {
|
|
8
|
+
private httpKernel;
|
|
9
|
+
module: Module;
|
|
10
|
+
ready: boolean;
|
|
11
|
+
server: WebSocketServer | null;
|
|
12
|
+
port: number;
|
|
13
|
+
domain: string;
|
|
14
|
+
protocol: ProtocolType;
|
|
15
|
+
family: FamilyType | null;
|
|
16
|
+
scheme: SchemeType;
|
|
17
|
+
address: string | null;
|
|
18
|
+
type: ServerType;
|
|
19
|
+
infos: AddressInfo | null;
|
|
20
|
+
/** Timer keep-alive (UN par serveur). `null` si désactivé ou pas démarré. */
|
|
21
|
+
heartbeatTimer: ReturnType<typeof setInterval> | null;
|
|
22
|
+
constructor(module: Module, httpKernel: HttpKernel);
|
|
23
|
+
setPort(): number;
|
|
24
|
+
createServer(serverHttps: httpsServers): Promise<WebSocketServer>;
|
|
25
|
+
onConnection(ws: Ws, req: IncomingMessage): void;
|
|
26
|
+
terminate(): Promise<boolean>;
|
|
27
|
+
showBanner(): void;
|
|
28
|
+
}
|
|
29
|
+
export default WebsocketSecure;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import Ws, { WebSocketServer } from "ws";
|
|
2
|
+
import { Service, Module, FamilyType } from "nodefony";
|
|
3
|
+
import HttpKernel, { ProtocolType, ServerType, SchemeType } from "../http-kernel.js";
|
|
4
|
+
import { AddressInfo } from "node:net";
|
|
5
|
+
import type { IncomingMessage } from "node:http";
|
|
6
|
+
import httpServer from "./server-http.js";
|
|
7
|
+
declare class Websocket extends Service {
|
|
8
|
+
private httpKernel;
|
|
9
|
+
module: Module;
|
|
10
|
+
ready: boolean;
|
|
11
|
+
server: WebSocketServer | null;
|
|
12
|
+
port: number;
|
|
13
|
+
domain: string;
|
|
14
|
+
protocol: ProtocolType;
|
|
15
|
+
family: FamilyType | null;
|
|
16
|
+
scheme: SchemeType;
|
|
17
|
+
address: string | null;
|
|
18
|
+
type: ServerType;
|
|
19
|
+
infos: AddressInfo | null;
|
|
20
|
+
/** Timer keep-alive (UN par serveur). `null` si désactivé ou pas démarré. */
|
|
21
|
+
heartbeatTimer: ReturnType<typeof setInterval> | null;
|
|
22
|
+
constructor(module: Module, httpKernel: HttpKernel);
|
|
23
|
+
setPort(): number;
|
|
24
|
+
createServer(serverHttp: httpServer): Promise<WebSocketServer>;
|
|
25
|
+
onConnection(ws: Ws, req: IncomingMessage): void;
|
|
26
|
+
terminate(): Promise<boolean>;
|
|
27
|
+
showBanner(): void;
|
|
28
|
+
}
|
|
29
|
+
export default Websocket;
|