@morojs/moro 1.6.0 → 1.6.2
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/core/config/config-sources.js +4 -0
- package/dist/core/config/config-sources.js.map +1 -1
- package/dist/core/config/config-validator.js +3 -0
- package/dist/core/config/config-validator.js.map +1 -1
- package/dist/core/config/file-loader.js +3 -1
- package/dist/core/config/file-loader.js.map +1 -1
- package/dist/core/config/schema.js +4 -1
- package/dist/core/config/schema.js.map +1 -1
- package/dist/core/events/event-bus.js +1 -1
- package/dist/core/events/event-bus.js.map +1 -1
- package/dist/core/framework.d.ts +1 -1
- package/dist/core/framework.js +13 -7
- package/dist/core/framework.js.map +1 -1
- package/dist/core/http/http-server.d.ts +55 -15
- package/dist/core/http/http-server.js +70 -146
- package/dist/core/http/http-server.js.map +1 -1
- package/dist/core/http/index.d.ts +1 -1
- package/dist/core/http/index.js +1 -1
- package/dist/core/http/index.js.map +1 -1
- package/dist/core/http/uws-http-server.d.ts +4 -22
- package/dist/core/http/uws-http-server.js +43 -208
- package/dist/core/http/uws-http-server.js.map +1 -1
- package/dist/core/networking/adapters/uws-adapter.d.ts +1 -1
- package/dist/core/networking/adapters/uws-adapter.js +1 -1
- package/dist/core/pooling/object-pool-manager.d.ts +140 -0
- package/dist/core/pooling/object-pool-manager.js +502 -0
- package/dist/core/pooling/object-pool-manager.js.map +1 -0
- package/dist/core/routing/app-integration.d.ts +12 -10
- package/dist/core/routing/app-integration.js +43 -74
- package/dist/core/routing/app-integration.js.map +1 -1
- package/dist/core/routing/index.d.ts +15 -29
- package/dist/core/routing/index.js +43 -390
- package/dist/core/routing/index.js.map +1 -1
- package/dist/core/routing/path-matcher.d.ts +67 -0
- package/dist/core/routing/path-matcher.js +182 -0
- package/dist/core/routing/path-matcher.js.map +1 -0
- package/dist/core/{http → routing}/router.d.ts +21 -9
- package/dist/core/routing/router.js +68 -0
- package/dist/core/routing/router.js.map +1 -0
- package/dist/core/routing/unified-router.d.ts +148 -0
- package/dist/core/routing/unified-router.js +684 -0
- package/dist/core/routing/unified-router.js.map +1 -0
- package/dist/moro.d.ts +10 -7
- package/dist/moro.js +90 -41
- package/dist/moro.js.map +1 -1
- package/dist/types/config.d.ts +3 -0
- package/package.json +1 -1
- package/src/core/config/config-sources.ts +4 -0
- package/src/core/config/config-validator.ts +3 -0
- package/src/core/config/file-loader.ts +4 -1
- package/src/core/config/schema.ts +4 -1
- package/src/core/events/event-bus.ts +1 -1
- package/src/core/framework.ts +14 -9
- package/src/core/http/http-server.ts +76 -161
- package/src/core/http/index.ts +1 -1
- package/src/core/http/uws-http-server.ts +43 -246
- package/src/core/networking/adapters/uws-adapter.ts +1 -1
- package/src/core/pooling/object-pool-manager.ts +630 -0
- package/src/core/routing/app-integration.ts +57 -109
- package/src/core/routing/index.ts +62 -473
- package/src/core/routing/path-matcher.ts +222 -0
- package/src/core/routing/router.ts +97 -0
- package/src/core/routing/unified-router.ts +870 -0
- package/src/moro.ts +107 -57
- package/src/types/config.ts +3 -0
- package/dist/core/http/router.js +0 -183
- package/dist/core/http/router.js.map +0 -1
- package/src/core/http/router.ts +0 -230
|
@@ -10,10 +10,13 @@ export const DEFAULT_CONFIG: AppConfig = {
|
|
|
10
10
|
maxConnections: 1000,
|
|
11
11
|
timeout: 30000,
|
|
12
12
|
bodySizeLimit: '10mb',
|
|
13
|
-
useUWebSockets: false, // Opt-in for
|
|
13
|
+
useUWebSockets: false, // Opt-in for high performance
|
|
14
14
|
requestTracking: {
|
|
15
15
|
enabled: true, // Enable by default for debugging
|
|
16
16
|
},
|
|
17
|
+
requestLogging: {
|
|
18
|
+
enabled: true, // Enable by default - logs requests independently
|
|
19
|
+
},
|
|
17
20
|
errorBoundary: {
|
|
18
21
|
enabled: true, // Always enabled for safety
|
|
19
22
|
},
|
|
@@ -33,7 +33,7 @@ export class MoroEventBus implements GlobalEventBus {
|
|
|
33
33
|
|
|
34
34
|
// Global event emission with full context and metrics
|
|
35
35
|
async emit<T = any>(event: string, data: T, context?: Partial<EventContext>): Promise<boolean> {
|
|
36
|
-
//
|
|
36
|
+
// Skip processing if no listeners
|
|
37
37
|
if (this.emitter.listenerCount(event) === 0) {
|
|
38
38
|
return false;
|
|
39
39
|
}
|
package/src/core/framework.ts
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
import { EventEmitter } from 'events';
|
|
8
8
|
import { MoroHttpServer, HttpRequest, HttpResponse, middleware } from './http/index.js';
|
|
9
9
|
import { UWebSocketsHttpServer } from './http/uws-http-server.js';
|
|
10
|
-
import { Router } from './
|
|
10
|
+
import { Router } from './routing/router.js';
|
|
11
11
|
import { Container } from './utilities/container.js';
|
|
12
12
|
import { ModuleLoader } from './modules/index.js';
|
|
13
13
|
import { WebSocketManager } from './networking/websocket-manager.js';
|
|
@@ -87,7 +87,7 @@ export class Moro extends EventEmitter {
|
|
|
87
87
|
|
|
88
88
|
if (useUWebSockets) {
|
|
89
89
|
try {
|
|
90
|
-
// Try to use uWebSockets for
|
|
90
|
+
// Try to use uWebSockets for high performance HTTP and WebSocket
|
|
91
91
|
const sslOptions = this.config.server?.ssl || options.https;
|
|
92
92
|
this.httpServer = new UWebSocketsHttpServer({ ssl: sslOptions });
|
|
93
93
|
this.server = (this.httpServer as UWebSocketsHttpServer).getApp();
|
|
@@ -199,9 +199,14 @@ export class Moro extends EventEmitter {
|
|
|
199
199
|
// Body size middleware - always enabled with configurable limit
|
|
200
200
|
this.httpServer.use(middleware.bodySize({ limit: this.config.server.bodySizeLimit }));
|
|
201
201
|
|
|
202
|
-
//
|
|
203
|
-
if (this.
|
|
204
|
-
this.httpServer.
|
|
202
|
+
// Configure request tracking (ID generation) in HTTP server
|
|
203
|
+
if (this.httpServer.setRequestTracking) {
|
|
204
|
+
this.httpServer.setRequestTracking(this.config.server.requestTracking.enabled);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Request logging middleware - separate from request tracking (ID generation)
|
|
208
|
+
if (this.config.server.requestLogging.enabled) {
|
|
209
|
+
this.httpServer.use(this.requestLoggingMiddleware());
|
|
205
210
|
}
|
|
206
211
|
|
|
207
212
|
// Error boundary middleware - configurable but recommended to keep enabled
|
|
@@ -339,15 +344,15 @@ export class Moro extends EventEmitter {
|
|
|
339
344
|
return null;
|
|
340
345
|
}
|
|
341
346
|
|
|
342
|
-
private
|
|
347
|
+
private requestLoggingMiddleware() {
|
|
343
348
|
return (req: HttpRequest, res: HttpResponse, next: () => void) => {
|
|
344
349
|
const startTime = Date.now();
|
|
345
350
|
|
|
346
351
|
res.on('finish', () => {
|
|
347
352
|
const duration = Date.now() - startTime;
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
);
|
|
353
|
+
// Include request ID in log if request tracking is enabled
|
|
354
|
+
const idPart = req.requestId ? ` [${req.requestId}]` : '';
|
|
355
|
+
this.logger.info(`${req.method} ${req.path} - ${res.statusCode} - ${duration}ms${idPart}`);
|
|
351
356
|
});
|
|
352
357
|
|
|
353
358
|
next();
|
|
@@ -12,6 +12,8 @@ import {
|
|
|
12
12
|
Middleware,
|
|
13
13
|
RouteEntry,
|
|
14
14
|
} from '../../types/http.js';
|
|
15
|
+
import { PathMatcher } from '../routing/path-matcher.js';
|
|
16
|
+
import { ObjectPoolManager } from '../pooling/object-pool-manager.js';
|
|
15
17
|
|
|
16
18
|
const gzip = promisify(zlib.gzip);
|
|
17
19
|
const deflate = promisify(zlib.deflate);
|
|
@@ -22,46 +24,15 @@ export class MoroHttpServer {
|
|
|
22
24
|
private globalMiddleware: Middleware[] = [];
|
|
23
25
|
private compressionEnabled = true;
|
|
24
26
|
private compressionThreshold = 1024;
|
|
27
|
+
private requestTrackingEnabled = true; // Generate request IDs
|
|
25
28
|
private logger = createFrameworkLogger('HttpServer');
|
|
26
29
|
private hookManager: any;
|
|
27
30
|
private requestCounter = 0;
|
|
28
31
|
|
|
29
|
-
//
|
|
30
|
-
private
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
// Request handler pooling to avoid function creation overhead
|
|
35
|
-
private middlewareExecutionCache = new Map<string, Function>();
|
|
36
|
-
|
|
37
|
-
// Response caching for ultra-fast common responses
|
|
38
|
-
private responseCache = new Map<string, Buffer>();
|
|
39
|
-
private responseCacheHits = 0;
|
|
40
|
-
private responseCacheMisses = 0;
|
|
41
|
-
|
|
42
|
-
// String interning for common values (massive memory savings)
|
|
43
|
-
private static readonly INTERNED_METHODS = new Map([
|
|
44
|
-
['GET', 'GET'],
|
|
45
|
-
['POST', 'POST'],
|
|
46
|
-
['PUT', 'PUT'],
|
|
47
|
-
['DELETE', 'DELETE'],
|
|
48
|
-
['PATCH', 'PATCH'],
|
|
49
|
-
['HEAD', 'HEAD'],
|
|
50
|
-
['OPTIONS', 'OPTIONS'],
|
|
51
|
-
]);
|
|
52
|
-
|
|
53
|
-
private static readonly INTERNED_HEADERS = new Map([
|
|
54
|
-
['content-type', 'content-type'],
|
|
55
|
-
['content-length', 'content-length'],
|
|
56
|
-
['authorization', 'authorization'],
|
|
57
|
-
['accept', 'accept'],
|
|
58
|
-
['user-agent', 'user-agent'],
|
|
59
|
-
['host', 'host'],
|
|
60
|
-
['connection', 'connection'],
|
|
61
|
-
['cache-control', 'cache-control'],
|
|
62
|
-
]);
|
|
63
|
-
|
|
64
|
-
// Pre-compiled response templates for ultra-common responses
|
|
32
|
+
// Use shared object pool manager
|
|
33
|
+
private poolManager = ObjectPoolManager.getInstance();
|
|
34
|
+
|
|
35
|
+
// Pre-compiled response templates for common responses
|
|
65
36
|
private static readonly RESPONSE_TEMPLATES = {
|
|
66
37
|
notFound: Buffer.from('{"success":false,"error":"Not found"}'),
|
|
67
38
|
unauthorized: Buffer.from('{"success":false,"error":"Unauthorized"}'),
|
|
@@ -71,7 +42,7 @@ export class MoroHttpServer {
|
|
|
71
42
|
rateLimited: Buffer.from('{"success":false,"error":"Rate limit exceeded"}'),
|
|
72
43
|
};
|
|
73
44
|
|
|
74
|
-
//
|
|
45
|
+
// Buffer pool for zero-copy operations
|
|
75
46
|
private static readonly BUFFER_SIZES = [64, 256, 1024, 4096, 16384];
|
|
76
47
|
private static readonly BUFFER_POOLS = new Map<number, Buffer[]>();
|
|
77
48
|
|
|
@@ -139,6 +110,11 @@ export class MoroHttpServer {
|
|
|
139
110
|
}
|
|
140
111
|
}
|
|
141
112
|
|
|
113
|
+
// Configure request tracking (ID generation)
|
|
114
|
+
setRequestTracking(enabled: boolean): void {
|
|
115
|
+
this.requestTrackingEnabled = enabled;
|
|
116
|
+
}
|
|
117
|
+
|
|
142
118
|
// Middleware management
|
|
143
119
|
use(middleware: Middleware): void {
|
|
144
120
|
this.globalMiddleware.push(middleware);
|
|
@@ -206,45 +182,37 @@ export class MoroHttpServer {
|
|
|
206
182
|
}
|
|
207
183
|
|
|
208
184
|
private pathToRegex(path: string): { pattern: RegExp; paramNames: string[] } {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
// Convert parameterized routes to regex
|
|
212
|
-
const regexPattern = path
|
|
213
|
-
.replace(/\/:([^/]+)/g, (match, paramName) => {
|
|
214
|
-
paramNames.push(paramName);
|
|
215
|
-
return '/([^/]+)';
|
|
216
|
-
})
|
|
217
|
-
.replace(/\//g, '\\/');
|
|
218
|
-
|
|
185
|
+
// Use shared PathMatcher for consistent path compilation
|
|
186
|
+
const compiled = PathMatcher.compile(path);
|
|
219
187
|
return {
|
|
220
|
-
pattern: new RegExp(`^${
|
|
221
|
-
paramNames,
|
|
188
|
+
pattern: compiled.pattern || new RegExp(`^${path.replace(/\//g, '\\/')}$`),
|
|
189
|
+
paramNames: compiled.paramNames,
|
|
222
190
|
};
|
|
223
191
|
}
|
|
224
192
|
|
|
225
193
|
private async handleRequest(req: IncomingMessage, res: ServerResponse): Promise<void> {
|
|
226
194
|
const httpReq = this.enhanceRequest(req);
|
|
227
|
-
const httpRes = this.enhanceResponse(res);
|
|
195
|
+
const httpRes = this.enhanceResponse(res, httpReq);
|
|
228
196
|
|
|
229
197
|
// Store original params for efficient cleanup
|
|
230
198
|
const originalParams = httpReq.params;
|
|
231
199
|
|
|
232
200
|
try {
|
|
233
|
-
// Optimized URL and query parsing
|
|
201
|
+
// Optimized URL and query parsing with object pooling
|
|
234
202
|
const urlString = req.url!;
|
|
235
203
|
const queryIndex = urlString.indexOf('?');
|
|
236
204
|
|
|
237
205
|
if (queryIndex === -1) {
|
|
238
|
-
// No query string
|
|
206
|
+
// No query string
|
|
239
207
|
httpReq.path = urlString;
|
|
240
208
|
httpReq.query = {};
|
|
241
209
|
} else {
|
|
242
|
-
// Has query string - parse efficiently
|
|
210
|
+
// Has query string - parse efficiently with pooled object
|
|
243
211
|
httpReq.path = urlString.substring(0, queryIndex);
|
|
244
|
-
httpReq.query = this.
|
|
212
|
+
httpReq.query = this.parseQueryStringPooled(urlString.substring(queryIndex + 1));
|
|
245
213
|
}
|
|
246
214
|
|
|
247
|
-
//
|
|
215
|
+
// Method checking - avoid array includes
|
|
248
216
|
const method = req.method!;
|
|
249
217
|
if (method === 'POST' || method === 'PUT' || method === 'PATCH') {
|
|
250
218
|
httpReq.body = await this.parseBody(req);
|
|
@@ -269,7 +237,7 @@ export class MoroHttpServer {
|
|
|
269
237
|
// Find matching route
|
|
270
238
|
const route = this.findRoute(req.method!, httpReq.path);
|
|
271
239
|
if (!route) {
|
|
272
|
-
//
|
|
240
|
+
// 404 response with pre-compiled buffer
|
|
273
241
|
httpRes.statusCode = 404;
|
|
274
242
|
httpRes.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|
275
243
|
httpRes.setHeader('Content-Length', MoroHttpServer.RESPONSE_TEMPLATES.notFound.length);
|
|
@@ -318,7 +286,7 @@ export class MoroHttpServer {
|
|
|
318
286
|
requestId: httpReq.requestId,
|
|
319
287
|
});
|
|
320
288
|
} else {
|
|
321
|
-
//
|
|
289
|
+
// Defensive fallback - check each method individually
|
|
322
290
|
if (typeof httpRes.setHeader === 'function') {
|
|
323
291
|
httpRes.statusCode = 500;
|
|
324
292
|
httpRes.setHeader('Content-Type', 'application/json');
|
|
@@ -376,55 +344,32 @@ export class MoroHttpServer {
|
|
|
376
344
|
});
|
|
377
345
|
}
|
|
378
346
|
|
|
379
|
-
//
|
|
347
|
+
// Use shared object pool for parameter objects
|
|
380
348
|
private acquireParamObject(): Record<string, string> {
|
|
381
|
-
|
|
382
|
-
if (obj) {
|
|
383
|
-
// ES2022: Use Object.hasOwn for safer property checks and faster clearing
|
|
384
|
-
// Clear existing properties more efficiently
|
|
385
|
-
for (const key in obj) {
|
|
386
|
-
if (Object.hasOwn(obj, key)) {
|
|
387
|
-
delete obj[key];
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
return obj;
|
|
391
|
-
}
|
|
392
|
-
return {};
|
|
349
|
+
return this.poolManager.acquireParams();
|
|
393
350
|
}
|
|
394
351
|
|
|
395
352
|
private releaseParamObject(params: Record<string, string>): void {
|
|
396
|
-
|
|
397
|
-
this.paramObjectPool.push(params);
|
|
398
|
-
}
|
|
353
|
+
this.poolManager.releaseParams(params);
|
|
399
354
|
}
|
|
400
355
|
|
|
401
356
|
// Force cleanup of all pooled objects
|
|
402
357
|
private forceCleanupPools(): void {
|
|
403
|
-
//
|
|
404
|
-
this.
|
|
405
|
-
this.bufferPool.splice(0);
|
|
358
|
+
// Use shared pool manager cleanup
|
|
359
|
+
this.poolManager.clearAll();
|
|
406
360
|
|
|
407
361
|
// Force garbage collection if available
|
|
408
|
-
// Use modern globalThis check with optional chaining
|
|
409
362
|
if (globalThis?.gc) {
|
|
410
363
|
globalThis.gc();
|
|
411
364
|
}
|
|
412
365
|
}
|
|
413
366
|
|
|
414
367
|
private acquireBuffer(size: number): Buffer {
|
|
415
|
-
|
|
416
|
-
const index = this.bufferPool.findIndex(b => b.length >= size);
|
|
417
|
-
if (index !== -1) {
|
|
418
|
-
const buffer = this.bufferPool.splice(index, 1)[0];
|
|
419
|
-
return buffer.subarray(0, size);
|
|
420
|
-
}
|
|
421
|
-
return Buffer.allocUnsafe(size);
|
|
368
|
+
return this.poolManager.acquireBuffer(size);
|
|
422
369
|
}
|
|
423
370
|
|
|
424
371
|
private releaseBuffer(buffer: Buffer): void {
|
|
425
|
-
|
|
426
|
-
this.bufferPool.push(buffer);
|
|
427
|
-
}
|
|
372
|
+
this.poolManager.releaseBuffer(buffer);
|
|
428
373
|
}
|
|
429
374
|
|
|
430
375
|
private streamLargeResponse(res: any, data: any): void {
|
|
@@ -448,7 +393,7 @@ export class MoroHttpServer {
|
|
|
448
393
|
return this.pathNormalizationCache.get(path)!;
|
|
449
394
|
}
|
|
450
395
|
|
|
451
|
-
//
|
|
396
|
+
// Normalization: remove trailing slash (except root), decode once
|
|
452
397
|
let normalized = path;
|
|
453
398
|
if (normalized.length > 1 && normalized.endsWith('/')) {
|
|
454
399
|
normalized = normalized.slice(0, -1);
|
|
@@ -469,8 +414,8 @@ export class MoroHttpServer {
|
|
|
469
414
|
httpReq.body = null;
|
|
470
415
|
httpReq.path = '';
|
|
471
416
|
httpReq.ip = req.socket.remoteAddress || '';
|
|
472
|
-
//
|
|
473
|
-
httpReq.requestId =
|
|
417
|
+
// Request ID generation using pool manager (if enabled)
|
|
418
|
+
httpReq.requestId = this.requestTrackingEnabled ? this.poolManager.generateRequestId() : '';
|
|
474
419
|
httpReq.headers = req.headers as Record<string, string>;
|
|
475
420
|
|
|
476
421
|
// Parse cookies
|
|
@@ -493,9 +438,12 @@ export class MoroHttpServer {
|
|
|
493
438
|
return cookies;
|
|
494
439
|
}
|
|
495
440
|
|
|
496
|
-
private enhanceResponse(res: ServerResponse): HttpResponse {
|
|
441
|
+
private enhanceResponse(res: ServerResponse, req: HttpRequest): HttpResponse {
|
|
497
442
|
const httpRes = res as HttpResponse;
|
|
498
443
|
|
|
444
|
+
// Store request reference for access to headers (needed for compression, logging, etc.)
|
|
445
|
+
(httpRes as any).req = req;
|
|
446
|
+
|
|
499
447
|
// BULLETPROOF status method - always works
|
|
500
448
|
httpRes.status = (code: number) => {
|
|
501
449
|
httpRes.statusCode = code;
|
|
@@ -505,21 +453,7 @@ export class MoroHttpServer {
|
|
|
505
453
|
httpRes.json = async (data: any) => {
|
|
506
454
|
if (httpRes.headersSent) return;
|
|
507
455
|
|
|
508
|
-
//
|
|
509
|
-
const cacheKey = this.getResponseCacheKey(data);
|
|
510
|
-
if (cacheKey) {
|
|
511
|
-
const cachedBuffer = this.responseCache.get(cacheKey);
|
|
512
|
-
if (cachedBuffer) {
|
|
513
|
-
this.responseCacheHits++;
|
|
514
|
-
httpRes.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|
515
|
-
httpRes.setHeader('Content-Length', cachedBuffer.length);
|
|
516
|
-
httpRes.end(cachedBuffer);
|
|
517
|
-
return;
|
|
518
|
-
}
|
|
519
|
-
this.responseCacheMisses++;
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
// Ultra-fast JSON serialization with zero-copy buffers
|
|
456
|
+
// JSON serialization with zero-copy buffers
|
|
523
457
|
let jsonString: string;
|
|
524
458
|
|
|
525
459
|
// Enhanced JSON optimization for common API patterns
|
|
@@ -606,11 +540,6 @@ export class MoroHttpServer {
|
|
|
606
540
|
|
|
607
541
|
httpRes.end(finalBuffer);
|
|
608
542
|
|
|
609
|
-
// PERFORMANCE OPTIMIZATION: Cache small, common responses
|
|
610
|
-
if (cacheKey && finalBuffer.length < 1024 && this.responseCache.size < 100) {
|
|
611
|
-
this.responseCache.set(cacheKey, Buffer.from(finalBuffer));
|
|
612
|
-
}
|
|
613
|
-
|
|
614
543
|
// Return buffer to pool after response (zero-copy achievement!)
|
|
615
544
|
process.nextTick(() => MoroHttpServer.returnBuffer(buffer));
|
|
616
545
|
};
|
|
@@ -915,12 +844,20 @@ export class MoroHttpServer {
|
|
|
915
844
|
return result;
|
|
916
845
|
}
|
|
917
846
|
|
|
847
|
+
// Legacy method for backward compatibility
|
|
918
848
|
private parseQueryString(queryString: string): Record<string, string> {
|
|
919
|
-
|
|
920
|
-
|
|
849
|
+
return this.parseQueryStringPooled(queryString);
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
// Optimized query string parser with object pooling
|
|
853
|
+
private parseQueryStringPooled(queryString: string): Record<string, string> {
|
|
854
|
+
if (!queryString) return {};
|
|
921
855
|
|
|
856
|
+
const result = this.poolManager.acquireQuery();
|
|
922
857
|
const pairs = queryString.split('&');
|
|
923
|
-
|
|
858
|
+
|
|
859
|
+
for (let i = 0; i < pairs.length; i++) {
|
|
860
|
+
const pair = pairs[i];
|
|
924
861
|
const equalIndex = pair.indexOf('=');
|
|
925
862
|
if (equalIndex === -1) {
|
|
926
863
|
result[decodeURIComponent(pair)] = '';
|
|
@@ -940,7 +877,7 @@ export class MoroHttpServer {
|
|
|
940
877
|
private routesBySegmentCount = new Map<number, RouteEntry[]>();
|
|
941
878
|
private pathNormalizationCache = new Map<string, string>();
|
|
942
879
|
|
|
943
|
-
//
|
|
880
|
+
// CPU cache-friendly optimizations
|
|
944
881
|
private routeHitCount = new Map<string, number>(); // Track route popularity for cache optimization
|
|
945
882
|
private static readonly HOT_ROUTE_THRESHOLD = 100; // Routes accessed 100+ times get hot path treatment
|
|
946
883
|
|
|
@@ -996,21 +933,25 @@ export class MoroHttpServer {
|
|
|
996
933
|
return route;
|
|
997
934
|
}
|
|
998
935
|
|
|
936
|
+
// Optimized middleware execution with reduced Promise allocation
|
|
999
937
|
private async executeMiddleware(
|
|
1000
938
|
middleware: Middleware[],
|
|
1001
939
|
req: HttpRequest,
|
|
1002
940
|
res: HttpResponse
|
|
1003
941
|
): Promise<void> {
|
|
1004
|
-
for (
|
|
942
|
+
for (let i = 0; i < middleware.length; i++) {
|
|
1005
943
|
// Short-circuit if response already sent
|
|
1006
944
|
if (res.headersSent) return;
|
|
1007
945
|
|
|
946
|
+
const mw = middleware[i];
|
|
947
|
+
|
|
1008
948
|
await new Promise<void>((resolve, reject) => {
|
|
1009
|
-
let
|
|
949
|
+
let resolved = false;
|
|
1010
950
|
|
|
951
|
+
// Reuse next function to reduce allocations
|
|
1011
952
|
const next = () => {
|
|
1012
|
-
if (
|
|
1013
|
-
|
|
953
|
+
if (resolved) return;
|
|
954
|
+
resolved = true;
|
|
1014
955
|
resolve();
|
|
1015
956
|
};
|
|
1016
957
|
|
|
@@ -1018,17 +959,21 @@ export class MoroHttpServer {
|
|
|
1018
959
|
const result = mw(req, res, next);
|
|
1019
960
|
|
|
1020
961
|
// Handle async middleware
|
|
1021
|
-
if (result
|
|
1022
|
-
result
|
|
962
|
+
if (result && typeof (result as any).then === 'function') {
|
|
963
|
+
(result as Promise<void>)
|
|
1023
964
|
.then(() => {
|
|
1024
|
-
if (!
|
|
965
|
+
if (!resolved) next();
|
|
1025
966
|
})
|
|
1026
|
-
.catch(
|
|
1027
|
-
|
|
1028
|
-
|
|
967
|
+
.catch(reject);
|
|
968
|
+
} else if (!resolved) {
|
|
969
|
+
// Sync middleware that didn't call next
|
|
970
|
+
next();
|
|
1029
971
|
}
|
|
1030
972
|
} catch (error) {
|
|
1031
|
-
|
|
973
|
+
if (!resolved) {
|
|
974
|
+
resolved = true;
|
|
975
|
+
reject(error);
|
|
976
|
+
}
|
|
1032
977
|
}
|
|
1033
978
|
});
|
|
1034
979
|
}
|
|
@@ -1065,44 +1010,14 @@ export class MoroHttpServer {
|
|
|
1065
1010
|
return this.server;
|
|
1066
1011
|
}
|
|
1067
1012
|
|
|
1068
|
-
// PERFORMANCE OPTIMIZATION: Generate cache key for common response patterns
|
|
1069
|
-
private getResponseCacheKey(data: any): string | null {
|
|
1070
|
-
// Only cache simple, common responses
|
|
1071
|
-
if (!data || typeof data !== 'object') {
|
|
1072
|
-
// Simple primitives or strings - generate key
|
|
1073
|
-
const key = JSON.stringify(data);
|
|
1074
|
-
return key.length < 100 ? key : null;
|
|
1075
|
-
}
|
|
1076
|
-
|
|
1077
|
-
// Common API response patterns
|
|
1078
|
-
if ('hello' in data && Object.keys(data).length <= 2) {
|
|
1079
|
-
// Hello world type responses
|
|
1080
|
-
return `hello:${JSON.stringify(data)}`;
|
|
1081
|
-
}
|
|
1082
|
-
|
|
1083
|
-
if ('status' in data && Object.keys(data).length <= 3) {
|
|
1084
|
-
// Status responses like {status: "ok", version: "1.0.0"}
|
|
1085
|
-
return `status:${JSON.stringify(data)}`;
|
|
1086
|
-
}
|
|
1087
|
-
|
|
1088
|
-
if ('success' in data && 'message' in data && Object.keys(data).length <= 3) {
|
|
1089
|
-
// Simple success/error responses
|
|
1090
|
-
return `msg:${data.success}:${data.message}`;
|
|
1091
|
-
}
|
|
1092
|
-
|
|
1093
|
-
// Don't cache complex objects
|
|
1094
|
-
return null;
|
|
1095
|
-
}
|
|
1096
|
-
|
|
1097
1013
|
// Performance statistics
|
|
1098
1014
|
getPerformanceStats() {
|
|
1015
|
+
const poolStats = this.poolManager.getStats();
|
|
1099
1016
|
return {
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
bufferPoolSize: this.bufferPool.length,
|
|
1105
|
-
middlewareExecutionCacheSize: this.middlewareExecutionCache.size,
|
|
1017
|
+
paramObjectPoolSize: poolStats.paramPool.poolSize,
|
|
1018
|
+
queryObjectPoolSize: poolStats.queryPool.poolSize,
|
|
1019
|
+
headerObjectPoolSize: poolStats.headerPool.poolSize,
|
|
1020
|
+
poolManager: poolStats,
|
|
1106
1021
|
};
|
|
1107
1022
|
}
|
|
1108
1023
|
}
|
package/src/core/http/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// HTTP System - Centralized Exports
|
|
2
2
|
export { MoroHttpServer, middleware } from './http-server.js';
|
|
3
3
|
export { UWebSocketsHttpServer } from './uws-http-server.js';
|
|
4
|
-
export { Router } from '
|
|
4
|
+
export { Router } from '../routing/router.js';
|
|
5
5
|
|
|
6
6
|
// Type exports
|
|
7
7
|
export type { HttpRequest, HttpResponse, HttpHandler, Middleware } from '../../types/http.js';
|