xypriss 1.1.4 → 1.2.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/dist/cjs/src/index.js +56 -0
- package/dist/cjs/src/index.js.map +1 -1
- package/dist/cjs/src/plugins/modules/PluginRegistry.js +6 -38
- package/dist/cjs/src/plugins/modules/PluginRegistry.js.map +1 -1
- package/dist/cjs/src/plugins/modules/builtin/JWTAuthPlugin.js +1 -1
- package/dist/cjs/src/plugins/modules/builtin/JWTAuthPlugin.js.map +1 -1
- package/dist/cjs/src/plugins/modules/builtin/SmartCachePlugin.js +1 -1
- package/dist/cjs/src/plugins/modules/builtin/SmartCachePlugin.js.map +1 -1
- package/dist/cjs/src/plugins/modules/core/CachePlugin.js +4 -4
- package/dist/cjs/src/plugins/modules/core/CachePlugin.js.map +1 -1
- package/dist/cjs/src/plugins/modules/core/PerformancePlugin.js +2 -2
- package/dist/cjs/src/plugins/modules/core/PerformancePlugin.js.map +1 -1
- package/dist/cjs/src/plugins/modules/core/SecurityPlugin.js +2 -2
- package/dist/cjs/src/plugins/modules/core/SecurityPlugin.js.map +1 -1
- package/dist/cjs/src/plugins/modules/index.js +304 -0
- package/dist/cjs/src/plugins/modules/index.js.map +1 -0
- package/dist/cjs/src/plugins/modules/network/builtin/CompressionPlugin.js +410 -0
- package/dist/cjs/src/plugins/modules/network/builtin/CompressionPlugin.js.map +1 -0
- package/dist/cjs/src/plugins/modules/network/builtin/ConnectionPlugin.js +797 -0
- package/dist/cjs/src/plugins/modules/network/builtin/ConnectionPlugin.js.map +1 -0
- package/dist/cjs/src/plugins/modules/network/builtin/ProxyPlugin.js +409 -0
- package/dist/cjs/src/plugins/modules/network/builtin/ProxyPlugin.js.map +1 -0
- package/dist/cjs/src/plugins/modules/network/builtin/RateLimitPlugin.js +606 -0
- package/dist/cjs/src/plugins/modules/network/builtin/RateLimitPlugin.js.map +1 -0
- package/dist/cjs/src/plugins/modules/network/core/NetworkPlugin.js +225 -0
- package/dist/cjs/src/plugins/modules/network/core/NetworkPlugin.js.map +1 -0
- package/dist/cjs/src/plugins/modules/network/core/NetworkPluginFactory.js +40 -0
- package/dist/cjs/src/plugins/modules/network/core/NetworkPluginFactory.js.map +1 -0
- package/dist/cjs/src/plugins/modules/network/index.js +120 -0
- package/dist/cjs/src/plugins/modules/network/index.js.map +1 -0
- package/dist/cjs/src/plugins/modules/network/types/NetworkTypes.js +24 -0
- package/dist/cjs/src/plugins/modules/network/types/NetworkTypes.js.map +1 -0
- package/dist/cjs/src/plugins/modules/network/utils/NetworkPluginUtils.js +63 -0
- package/dist/cjs/src/plugins/modules/network/utils/NetworkPluginUtils.js.map +1 -0
- package/dist/cjs/src/plugins/modules/types/PluginTypes.js +1 -0
- package/dist/cjs/src/plugins/modules/types/PluginTypes.js.map +1 -1
- package/dist/cjs/src/server/FastServer.js +111 -2
- package/dist/cjs/src/server/FastServer.js.map +1 -1
- package/dist/cjs/src/server/conf/networkConnectionConf.js +25 -0
- package/dist/cjs/src/server/conf/networkConnectionConf.js.map +1 -0
- package/dist/cjs/src/server/conf/proxyConfig.js +23 -0
- package/dist/cjs/src/server/conf/proxyConfig.js.map +1 -0
- package/dist/cjs/src/server/conf/rateLimitConfig.js +35 -0
- package/dist/cjs/src/server/conf/rateLimitConfig.js.map +1 -0
- package/dist/cjs/src/server/const/default.js +6 -0
- package/dist/cjs/src/server/const/default.js.map +1 -1
- package/dist/cjs/src/server/handlers/NotFoundHandler.js +217 -0
- package/dist/cjs/src/server/handlers/NotFoundHandler.js.map +1 -0
- package/dist/cjs/src/server/handlers/templates/notFoundTemp.js +163 -0
- package/dist/cjs/src/server/handlers/templates/notFoundTemp.js.map +1 -0
- package/dist/esm/mods/security/src/components/cache/UFSIMC.js +5 -5
- package/dist/esm/mods/security/src/components/cache/UFSIMC.js.map +1 -1
- package/dist/esm/mods/security/src/components/cache/cacheSys.utils.js +3 -3
- package/dist/esm/mods/security/src/components/cache/cacheSys.utils.js.map +1 -1
- package/dist/esm/src/index.js +18 -0
- package/dist/esm/src/index.js.map +1 -1
- package/dist/esm/src/plugins/modules/PluginRegistry.js +6 -38
- package/dist/esm/src/plugins/modules/PluginRegistry.js.map +1 -1
- package/dist/esm/src/plugins/modules/builtin/JWTAuthPlugin.js +1 -1
- package/dist/esm/src/plugins/modules/builtin/JWTAuthPlugin.js.map +1 -1
- package/dist/esm/src/plugins/modules/builtin/SmartCachePlugin.js +1 -1
- package/dist/esm/src/plugins/modules/builtin/SmartCachePlugin.js.map +1 -1
- package/dist/esm/src/plugins/modules/core/CachePlugin.js +4 -4
- package/dist/esm/src/plugins/modules/core/CachePlugin.js.map +1 -1
- package/dist/esm/src/plugins/modules/core/PerformancePlugin.js +2 -2
- package/dist/esm/src/plugins/modules/core/PerformancePlugin.js.map +1 -1
- package/dist/esm/src/plugins/modules/core/SecurityPlugin.js +2 -2
- package/dist/esm/src/plugins/modules/core/SecurityPlugin.js.map +1 -1
- package/dist/esm/src/plugins/modules/index.js +275 -0
- package/dist/esm/src/plugins/modules/index.js.map +1 -0
- package/dist/esm/src/plugins/modules/network/builtin/CompressionPlugin.js +389 -0
- package/dist/esm/src/plugins/modules/network/builtin/CompressionPlugin.js.map +1 -0
- package/dist/esm/src/plugins/modules/network/builtin/ConnectionPlugin.js +776 -0
- package/dist/esm/src/plugins/modules/network/builtin/ConnectionPlugin.js.map +1 -0
- package/dist/esm/src/plugins/modules/network/builtin/ProxyPlugin.js +407 -0
- package/dist/esm/src/plugins/modules/network/builtin/ProxyPlugin.js.map +1 -0
- package/dist/esm/src/plugins/modules/network/builtin/RateLimitPlugin.js +585 -0
- package/dist/esm/src/plugins/modules/network/builtin/RateLimitPlugin.js.map +1 -0
- package/dist/esm/src/plugins/modules/network/core/NetworkPlugin.js +223 -0
- package/dist/esm/src/plugins/modules/network/core/NetworkPlugin.js.map +1 -0
- package/dist/esm/src/plugins/modules/network/core/NetworkPluginFactory.js +38 -0
- package/dist/esm/src/plugins/modules/network/core/NetworkPluginFactory.js.map +1 -0
- package/dist/esm/src/plugins/modules/network/index.js +109 -0
- package/dist/esm/src/plugins/modules/network/index.js.map +1 -0
- package/dist/esm/src/plugins/modules/network/types/NetworkTypes.js +24 -0
- package/dist/esm/src/plugins/modules/network/types/NetworkTypes.js.map +1 -0
- package/dist/esm/src/plugins/modules/network/utils/NetworkPluginUtils.js +61 -0
- package/dist/esm/src/plugins/modules/network/utils/NetworkPluginUtils.js.map +1 -0
- package/dist/esm/src/plugins/modules/types/PluginTypes.js +1 -0
- package/dist/esm/src/plugins/modules/types/PluginTypes.js.map +1 -1
- package/dist/esm/src/server/FastServer.js +111 -2
- package/dist/esm/src/server/FastServer.js.map +1 -1
- package/dist/esm/src/server/conf/networkConnectionConf.js +23 -0
- package/dist/esm/src/server/conf/networkConnectionConf.js.map +1 -0
- package/dist/esm/src/server/conf/proxyConfig.js +21 -0
- package/dist/esm/src/server/conf/proxyConfig.js.map +1 -0
- package/dist/esm/src/server/conf/rateLimitConfig.js +33 -0
- package/dist/esm/src/server/conf/rateLimitConfig.js.map +1 -0
- package/dist/esm/src/server/const/default.js +6 -0
- package/dist/esm/src/server/const/default.js.map +1 -1
- package/dist/esm/src/server/handlers/NotFoundHandler.js +194 -0
- package/dist/esm/src/server/handlers/NotFoundHandler.js.map +1 -0
- package/dist/esm/src/server/handlers/templates/notFoundTemp.js +161 -0
- package/dist/esm/src/server/handlers/templates/notFoundTemp.js.map +1 -0
- package/dist/index.d.ts +5085 -14
- package/package.json +8 -7
|
@@ -0,0 +1,797 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var perf_hooks = require('perf_hooks');
|
|
4
|
+
var NetworkPlugin = require('../core/NetworkPlugin.js');
|
|
5
|
+
var fs = require('fs');
|
|
6
|
+
var path = require('path');
|
|
7
|
+
var xyprissSecurity = require('xypriss-security');
|
|
8
|
+
var crypto = require('crypto');
|
|
9
|
+
var NetworkTypes = require('../types/NetworkTypes.js');
|
|
10
|
+
|
|
11
|
+
function _interopNamespaceDefault(e) {
|
|
12
|
+
var n = Object.create(null);
|
|
13
|
+
if (e) {
|
|
14
|
+
Object.keys(e).forEach(function (k) {
|
|
15
|
+
if (k !== 'default') {
|
|
16
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
17
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
18
|
+
enumerable: true,
|
|
19
|
+
get: function () { return e[k]; }
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
n.default = e;
|
|
25
|
+
return Object.freeze(n);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
var crypto__namespace = /*#__PURE__*/_interopNamespaceDefault(crypto);
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Connection Plugin
|
|
32
|
+
*
|
|
33
|
+
* Handles HTTP/2, connection pooling, keep-alive management, and connection optimization
|
|
34
|
+
* Provides advanced connection management features for XyPriss servers
|
|
35
|
+
*/
|
|
36
|
+
// MIME type lookup with fallback for ESM compatibility
|
|
37
|
+
const getMimeType = (path) => {
|
|
38
|
+
const ext = path.split(".").pop()?.toLowerCase();
|
|
39
|
+
const mimeMap = {
|
|
40
|
+
css: "text/css",
|
|
41
|
+
js: "application/javascript",
|
|
42
|
+
json: "application/json",
|
|
43
|
+
html: "text/html",
|
|
44
|
+
htm: "text/html",
|
|
45
|
+
txt: "text/plain",
|
|
46
|
+
xml: "application/xml",
|
|
47
|
+
png: "image/png",
|
|
48
|
+
jpg: "image/jpeg",
|
|
49
|
+
jpeg: "image/jpeg",
|
|
50
|
+
gif: "image/gif",
|
|
51
|
+
svg: "image/svg+xml",
|
|
52
|
+
ico: "image/x-icon",
|
|
53
|
+
woff: "font/woff",
|
|
54
|
+
woff2: "font/woff2",
|
|
55
|
+
ttf: "font/ttf",
|
|
56
|
+
eot: "application/vnd.ms-fontobject",
|
|
57
|
+
};
|
|
58
|
+
return mimeMap[ext || ""] || "application/octet-stream";
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Connection management plugin for optimizing HTTP connections
|
|
62
|
+
*/
|
|
63
|
+
class ConnectionPlugin extends NetworkPlugin.NetworkPlugin {
|
|
64
|
+
constructor(config = {}) {
|
|
65
|
+
super(config);
|
|
66
|
+
this.id = "xypriss.network.connection";
|
|
67
|
+
this.name = "Connection Management Plugin";
|
|
68
|
+
this.version = "1.0.0";
|
|
69
|
+
this.networkCategory = NetworkTypes.NetworkCategory.CONNECTION;
|
|
70
|
+
// Connection-specific state
|
|
71
|
+
this.connectionPool = new Map();
|
|
72
|
+
this.activeConnections = 0;
|
|
73
|
+
this.connectionTimeouts = new Map();
|
|
74
|
+
this.keepAliveStats = {
|
|
75
|
+
totalConnections: 0,
|
|
76
|
+
reuseCount: 0,
|
|
77
|
+
timeoutCount: 0,
|
|
78
|
+
};
|
|
79
|
+
this.http2Stats = {
|
|
80
|
+
maxStreams: 100,
|
|
81
|
+
serverPushEnabled: false,
|
|
82
|
+
configured: false,
|
|
83
|
+
};
|
|
84
|
+
this.maxConnections = config.connectionPool?.maxConnections || 1000;
|
|
85
|
+
// Use a reasonable default for static files directory
|
|
86
|
+
this.staticBaseDir = config.staticBaseDir || process.cwd();
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Initialize connection management
|
|
90
|
+
*/
|
|
91
|
+
async initializeNetwork() {
|
|
92
|
+
// Set up connection pool monitoring
|
|
93
|
+
this.startConnectionMonitoring();
|
|
94
|
+
// Configure HTTP/2 if enabled
|
|
95
|
+
if (this.getConnectionConfig().http2?.enabled) {
|
|
96
|
+
await this.configureHTTP2();
|
|
97
|
+
}
|
|
98
|
+
// Set up keep-alive management
|
|
99
|
+
if (this.getConnectionConfig().keepAlive?.enabled !== false) {
|
|
100
|
+
this.configureKeepAlive();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Execute connection management logic
|
|
105
|
+
*/
|
|
106
|
+
async executeNetwork(context) {
|
|
107
|
+
const startTime = perf_hooks.performance.now();
|
|
108
|
+
try {
|
|
109
|
+
// Get or create connection info
|
|
110
|
+
const connectionKey = this.getConnectionKey(context);
|
|
111
|
+
let connectionInfo = this.connectionPool.get(connectionKey);
|
|
112
|
+
if (!connectionInfo) {
|
|
113
|
+
connectionInfo = await this.createConnection(context);
|
|
114
|
+
this.connectionPool.set(connectionKey, connectionInfo);
|
|
115
|
+
this.activeConnections++;
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
// Update existing connection
|
|
119
|
+
connectionInfo.lastUsed = Date.now();
|
|
120
|
+
connectionInfo.requestCount++;
|
|
121
|
+
this.keepAliveStats.reuseCount++;
|
|
122
|
+
}
|
|
123
|
+
// Apply connection optimizations
|
|
124
|
+
await this.applyConnectionOptimizations(context, connectionInfo);
|
|
125
|
+
// Set up connection cleanup
|
|
126
|
+
this.setupConnectionCleanup(connectionKey, connectionInfo);
|
|
127
|
+
const executionTime = perf_hooks.performance.now() - startTime;
|
|
128
|
+
return {
|
|
129
|
+
success: true,
|
|
130
|
+
executionTime,
|
|
131
|
+
shouldContinue: true,
|
|
132
|
+
data: {
|
|
133
|
+
connectionId: connectionInfo.id,
|
|
134
|
+
isReused: connectionInfo.requestCount > 1,
|
|
135
|
+
protocol: connectionInfo.protocol,
|
|
136
|
+
},
|
|
137
|
+
modifications: {
|
|
138
|
+
headers: this.getConnectionHeaders(connectionInfo),
|
|
139
|
+
},
|
|
140
|
+
networkMetrics: {
|
|
141
|
+
processingTime: executionTime,
|
|
142
|
+
memoryUsage: process.memoryUsage().heapUsed,
|
|
143
|
+
cpuUsage: process.cpuUsage().user,
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
return {
|
|
149
|
+
success: false,
|
|
150
|
+
executionTime: perf_hooks.performance.now() - startTime,
|
|
151
|
+
shouldContinue: true,
|
|
152
|
+
error,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Create new connection info
|
|
158
|
+
*/
|
|
159
|
+
async createConnection(context) {
|
|
160
|
+
const config = this.getConnectionConfig();
|
|
161
|
+
return {
|
|
162
|
+
id: this.generateConnectionId(),
|
|
163
|
+
remoteAddress: context.connection.remoteAddress || "unknown",
|
|
164
|
+
protocol: context.connection.protocol,
|
|
165
|
+
encrypted: context.connection.encrypted,
|
|
166
|
+
created: Date.now(),
|
|
167
|
+
lastUsed: Date.now(),
|
|
168
|
+
requestCount: 1,
|
|
169
|
+
keepAlive: config.keepAlive?.enabled !== false,
|
|
170
|
+
http2: config.http2?.enabled || false,
|
|
171
|
+
maxRequests: config.keepAlive?.maxRequests || 1000,
|
|
172
|
+
timeout: config.keepAlive?.timeout || 30000,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Apply connection optimizations
|
|
177
|
+
*/
|
|
178
|
+
async applyConnectionOptimizations(context, connectionInfo) {
|
|
179
|
+
const { res } = context;
|
|
180
|
+
// Set keep-alive headers
|
|
181
|
+
if (connectionInfo.keepAlive) {
|
|
182
|
+
res.setHeader("Connection", "keep-alive");
|
|
183
|
+
res.setHeader("Keep-Alive", `timeout=${connectionInfo.timeout / 1000}, max=${connectionInfo.maxRequests}`);
|
|
184
|
+
}
|
|
185
|
+
// Set HTTP/2 server push hints if supported
|
|
186
|
+
if (connectionInfo.http2 && res.push) {
|
|
187
|
+
await this.setupHTTP2ServerPush(context, res);
|
|
188
|
+
}
|
|
189
|
+
// Apply connection-specific timeouts
|
|
190
|
+
this.applyConnectionTimeouts(context, connectionInfo);
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Set up HTTP/2 server push with intelligent resource detection
|
|
194
|
+
*/
|
|
195
|
+
async setupHTTP2ServerPush(context, res) {
|
|
196
|
+
const { req } = context;
|
|
197
|
+
const http2Res = res; // HTTP/2 response with push method
|
|
198
|
+
const config = this.getConnectionConfig();
|
|
199
|
+
if (!config.http2?.enabled || !http2Res.push) {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
try {
|
|
203
|
+
// Analyze request to determine critical resources to push
|
|
204
|
+
const criticalResources = await this.identifyCriticalResources(req);
|
|
205
|
+
for (const resource of criticalResources) {
|
|
206
|
+
// Check if resource should be pushed based on cache headers and client hints
|
|
207
|
+
if (await this.shouldPushResource(req, resource)) {
|
|
208
|
+
await this.pushResource(http2Res, resource);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
catch (error) {
|
|
213
|
+
// Log error but don't fail the request
|
|
214
|
+
console.warn("HTTP/2 server push failed:", error);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Identify critical resources based on request analysis and file existence
|
|
219
|
+
*/
|
|
220
|
+
async identifyCriticalResources(req) {
|
|
221
|
+
const potentialResources = [];
|
|
222
|
+
const userAgent = req.get("user-agent") || "";
|
|
223
|
+
const acceptHeader = req.get("accept") || "";
|
|
224
|
+
// Analyze request path and headers to determine critical resources
|
|
225
|
+
if (req.path === "/" || req.path.endsWith(".html")) {
|
|
226
|
+
// For HTML pages, push critical CSS and JS
|
|
227
|
+
if (acceptHeader.includes("text/css")) {
|
|
228
|
+
potentialResources.push("/assets/critical.css", "/css/main.css", "/styles/app.css");
|
|
229
|
+
}
|
|
230
|
+
if (acceptHeader.includes("application/javascript")) {
|
|
231
|
+
potentialResources.push("/assets/app.js", "/js/main.js", "/scripts/app.js");
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
// Add resources based on user agent (mobile vs desktop)
|
|
235
|
+
if (userAgent.includes("Mobile")) {
|
|
236
|
+
potentialResources.push("/assets/mobile.css", "/css/mobile.css");
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
potentialResources.push("/assets/desktop.css", "/css/desktop.css");
|
|
240
|
+
}
|
|
241
|
+
// Filter resources to only include those that actually exist
|
|
242
|
+
const existingResources = [];
|
|
243
|
+
for (const resource of potentialResources) {
|
|
244
|
+
if (await this.resourceExists(resource)) {
|
|
245
|
+
existingResources.push(resource);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return existingResources;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Determine if a resource should be pushed based on cache headers and client hints
|
|
252
|
+
*/
|
|
253
|
+
async shouldPushResource(req, resource) {
|
|
254
|
+
// Check client cache control directives
|
|
255
|
+
const cacheControl = req.get("cache-control") || "";
|
|
256
|
+
// Don't push if client explicitly doesn't want cached resources
|
|
257
|
+
if (cacheControl.includes("no-cache") ||
|
|
258
|
+
cacheControl.includes("no-store")) {
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
261
|
+
// Check if resource is already in client cache using proper ETag comparison
|
|
262
|
+
const ifNoneMatch = req.get("if-none-match");
|
|
263
|
+
if (ifNoneMatch) {
|
|
264
|
+
const resourceETag = await this.generateResourceETag(resource);
|
|
265
|
+
// Parse multiple ETags from If-None-Match header
|
|
266
|
+
const clientETags = ifNoneMatch
|
|
267
|
+
.split(",")
|
|
268
|
+
.map((etag) => etag.trim());
|
|
269
|
+
for (const clientETag of clientETags) {
|
|
270
|
+
if (clientETag === "*" ||
|
|
271
|
+
clientETag === resourceETag ||
|
|
272
|
+
clientETag === `W/${resourceETag}`) {
|
|
273
|
+
return false; // Resource is already cached
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
// Check if resource was recently modified using If-Modified-Since
|
|
278
|
+
const ifModifiedSince = req.get("if-modified-since");
|
|
279
|
+
if (ifModifiedSince) {
|
|
280
|
+
const modifiedSince = new Date(ifModifiedSince);
|
|
281
|
+
const resourceModified = await this.getResourceModificationTime(resource);
|
|
282
|
+
if (resourceModified <= modifiedSince) {
|
|
283
|
+
return false; // Resource hasn't been modified
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
// Check client connection type for bandwidth optimization
|
|
287
|
+
const connection = req.get("connection") || "";
|
|
288
|
+
if (connection.includes("slow") || req.get("save-data") === "on") {
|
|
289
|
+
// Only push critical resources for slow connections
|
|
290
|
+
return this.isCriticalResource(resource);
|
|
291
|
+
}
|
|
292
|
+
return true;
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Get resource modification time from actual file system
|
|
296
|
+
*/
|
|
297
|
+
async getResourceModificationTime(resource) {
|
|
298
|
+
try {
|
|
299
|
+
const filePath = this.resolveResourcePath(resource);
|
|
300
|
+
const stats = await fs.promises.stat(filePath);
|
|
301
|
+
return stats.mtime;
|
|
302
|
+
}
|
|
303
|
+
catch (error) {
|
|
304
|
+
// If file doesn't exist or can't be accessed, return current time
|
|
305
|
+
// This ensures ETags and cache headers still work
|
|
306
|
+
return new Date();
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Resolve resource path to actual file system path
|
|
311
|
+
*/
|
|
312
|
+
resolveResourcePath(resource) {
|
|
313
|
+
// Remove leading slash and resolve relative to static base directory
|
|
314
|
+
const relativePath = resource.startsWith("/")
|
|
315
|
+
? resource.slice(1)
|
|
316
|
+
: resource;
|
|
317
|
+
return path.resolve(this.staticBaseDir, relativePath);
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Check if a resource exists and is accessible
|
|
321
|
+
*/
|
|
322
|
+
async resourceExists(resource) {
|
|
323
|
+
try {
|
|
324
|
+
const filePath = this.resolveResourcePath(resource);
|
|
325
|
+
await fs.promises.access(filePath, fs.constants.R_OK);
|
|
326
|
+
const stats = await fs.promises.stat(filePath);
|
|
327
|
+
return stats.isFile();
|
|
328
|
+
}
|
|
329
|
+
catch (error) {
|
|
330
|
+
return false;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Determine if a resource is critical for page rendering
|
|
335
|
+
*/
|
|
336
|
+
isCriticalResource(resource) {
|
|
337
|
+
// Critical resources that should always be pushed for performance
|
|
338
|
+
const criticalPatterns = [
|
|
339
|
+
/\/critical\./,
|
|
340
|
+
/\/main\./,
|
|
341
|
+
/\/app\./,
|
|
342
|
+
/\/styles?\./,
|
|
343
|
+
/\/fonts?\//,
|
|
344
|
+
];
|
|
345
|
+
return criticalPatterns.some((pattern) => pattern.test(resource));
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Push a resource using HTTP/2 server push with proper content handling
|
|
349
|
+
*/
|
|
350
|
+
async pushResource(http2Res, resource) {
|
|
351
|
+
return new Promise(async (resolve) => {
|
|
352
|
+
const resourceETag = await this.generateResourceETag(resource);
|
|
353
|
+
const lastModified = await this.getResourceModificationTime(resource);
|
|
354
|
+
http2Res.push(resource, {
|
|
355
|
+
request: {
|
|
356
|
+
accept: this.getAcceptHeaderForResource(resource),
|
|
357
|
+
"user-agent": "XyPriss-ServerPush/1.0",
|
|
358
|
+
},
|
|
359
|
+
response: {
|
|
360
|
+
"content-type": this.getContentType(resource),
|
|
361
|
+
"cache-control": this.getCacheControlForResource(resource),
|
|
362
|
+
etag: resourceETag,
|
|
363
|
+
"last-modified": lastModified.toUTCString(),
|
|
364
|
+
"x-pushed-by": "xypriss",
|
|
365
|
+
vary: "Accept-Encoding",
|
|
366
|
+
},
|
|
367
|
+
}, async (err, pushStream) => {
|
|
368
|
+
if (err) {
|
|
369
|
+
resolve();
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
try {
|
|
373
|
+
// Generate appropriate content for the resource
|
|
374
|
+
const content = await this.generateResourceContent(resource);
|
|
375
|
+
// Set content length
|
|
376
|
+
pushStream.setHeader("content-length", Buffer.byteLength(content));
|
|
377
|
+
// Send the content
|
|
378
|
+
pushStream.end(content);
|
|
379
|
+
resolve();
|
|
380
|
+
}
|
|
381
|
+
catch (error) {
|
|
382
|
+
// If content generation fails, send minimal fallback
|
|
383
|
+
const fallbackContent = this.getFallbackContent(resource);
|
|
384
|
+
pushStream.end(fallbackContent);
|
|
385
|
+
resolve();
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Generate appropriate cache control header for resource type
|
|
392
|
+
*/
|
|
393
|
+
getCacheControlForResource(resource) {
|
|
394
|
+
if (resource.includes("/fonts/") || resource.includes("/images/")) {
|
|
395
|
+
// Long cache for static assets
|
|
396
|
+
return "public, max-age=31536000, immutable";
|
|
397
|
+
}
|
|
398
|
+
else if (resource.endsWith(".css") || resource.endsWith(".js")) {
|
|
399
|
+
// Medium cache for stylesheets and scripts
|
|
400
|
+
return "public, max-age=86400, must-revalidate";
|
|
401
|
+
}
|
|
402
|
+
else {
|
|
403
|
+
// Short cache for dynamic content
|
|
404
|
+
return "public, max-age=3600, must-revalidate";
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Generate content for a resource by reading from file system
|
|
409
|
+
*/
|
|
410
|
+
async generateResourceContent(resource) {
|
|
411
|
+
try {
|
|
412
|
+
const filePath = this.resolveResourcePath(resource);
|
|
413
|
+
// Check if file exists and is readable
|
|
414
|
+
await fs.promises.access(filePath, fs.constants.R_OK);
|
|
415
|
+
// Read file content
|
|
416
|
+
const content = await fs.promises.readFile(filePath, "utf8");
|
|
417
|
+
return content;
|
|
418
|
+
}
|
|
419
|
+
catch (error) {
|
|
420
|
+
// If file doesn't exist or can't be read, generate fallback content
|
|
421
|
+
if (resource.endsWith(".css")) {
|
|
422
|
+
return this.generateCSSContent(resource);
|
|
423
|
+
}
|
|
424
|
+
else if (resource.endsWith(".js")) {
|
|
425
|
+
return this.generateJSContent(resource);
|
|
426
|
+
}
|
|
427
|
+
else if (resource.endsWith(".json")) {
|
|
428
|
+
return this.generateJSONContent(resource);
|
|
429
|
+
}
|
|
430
|
+
else {
|
|
431
|
+
return this.generateGenericContent(resource);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Generate CSS content for stylesheets
|
|
437
|
+
*/
|
|
438
|
+
generateCSSContent(resource) {
|
|
439
|
+
const resourceName = resource.split("/").pop()?.replace(".css", "") || "default";
|
|
440
|
+
return `/* ${resourceName} stylesheet - Generated by XyPriss */
|
|
441
|
+
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
|
|
442
|
+
.${resourceName} { display: block; margin: 0; padding: 0; }
|
|
443
|
+
/* Optimized for ${resource} */`;
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Generate JavaScript content for scripts
|
|
447
|
+
*/
|
|
448
|
+
generateJSContent(resource) {
|
|
449
|
+
const resourceName = resource.split("/").pop()?.replace(".js", "") || "default";
|
|
450
|
+
return `// ${resourceName} script - Generated by Nehonix XyPriss
|
|
451
|
+
(function() {
|
|
452
|
+
'use strict';
|
|
453
|
+
console.log('${resourceName} loaded via HTTP/2 server push');
|
|
454
|
+
// Optimized for ${resource}
|
|
455
|
+
})();`;
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Generate JSON content for data resources
|
|
459
|
+
*/
|
|
460
|
+
generateJSONContent(resource) {
|
|
461
|
+
const resourceName = resource.split("/").pop()?.replace(".json", "") || "default";
|
|
462
|
+
return JSON.stringify({
|
|
463
|
+
resource: resourceName,
|
|
464
|
+
pushedBy: "xypriss",
|
|
465
|
+
timestamp: new Date().toISOString(),
|
|
466
|
+
path: resource,
|
|
467
|
+
}, null, 2);
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Generate generic content for other resource types
|
|
471
|
+
*/
|
|
472
|
+
generateGenericContent(resource) {
|
|
473
|
+
return `Resource: ${resource}
|
|
474
|
+
Generated by: XyPriss HTTP/2 Server Push
|
|
475
|
+
Timestamp: ${new Date().toISOString()}
|
|
476
|
+
Content-Type: ${this.getContentType(resource)}`;
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* Get fallback content when resource generation fails
|
|
480
|
+
*/
|
|
481
|
+
getFallbackContent(resource) {
|
|
482
|
+
return `/* Fallback content for ${resource} */`;
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Generate ETag for a resource based on file stats
|
|
486
|
+
*/
|
|
487
|
+
async generateResourceETag(resource) {
|
|
488
|
+
try {
|
|
489
|
+
const filePath = this.resolveResourcePath(resource);
|
|
490
|
+
const stats = await fs.promises.stat(filePath);
|
|
491
|
+
// Create ETag based on file modification time and size
|
|
492
|
+
const resourceHash = crypto__namespace
|
|
493
|
+
.createHash("md5")
|
|
494
|
+
.update(`${resource}-${stats.mtime.getTime()}-${stats.size}`)
|
|
495
|
+
.digest("hex")
|
|
496
|
+
.substring(0, 16);
|
|
497
|
+
return `"${resourceHash}"`;
|
|
498
|
+
}
|
|
499
|
+
catch (error) {
|
|
500
|
+
// If file doesn't exist, create ETag based on resource path
|
|
501
|
+
const resourceHash = crypto__namespace
|
|
502
|
+
.createHash("md5")
|
|
503
|
+
.update(resource)
|
|
504
|
+
.digest("hex")
|
|
505
|
+
.substring(0, 16);
|
|
506
|
+
return `"${resourceHash}"`;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* Get appropriate Accept header for resource type
|
|
511
|
+
*/
|
|
512
|
+
getAcceptHeaderForResource(resource) {
|
|
513
|
+
if (resource.endsWith(".css")) {
|
|
514
|
+
return "text/css,*/*;q=0.1";
|
|
515
|
+
}
|
|
516
|
+
if (resource.endsWith(".js")) {
|
|
517
|
+
return "application/javascript,*/*;q=0.1";
|
|
518
|
+
}
|
|
519
|
+
if (resource.endsWith(".json")) {
|
|
520
|
+
return "application/json,*/*;q=0.1";
|
|
521
|
+
}
|
|
522
|
+
return "*/*";
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Apply connection timeouts
|
|
526
|
+
*/
|
|
527
|
+
applyConnectionTimeouts(context, _connectionInfo) {
|
|
528
|
+
const { req, res } = context;
|
|
529
|
+
const config = this.getConnectionConfig();
|
|
530
|
+
// Request timeout
|
|
531
|
+
if (config.timeouts?.request) {
|
|
532
|
+
req.setTimeout(config.timeouts.request, () => {
|
|
533
|
+
if (!res.headersSent) {
|
|
534
|
+
res.status(408).json({ error: "Request timeout" });
|
|
535
|
+
}
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
// Response timeout
|
|
539
|
+
if (config.timeouts?.response) {
|
|
540
|
+
res.setTimeout(config.timeouts.response, () => {
|
|
541
|
+
if (!res.headersSent) {
|
|
542
|
+
res.status(504).json({ error: "Response timeout" });
|
|
543
|
+
}
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Set up connection cleanup
|
|
549
|
+
*/
|
|
550
|
+
setupConnectionCleanup(connectionKey, connectionInfo) {
|
|
551
|
+
// Clear existing timeout
|
|
552
|
+
const existingTimeout = this.connectionTimeouts.get(connectionKey);
|
|
553
|
+
if (existingTimeout) {
|
|
554
|
+
clearTimeout(existingTimeout);
|
|
555
|
+
}
|
|
556
|
+
// Set new cleanup timeout
|
|
557
|
+
const timeout = setTimeout(() => {
|
|
558
|
+
this.cleanupConnection(connectionKey);
|
|
559
|
+
}, connectionInfo.timeout);
|
|
560
|
+
this.connectionTimeouts.set(connectionKey, timeout);
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* Clean up connection
|
|
564
|
+
*/
|
|
565
|
+
cleanupConnection(connectionKey) {
|
|
566
|
+
const connectionInfo = this.connectionPool.get(connectionKey);
|
|
567
|
+
if (connectionInfo) {
|
|
568
|
+
this.connectionPool.delete(connectionKey);
|
|
569
|
+
this.activeConnections--;
|
|
570
|
+
this.keepAliveStats.timeoutCount++;
|
|
571
|
+
}
|
|
572
|
+
const timeout = this.connectionTimeouts.get(connectionKey);
|
|
573
|
+
if (timeout) {
|
|
574
|
+
clearTimeout(timeout);
|
|
575
|
+
this.connectionTimeouts.delete(connectionKey);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* Get connection headers
|
|
580
|
+
*/
|
|
581
|
+
getConnectionHeaders(connectionInfo) {
|
|
582
|
+
const headers = {};
|
|
583
|
+
if (connectionInfo.keepAlive) {
|
|
584
|
+
headers["Connection"] = "keep-alive";
|
|
585
|
+
headers["Keep-Alive"] = `timeout=${connectionInfo.timeout / 1000}, max=${connectionInfo.maxRequests}`;
|
|
586
|
+
}
|
|
587
|
+
if (connectionInfo.http2) {
|
|
588
|
+
headers["Alt-Svc"] = 'h2=":443"; ma=86400';
|
|
589
|
+
}
|
|
590
|
+
return headers;
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* Configure HTTP/2 settings and optimizations
|
|
594
|
+
*/
|
|
595
|
+
async configureHTTP2() {
|
|
596
|
+
const config = this.getConnectionConfig();
|
|
597
|
+
if (!config.http2?.enabled) {
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
// Set HTTP/2 specific connection parameters
|
|
601
|
+
const http2Config = config.http2;
|
|
602
|
+
// Configure stream limits
|
|
603
|
+
if (http2Config.maxConcurrentStreams) {
|
|
604
|
+
this.http2Stats.maxStreams = http2Config.maxConcurrentStreams;
|
|
605
|
+
}
|
|
606
|
+
// Configure server push settings (enabled by default for HTTP/2)
|
|
607
|
+
this.http2Stats.serverPushEnabled = true;
|
|
608
|
+
// Configure frame size and window size optimizations
|
|
609
|
+
this.http2Stats.configured = true;
|
|
610
|
+
}
|
|
611
|
+
/**
|
|
612
|
+
* Configure keep-alive
|
|
613
|
+
*/
|
|
614
|
+
configureKeepAlive() {
|
|
615
|
+
// Keep-alive configuration
|
|
616
|
+
const config = this.getConnectionConfig();
|
|
617
|
+
// Set up periodic cleanup of idle connections
|
|
618
|
+
setInterval(() => {
|
|
619
|
+
this.cleanupIdleConnections();
|
|
620
|
+
}, config.keepAlive?.maxIdleTime || 60000);
|
|
621
|
+
}
|
|
622
|
+
/**
|
|
623
|
+
* Clean up idle connections
|
|
624
|
+
*/
|
|
625
|
+
cleanupIdleConnections() {
|
|
626
|
+
const now = Date.now();
|
|
627
|
+
const config = this.getConnectionConfig();
|
|
628
|
+
const maxIdleTime = config.keepAlive?.maxIdleTime || 60000;
|
|
629
|
+
for (const [key, connectionInfo] of this.connectionPool.entries()) {
|
|
630
|
+
if (now - connectionInfo.lastUsed > maxIdleTime) {
|
|
631
|
+
this.cleanupConnection(key);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
/**
|
|
636
|
+
* Start connection monitoring
|
|
637
|
+
*/
|
|
638
|
+
startConnectionMonitoring() {
|
|
639
|
+
setInterval(() => {
|
|
640
|
+
this.updateHealthStatus();
|
|
641
|
+
}, 30000); // Update health every 30 seconds
|
|
642
|
+
}
|
|
643
|
+
/**
|
|
644
|
+
* Generate secure connection ID using xypriss-security
|
|
645
|
+
*/
|
|
646
|
+
generateConnectionId() {
|
|
647
|
+
try {
|
|
648
|
+
// Use secure token generation for connection IDs
|
|
649
|
+
const secureToken = xyprissSecurity.RandomTokens.generateSecureToken(16);
|
|
650
|
+
return `conn_${Date.now()}_${secureToken}`;
|
|
651
|
+
}
|
|
652
|
+
catch (error) {
|
|
653
|
+
// Fallback to crypto random bytes
|
|
654
|
+
const randomBytes = crypto__namespace.randomBytes(8).toString("hex");
|
|
655
|
+
return `conn_${Date.now()}_${randomBytes}`;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
/**
|
|
659
|
+
* Get connection key for pooling
|
|
660
|
+
*/
|
|
661
|
+
getConnectionKey(context) {
|
|
662
|
+
return `${context.connection.remoteAddress}:${context.connection.remotePort}`;
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* Get content type for resource using mime-types library
|
|
666
|
+
*/
|
|
667
|
+
getContentType(resource) {
|
|
668
|
+
try {
|
|
669
|
+
// Use mime-types library for accurate content type detection
|
|
670
|
+
const mimeType = getMimeType(resource);
|
|
671
|
+
return mimeType || "text/plain";
|
|
672
|
+
}
|
|
673
|
+
catch (error) {
|
|
674
|
+
// Fallback to basic detection if mime-types fails
|
|
675
|
+
if (resource.endsWith(".css"))
|
|
676
|
+
return "text/css";
|
|
677
|
+
if (resource.endsWith(".js"))
|
|
678
|
+
return "application/javascript";
|
|
679
|
+
if (resource.endsWith(".json"))
|
|
680
|
+
return "application/json";
|
|
681
|
+
if (resource.endsWith(".html"))
|
|
682
|
+
return "text/html";
|
|
683
|
+
if (resource.endsWith(".png"))
|
|
684
|
+
return "image/png";
|
|
685
|
+
if (resource.endsWith(".jpg") || resource.endsWith(".jpeg"))
|
|
686
|
+
return "image/jpeg";
|
|
687
|
+
if (resource.endsWith(".gif"))
|
|
688
|
+
return "image/gif";
|
|
689
|
+
if (resource.endsWith(".svg"))
|
|
690
|
+
return "image/svg+xml";
|
|
691
|
+
return "text/plain";
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
/**
|
|
695
|
+
* Get connection configuration
|
|
696
|
+
*/
|
|
697
|
+
getConnectionConfig() {
|
|
698
|
+
return this.config;
|
|
699
|
+
}
|
|
700
|
+
/**
|
|
701
|
+
* Validate connection configuration
|
|
702
|
+
*/
|
|
703
|
+
validateNetworkConfig(config) {
|
|
704
|
+
if (config.connectionPool?.maxConnections &&
|
|
705
|
+
config.connectionPool.maxConnections < 1) {
|
|
706
|
+
return false;
|
|
707
|
+
}
|
|
708
|
+
if (config.keepAlive?.timeout && config.keepAlive.timeout < 1000) {
|
|
709
|
+
return false;
|
|
710
|
+
}
|
|
711
|
+
return true;
|
|
712
|
+
}
|
|
713
|
+
/**
|
|
714
|
+
* Check network health
|
|
715
|
+
*/
|
|
716
|
+
async checkNetworkHealth() {
|
|
717
|
+
const errorRate = this.performanceMetrics.errorCount /
|
|
718
|
+
Math.max(this.performanceMetrics.totalExecutions, 1);
|
|
719
|
+
const connectionUtilization = this.activeConnections / this.maxConnections;
|
|
720
|
+
return {
|
|
721
|
+
healthy: errorRate < 0.1 && connectionUtilization < 0.9,
|
|
722
|
+
status: errorRate < 0.05 && connectionUtilization < 0.7
|
|
723
|
+
? "healthy"
|
|
724
|
+
: errorRate < 0.1 && connectionUtilization < 0.9
|
|
725
|
+
? "degraded"
|
|
726
|
+
: "unhealthy",
|
|
727
|
+
metrics: {
|
|
728
|
+
responseTime: this.performanceMetrics.averageExecutionTime,
|
|
729
|
+
errorRate,
|
|
730
|
+
throughput: this.performanceMetrics.totalExecutions,
|
|
731
|
+
connections: this.activeConnections,
|
|
732
|
+
},
|
|
733
|
+
lastCheck: new Date(),
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
/**
|
|
737
|
+
* Get connection statistics
|
|
738
|
+
*/
|
|
739
|
+
getConnectionStats() {
|
|
740
|
+
return {
|
|
741
|
+
activeConnections: this.activeConnections,
|
|
742
|
+
maxConnections: this.maxConnections,
|
|
743
|
+
connectionPoolSize: this.connectionPool.size,
|
|
744
|
+
keepAliveStats: { ...this.keepAliveStats },
|
|
745
|
+
};
|
|
746
|
+
}
|
|
747
|
+
/**
|
|
748
|
+
* Serve a static file with proper headers and caching
|
|
749
|
+
*/
|
|
750
|
+
async serveStaticFile(resource, res) {
|
|
751
|
+
try {
|
|
752
|
+
const filePath = this.resolveResourcePath(resource);
|
|
753
|
+
// Check if file exists and is readable
|
|
754
|
+
await fs.promises.access(filePath, fs.constants.R_OK);
|
|
755
|
+
const stats = await fs.promises.stat(filePath);
|
|
756
|
+
if (!stats.isFile()) {
|
|
757
|
+
return false;
|
|
758
|
+
}
|
|
759
|
+
// Set content type
|
|
760
|
+
const contentType = this.getContentType(resource);
|
|
761
|
+
res.setHeader("Content-Type", contentType);
|
|
762
|
+
// Set cache headers
|
|
763
|
+
const cacheControl = this.getCacheControlForResource(resource);
|
|
764
|
+
res.setHeader("Cache-Control", cacheControl);
|
|
765
|
+
// Set ETag and Last-Modified headers
|
|
766
|
+
const etag = await this.generateResourceETag(resource);
|
|
767
|
+
res.setHeader("ETag", etag);
|
|
768
|
+
res.setHeader("Last-Modified", stats.mtime.toUTCString());
|
|
769
|
+
// Set content length
|
|
770
|
+
res.setHeader("Content-Length", stats.size);
|
|
771
|
+
// Read and send file
|
|
772
|
+
const content = await fs.promises.readFile(filePath);
|
|
773
|
+
res.end(content);
|
|
774
|
+
return true;
|
|
775
|
+
}
|
|
776
|
+
catch (error) {
|
|
777
|
+
return false;
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
/**
|
|
781
|
+
* Cleanup resources
|
|
782
|
+
*/
|
|
783
|
+
async destroy() {
|
|
784
|
+
// Clear all timeouts
|
|
785
|
+
for (const timeout of this.connectionTimeouts.values()) {
|
|
786
|
+
clearTimeout(timeout);
|
|
787
|
+
}
|
|
788
|
+
this.connectionTimeouts.clear();
|
|
789
|
+
// Clear connection pool
|
|
790
|
+
this.connectionPool.clear();
|
|
791
|
+
this.activeConnections = 0;
|
|
792
|
+
await super.destroy();
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
exports.ConnectionPlugin = ConnectionPlugin;
|
|
797
|
+
//# sourceMappingURL=ConnectionPlugin.js.map
|