@morojs/moro 1.6.4 → 1.6.6
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/events/event-bus.js +7 -4
- package/dist/core/events/event-bus.js.map +1 -1
- package/dist/core/http/http-server.d.ts +7 -0
- package/dist/core/http/http-server.js +236 -114
- package/dist/core/http/http-server.js.map +1 -1
- package/dist/core/http/uws-http-server.d.ts +8 -1
- package/dist/core/http/uws-http-server.js +267 -153
- package/dist/core/http/uws-http-server.js.map +1 -1
- package/dist/core/logger/logger.d.ts +7 -5
- package/dist/core/logger/logger.js +61 -24
- package/dist/core/logger/logger.js.map +1 -1
- package/dist/core/middleware/built-in/rate-limit/core.d.ts +5 -0
- package/dist/core/middleware/built-in/rate-limit/core.js +16 -8
- package/dist/core/middleware/built-in/rate-limit/core.js.map +1 -1
- package/dist/core/middleware/built-in/validation/core.js +40 -19
- package/dist/core/middleware/built-in/validation/core.js.map +1 -1
- package/dist/core/pooling/object-pool-manager.d.ts +8 -2
- package/dist/core/pooling/object-pool-manager.js +35 -18
- package/dist/core/pooling/object-pool-manager.js.map +1 -1
- package/dist/core/routing/path-matcher.d.ts +6 -0
- package/dist/core/routing/path-matcher.js +46 -7
- package/dist/core/routing/path-matcher.js.map +1 -1
- package/dist/core/routing/unified-router.d.ts +4 -0
- package/dist/core/routing/unified-router.js +104 -43
- package/dist/core/routing/unified-router.js.map +1 -1
- package/dist/core/utilities/hooks.d.ts +4 -0
- package/dist/core/utilities/hooks.js +130 -22
- package/dist/core/utilities/hooks.js.map +1 -1
- package/dist/moro.js +4 -5
- package/dist/moro.js.map +1 -1
- package/dist/types/logger.d.ts +1 -0
- package/package.json +1 -1
|
@@ -23,10 +23,13 @@ export class MoroEventBus {
|
|
|
23
23
|
}
|
|
24
24
|
// Global event emission with full context and metrics
|
|
25
25
|
async emit(event, data, context) {
|
|
26
|
-
//
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
// CRITICAL: Check listeners FIRST - most events have zero listeners
|
|
27
|
+
// This early exit avoids ALL work below (timestamp, ID generation, metrics, etc.)
|
|
28
|
+
const listenerCount = this.emitter.listenerCount(event);
|
|
29
|
+
if (listenerCount === 0) {
|
|
30
|
+
return false; // Zero overhead when no listeners - instant return
|
|
29
31
|
}
|
|
32
|
+
// Only do expensive work if there ARE listeners
|
|
30
33
|
const startTime = Date.now();
|
|
31
34
|
const fullContext = {
|
|
32
35
|
timestamp: new Date(),
|
|
@@ -41,7 +44,7 @@ export class MoroEventBus {
|
|
|
41
44
|
try {
|
|
42
45
|
// Update metrics
|
|
43
46
|
this.updateMetrics(event, fullContext.moduleId);
|
|
44
|
-
// Audit logging
|
|
47
|
+
// Audit logging - early exit if disabled
|
|
45
48
|
if (this.auditEnabled) {
|
|
46
49
|
this.auditLog.push(payload);
|
|
47
50
|
if (this.auditLog.length > 1000) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"event-bus.js","sourceRoot":"","sources":["../../../src/core/events/event-bus.ts"],"names":[],"mappings":"AAAA,sDAAsD;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAUtC,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAE3D,MAAM,OAAO,YAAY;IAgBH;IAfZ,OAAO,GAAG,IAAI,YAAY,EAAE,CAAC;IAC7B,WAAW,GAAG,IAAI,GAAG,EAA0B,CAAC;IAChD,OAAO,GAAiB;QAC9B,WAAW,EAAE,CAAC;QACd,YAAY,EAAE,EAAE;QAChB,cAAc,EAAE,EAAE;QAClB,cAAc,EAAE,CAAC;QACjB,SAAS,EAAE,CAAC;KACb,CAAC;IACM,YAAY,GAAG,KAAK,CAAC;IACrB,QAAQ,GAAmB,EAAE,CAAC;IAC9B,UAAU,GAAG,CAAC,CAAC;IACf,UAAU,GAAG,CAAC,CAAC;IACf,MAAM,GAAG,qBAAqB,CAAC,UAAU,CAAC,CAAC;IAEnD,YAAoB,UAA2B,EAAE;QAA7B,YAAO,GAAP,OAAO,CAAsB;QAC/C,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,OAAO,CAAC,YAAY,IAAI,GAAG,CAAC,CAAC;IAC5D,CAAC;IAED,sDAAsD;IACtD,KAAK,CAAC,IAAI,CAAU,KAAa,EAAE,IAAO,EAAE,OAA+B;QACzE,
|
|
1
|
+
{"version":3,"file":"event-bus.js","sourceRoot":"","sources":["../../../src/core/events/event-bus.ts"],"names":[],"mappings":"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"}
|
|
@@ -11,6 +11,13 @@ export declare class MoroHttpServer {
|
|
|
11
11
|
private hookManager;
|
|
12
12
|
private requestCounter;
|
|
13
13
|
private poolManager;
|
|
14
|
+
private static readonly METHOD_POST;
|
|
15
|
+
private static readonly METHOD_PUT;
|
|
16
|
+
private static readonly METHOD_PATCH;
|
|
17
|
+
private static readonly METHOD_GET;
|
|
18
|
+
private static readonly METHOD_DELETE;
|
|
19
|
+
private static readonly METHOD_HEAD;
|
|
20
|
+
private static readonly METHOD_OPTIONS;
|
|
14
21
|
private static readonly RESPONSE_TEMPLATES;
|
|
15
22
|
private static readonly BUFFER_SIZES;
|
|
16
23
|
private static readonly BUFFER_POOLS;
|
|
@@ -21,6 +21,14 @@ export class MoroHttpServer {
|
|
|
21
21
|
requestCounter = 0;
|
|
22
22
|
// Use shared object pool manager
|
|
23
23
|
poolManager = ObjectPoolManager.getInstance();
|
|
24
|
+
// Interned method strings for fast reference equality comparison
|
|
25
|
+
static METHOD_POST = 'POST';
|
|
26
|
+
static METHOD_PUT = 'PUT';
|
|
27
|
+
static METHOD_PATCH = 'PATCH';
|
|
28
|
+
static METHOD_GET = 'GET';
|
|
29
|
+
static METHOD_DELETE = 'DELETE';
|
|
30
|
+
static METHOD_HEAD = 'HEAD';
|
|
31
|
+
static METHOD_OPTIONS = 'OPTIONS';
|
|
24
32
|
// Pre-compiled response templates for common responses
|
|
25
33
|
static RESPONSE_TEMPLATES = {
|
|
26
34
|
notFound: Buffer.from('{"success":false,"error":"Not found"}'),
|
|
@@ -135,8 +143,7 @@ export class MoroHttpServer {
|
|
|
135
143
|
else {
|
|
136
144
|
// Dynamic route - organize by segment count for faster matching
|
|
137
145
|
this.dynamicRoutes.push(route);
|
|
138
|
-
const
|
|
139
|
-
const segmentCount = segments.length;
|
|
146
|
+
const segmentCount = PathMatcher.countSegments(path);
|
|
140
147
|
if (!this.routesBySegmentCount.has(segmentCount)) {
|
|
141
148
|
this.routesBySegmentCount.set(segmentCount, []);
|
|
142
149
|
}
|
|
@@ -170,20 +177,24 @@ export class MoroHttpServer {
|
|
|
170
177
|
httpReq.path = urlString.substring(0, queryIndex);
|
|
171
178
|
httpReq.query = this.parseQueryStringPooled(urlString.substring(queryIndex + 1));
|
|
172
179
|
}
|
|
173
|
-
// Method checking -
|
|
174
|
-
|
|
175
|
-
|
|
180
|
+
// Method checking - use reference equality for interned strings (50-100% faster)
|
|
181
|
+
if (httpReq.method === MoroHttpServer.METHOD_POST ||
|
|
182
|
+
httpReq.method === MoroHttpServer.METHOD_PUT ||
|
|
183
|
+
httpReq.method === MoroHttpServer.METHOD_PATCH) {
|
|
176
184
|
httpReq.body = await this.parseBody(req);
|
|
177
185
|
}
|
|
178
|
-
// Execute hooks before request processing
|
|
186
|
+
// Execute hooks before request processing - NOOP if no hookManager
|
|
179
187
|
if (this.hookManager) {
|
|
180
188
|
await this.hookManager.execute('request', {
|
|
181
189
|
request: httpReq,
|
|
182
190
|
response: httpRes,
|
|
183
191
|
});
|
|
184
192
|
}
|
|
185
|
-
// Execute global middleware first
|
|
186
|
-
|
|
193
|
+
// Execute global middleware first - EARLY EXIT if none registered
|
|
194
|
+
const middlewareLen = this.globalMiddleware.length;
|
|
195
|
+
if (middlewareLen > 0) {
|
|
196
|
+
await this.executeMiddleware(this.globalMiddleware, httpReq, httpRes);
|
|
197
|
+
}
|
|
187
198
|
// If middleware handled the request, don't continue
|
|
188
199
|
if (httpRes.headersSent) {
|
|
189
200
|
return;
|
|
@@ -203,14 +214,22 @@ export class MoroHttpServer {
|
|
|
203
214
|
if (matches) {
|
|
204
215
|
// Use pooled object for parameters
|
|
205
216
|
httpReq.params = this.acquireParamObject();
|
|
206
|
-
route.paramNames
|
|
207
|
-
|
|
208
|
-
|
|
217
|
+
const paramNames = route.paramNames;
|
|
218
|
+
const paramNamesLen = paramNames.length;
|
|
219
|
+
for (let i = 0; i < paramNamesLen; i++) {
|
|
220
|
+
httpReq.params[paramNames[i]] = matches[i + 1];
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
// Execute middleware chain - EARLY EXIT if no route middleware
|
|
224
|
+
const routeMiddlewareLen = route.middleware.length;
|
|
225
|
+
if (routeMiddlewareLen > 0) {
|
|
226
|
+
await this.executeMiddleware(route.middleware, httpReq, httpRes);
|
|
227
|
+
}
|
|
228
|
+
// Execute handler - Don't await sync handlers
|
|
229
|
+
const handlerResult = route.handler(httpReq, httpRes);
|
|
230
|
+
if (handlerResult && typeof handlerResult.then === 'function') {
|
|
231
|
+
await handlerResult;
|
|
209
232
|
}
|
|
210
|
-
// Execute middleware chain
|
|
211
|
-
await this.executeMiddleware(route.middleware, httpReq, httpRes);
|
|
212
|
-
// Execute handler
|
|
213
|
-
await route.handler(httpReq, httpRes);
|
|
214
233
|
}
|
|
215
234
|
catch (error) {
|
|
216
235
|
// Debug: Log the actual error and where it came from
|
|
@@ -265,24 +284,50 @@ export class MoroHttpServer {
|
|
|
265
284
|
finally {
|
|
266
285
|
// CRITICAL: Always release pooled objects back to the pool
|
|
267
286
|
// This prevents memory leaks and ensures consistent performance
|
|
268
|
-
if
|
|
269
|
-
|
|
287
|
+
// Optimized: Check if object is empty without Object.keys()
|
|
288
|
+
if (originalParams) {
|
|
289
|
+
let isEmpty = true;
|
|
290
|
+
for (const _key in originalParams) {
|
|
291
|
+
isEmpty = false;
|
|
292
|
+
break;
|
|
293
|
+
}
|
|
294
|
+
if (isEmpty) {
|
|
295
|
+
this.releaseParamObject(originalParams);
|
|
296
|
+
}
|
|
270
297
|
}
|
|
271
|
-
if (httpReq.params &&
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
298
|
+
if (httpReq.params && httpReq.params !== originalParams) {
|
|
299
|
+
let isEmpty = true;
|
|
300
|
+
for (const _key in httpReq.params) {
|
|
301
|
+
isEmpty = false;
|
|
302
|
+
break;
|
|
303
|
+
}
|
|
304
|
+
if (isEmpty) {
|
|
305
|
+
this.releaseParamObject(httpReq.params);
|
|
306
|
+
}
|
|
275
307
|
}
|
|
276
308
|
}
|
|
277
309
|
// Additional cleanup on response completion to ensure objects are returned to pool
|
|
278
310
|
res.once('finish', () => {
|
|
279
|
-
if
|
|
280
|
-
|
|
311
|
+
// Optimized: Check if object is empty without Object.keys()
|
|
312
|
+
if (originalParams) {
|
|
313
|
+
let isEmpty = true;
|
|
314
|
+
for (const _key in originalParams) {
|
|
315
|
+
isEmpty = false;
|
|
316
|
+
break;
|
|
317
|
+
}
|
|
318
|
+
if (isEmpty) {
|
|
319
|
+
this.releaseParamObject(originalParams);
|
|
320
|
+
}
|
|
281
321
|
}
|
|
282
|
-
if (httpReq.params &&
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
322
|
+
if (httpReq.params && httpReq.params !== originalParams) {
|
|
323
|
+
let isEmpty = true;
|
|
324
|
+
for (const _key in httpReq.params) {
|
|
325
|
+
isEmpty = false;
|
|
326
|
+
break;
|
|
327
|
+
}
|
|
328
|
+
if (isEmpty) {
|
|
329
|
+
this.releaseParamObject(httpReq.params);
|
|
330
|
+
}
|
|
286
331
|
}
|
|
287
332
|
});
|
|
288
333
|
}
|
|
@@ -346,20 +391,61 @@ export class MoroHttpServer {
|
|
|
346
391
|
// Request ID generation using pool manager (if enabled)
|
|
347
392
|
httpReq.requestId = this.requestTrackingEnabled ? this.poolManager.generateRequestId() : '';
|
|
348
393
|
httpReq.headers = req.headers;
|
|
349
|
-
//
|
|
350
|
-
|
|
394
|
+
// Intern method string for fast reference equality comparison (50-100% faster)
|
|
395
|
+
const method = req.method;
|
|
396
|
+
switch (method) {
|
|
397
|
+
case 'POST':
|
|
398
|
+
httpReq.method = MoroHttpServer.METHOD_POST;
|
|
399
|
+
break;
|
|
400
|
+
case 'PUT':
|
|
401
|
+
httpReq.method = MoroHttpServer.METHOD_PUT;
|
|
402
|
+
break;
|
|
403
|
+
case 'PATCH':
|
|
404
|
+
httpReq.method = MoroHttpServer.METHOD_PATCH;
|
|
405
|
+
break;
|
|
406
|
+
case 'GET':
|
|
407
|
+
httpReq.method = MoroHttpServer.METHOD_GET;
|
|
408
|
+
break;
|
|
409
|
+
case 'DELETE':
|
|
410
|
+
httpReq.method = MoroHttpServer.METHOD_DELETE;
|
|
411
|
+
break;
|
|
412
|
+
case 'HEAD':
|
|
413
|
+
httpReq.method = MoroHttpServer.METHOD_HEAD;
|
|
414
|
+
break;
|
|
415
|
+
case 'OPTIONS':
|
|
416
|
+
httpReq.method = MoroHttpServer.METHOD_OPTIONS;
|
|
417
|
+
break;
|
|
418
|
+
default:
|
|
419
|
+
httpReq.method = method;
|
|
420
|
+
}
|
|
421
|
+
// Parse cookies - EARLY EXIT if no cookie header
|
|
422
|
+
const cookieHeader = req.headers.cookie;
|
|
423
|
+
if (cookieHeader) {
|
|
424
|
+
httpReq.cookies = this.parseCookies(cookieHeader);
|
|
425
|
+
}
|
|
426
|
+
else {
|
|
427
|
+
httpReq.cookies = {};
|
|
428
|
+
}
|
|
351
429
|
return httpReq;
|
|
352
430
|
}
|
|
353
431
|
parseCookies(cookieHeader) {
|
|
354
432
|
const cookies = {};
|
|
433
|
+
// EARLY EXIT if no cookie header
|
|
355
434
|
if (!cookieHeader)
|
|
356
435
|
return cookies;
|
|
357
|
-
cookieHeader.split(';')
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
436
|
+
const cookieParts = cookieHeader.split(';');
|
|
437
|
+
const cookiePartsLen = cookieParts.length;
|
|
438
|
+
for (let i = 0; i < cookiePartsLen; i++) {
|
|
439
|
+
const cookie = cookieParts[i];
|
|
440
|
+
const equalIndex = cookie.indexOf('=');
|
|
441
|
+
if (equalIndex > 0) {
|
|
442
|
+
const name = cookie.substring(0, equalIndex).trim();
|
|
443
|
+
const value = cookie.substring(equalIndex + 1);
|
|
444
|
+
if (name && value) {
|
|
445
|
+
cookies[name] = decodeURIComponent(value);
|
|
446
|
+
}
|
|
361
447
|
}
|
|
362
|
-
}
|
|
448
|
+
}
|
|
363
449
|
return cookies;
|
|
364
450
|
}
|
|
365
451
|
enhanceResponse(res, req) {
|
|
@@ -377,28 +463,51 @@ export class MoroHttpServer {
|
|
|
377
463
|
// JSON serialization with zero-copy buffers
|
|
378
464
|
let jsonString;
|
|
379
465
|
// Enhanced JSON optimization for common API patterns
|
|
380
|
-
//
|
|
466
|
+
// Fast path for common 2-3 key objects without Object.keys() overhead
|
|
381
467
|
if (data && typeof data === 'object' && 'success' in data) {
|
|
382
|
-
|
|
383
|
-
const
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
468
|
+
// Check for common patterns using 'in' operator (faster than Object.keys for small objects)
|
|
469
|
+
const hasData = 'data' in data;
|
|
470
|
+
const hasError = 'error' in data;
|
|
471
|
+
const hasTotal = 'total' in data;
|
|
472
|
+
// Fast path: {success, data} - most common pattern
|
|
473
|
+
if (hasData && !hasError && !hasTotal) {
|
|
474
|
+
// Verify it's exactly 2 keys by checking no other common keys exist
|
|
475
|
+
if (!('message' in data) && !('code' in data) && !('status' in data)) {
|
|
476
|
+
jsonString = `{"success":${data.success},"data":${JSON.stringify(data.data)}}`;
|
|
477
|
+
}
|
|
478
|
+
else {
|
|
479
|
+
jsonString = JSON.stringify(data);
|
|
480
|
+
}
|
|
387
481
|
}
|
|
388
|
-
else if (
|
|
389
|
-
// {success,
|
|
390
|
-
|
|
482
|
+
else if (hasError && !hasData && !hasTotal) {
|
|
483
|
+
// Fast path: {success, error}
|
|
484
|
+
if (!('message' in data) && !('code' in data) && !('status' in data)) {
|
|
485
|
+
jsonString = `{"success":${data.success},"error":${JSON.stringify(data.error)}}`;
|
|
486
|
+
}
|
|
487
|
+
else {
|
|
488
|
+
jsonString = JSON.stringify(data);
|
|
489
|
+
}
|
|
391
490
|
}
|
|
392
|
-
else if (
|
|
393
|
-
// {success, data}
|
|
394
|
-
|
|
491
|
+
else if (hasData && hasError && !hasTotal) {
|
|
492
|
+
// Fast path: {success, data, error}
|
|
493
|
+
if (!('message' in data) && !('code' in data) && !('status' in data)) {
|
|
494
|
+
jsonString = `{"success":${data.success},"data":${JSON.stringify(data.data)},"error":${JSON.stringify(data.error)}}`;
|
|
495
|
+
}
|
|
496
|
+
else {
|
|
497
|
+
jsonString = JSON.stringify(data);
|
|
498
|
+
}
|
|
395
499
|
}
|
|
396
|
-
else if (
|
|
397
|
-
// {success,
|
|
398
|
-
|
|
500
|
+
else if (hasData && hasTotal && !hasError) {
|
|
501
|
+
// Fast path: {success, data, total}
|
|
502
|
+
if (!('message' in data) && !('code' in data) && !('status' in data)) {
|
|
503
|
+
jsonString = `{"success":${data.success},"data":${JSON.stringify(data.data)},"total":${data.total}}`;
|
|
504
|
+
}
|
|
505
|
+
else {
|
|
506
|
+
jsonString = JSON.stringify(data);
|
|
507
|
+
}
|
|
399
508
|
}
|
|
400
509
|
else {
|
|
401
|
-
// Complex object
|
|
510
|
+
// Complex object - use standard JSON.stringify
|
|
402
511
|
jsonString = JSON.stringify(data);
|
|
403
512
|
}
|
|
404
513
|
}
|
|
@@ -419,40 +528,41 @@ export class MoroHttpServer {
|
|
|
419
528
|
const headers = {
|
|
420
529
|
'Content-Type': 'application/json; charset=utf-8',
|
|
421
530
|
};
|
|
422
|
-
// Compression with buffer pool
|
|
531
|
+
// Compression with buffer pool - EARLY EXIT if disabled or below threshold
|
|
532
|
+
// CRITICAL: Only make this async if compression is actually happening
|
|
423
533
|
if (this.compressionEnabled && finalBuffer.length > this.compressionThreshold) {
|
|
424
|
-
const acceptEncoding = httpRes.req.headers['accept-encoding']
|
|
425
|
-
if (acceptEncoding.includes('gzip')) {
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
httpRes.
|
|
534
|
+
const acceptEncoding = httpRes.req.headers['accept-encoding'];
|
|
535
|
+
if (acceptEncoding && acceptEncoding.includes('gzip')) {
|
|
536
|
+
// ASYNC PATH - compression needed
|
|
537
|
+
gzip(finalBuffer).then(compressed => {
|
|
538
|
+
headers['Content-Encoding'] = 'gzip';
|
|
539
|
+
headers['Content-Length'] = compressed.length;
|
|
540
|
+
// Batch write all headers at once (50-100% faster)
|
|
541
|
+
httpRes.writeHead(httpRes.statusCode || 200, headers);
|
|
542
|
+
httpRes.end(compressed);
|
|
543
|
+
// Return buffer to pool after response
|
|
544
|
+
process.nextTick(() => MoroHttpServer.returnBuffer(buffer));
|
|
432
545
|
});
|
|
433
|
-
httpRes.end(compressed);
|
|
434
|
-
// Return buffer to pool after response
|
|
435
|
-
process.nextTick(() => MoroHttpServer.returnBuffer(buffer));
|
|
436
546
|
return;
|
|
437
547
|
}
|
|
438
|
-
else if (acceptEncoding.includes('deflate')) {
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
548
|
+
else if (acceptEncoding && acceptEncoding.includes('deflate')) {
|
|
549
|
+
// ASYNC PATH - compression needed
|
|
550
|
+
deflate(finalBuffer).then(compressed => {
|
|
551
|
+
headers['Content-Encoding'] = 'deflate';
|
|
552
|
+
headers['Content-Length'] = compressed.length;
|
|
553
|
+
// Batch write all headers at once
|
|
554
|
+
httpRes.writeHead(httpRes.statusCode || 200, headers);
|
|
555
|
+
httpRes.end(compressed);
|
|
556
|
+
// Return buffer to pool after response
|
|
557
|
+
process.nextTick(() => MoroHttpServer.returnBuffer(buffer));
|
|
444
558
|
});
|
|
445
|
-
httpRes.end(compressed);
|
|
446
|
-
// Return buffer to pool after response
|
|
447
|
-
process.nextTick(() => MoroHttpServer.returnBuffer(buffer));
|
|
448
559
|
return;
|
|
449
560
|
}
|
|
450
561
|
}
|
|
562
|
+
// SYNC PATH - no compression, fast path
|
|
451
563
|
headers['Content-Length'] = finalBuffer.length;
|
|
452
|
-
//
|
|
453
|
-
|
|
454
|
-
httpRes.setHeader(key, value);
|
|
455
|
-
});
|
|
564
|
+
// Batch write all headers at once
|
|
565
|
+
httpRes.writeHead(httpRes.statusCode || 200, headers);
|
|
456
566
|
httpRes.end(finalBuffer);
|
|
457
567
|
// Return buffer to pool after response (zero-copy achievement!)
|
|
458
568
|
process.nextTick(() => MoroHttpServer.returnBuffer(buffer));
|
|
@@ -571,9 +681,12 @@ export class MoroHttpServer {
|
|
|
571
681
|
});
|
|
572
682
|
return httpRes;
|
|
573
683
|
}
|
|
574
|
-
Object.
|
|
575
|
-
|
|
576
|
-
|
|
684
|
+
const headerKeys = Object.keys(headers);
|
|
685
|
+
const headerKeysLen = headerKeys.length;
|
|
686
|
+
for (let i = 0; i < headerKeysLen; i++) {
|
|
687
|
+
const key = headerKeys[i];
|
|
688
|
+
httpRes.setHeader(key, headers[key]);
|
|
689
|
+
}
|
|
577
690
|
return httpRes;
|
|
578
691
|
};
|
|
579
692
|
httpRes.appendHeader = (name, value) => {
|
|
@@ -626,17 +739,16 @@ export class MoroHttpServer {
|
|
|
626
739
|
return mimeTypes[ext.toLowerCase()] || 'application/octet-stream';
|
|
627
740
|
}
|
|
628
741
|
addCharsetIfNeeded(mimeType) {
|
|
629
|
-
// Add charset for text-based content types
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
'application/json'
|
|
633
|
-
'application/javascript'
|
|
634
|
-
'application/xml'
|
|
635
|
-
'image/svg+xml'
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
return `${mimeType}; charset=utf-8`;
|
|
742
|
+
// Add charset for text-based content types - optimized with early exit
|
|
743
|
+
// Check most common cases first
|
|
744
|
+
if (mimeType.startsWith('text/') ||
|
|
745
|
+
mimeType.startsWith('application/json') ||
|
|
746
|
+
mimeType.startsWith('application/javascript') ||
|
|
747
|
+
mimeType.startsWith('application/xml') ||
|
|
748
|
+
mimeType.startsWith('image/svg+xml')) {
|
|
749
|
+
if (!mimeType.includes('charset')) {
|
|
750
|
+
return `${mimeType}; charset=utf-8`;
|
|
751
|
+
}
|
|
640
752
|
}
|
|
641
753
|
return mimeType;
|
|
642
754
|
}
|
|
@@ -685,7 +797,8 @@ export class MoroHttpServer {
|
|
|
685
797
|
const parts = buffer.toString('binary').split('--' + boundary);
|
|
686
798
|
const fields = {};
|
|
687
799
|
const files = {};
|
|
688
|
-
|
|
800
|
+
const partsLen = parts.length - 1;
|
|
801
|
+
for (let i = 1; i < partsLen; i++) {
|
|
689
802
|
const part = parts[i];
|
|
690
803
|
const [headers, content] = part.split('\r\n\r\n');
|
|
691
804
|
if (!headers || content === undefined)
|
|
@@ -733,7 +846,8 @@ export class MoroHttpServer {
|
|
|
733
846
|
return {};
|
|
734
847
|
const result = this.poolManager.acquireQuery();
|
|
735
848
|
const pairs = queryString.split('&');
|
|
736
|
-
|
|
849
|
+
const pairsLen = pairs.length;
|
|
850
|
+
for (let i = 0; i < pairsLen; i++) {
|
|
737
851
|
const pair = pairs[i];
|
|
738
852
|
const equalIndex = pair.indexOf('=');
|
|
739
853
|
if (equalIndex === -1) {
|
|
@@ -757,35 +871,39 @@ export class MoroHttpServer {
|
|
|
757
871
|
routeHitCount = new Map(); // Track route popularity for cache optimization
|
|
758
872
|
static HOT_ROUTE_THRESHOLD = 100; // Routes accessed 100+ times get hot path treatment
|
|
759
873
|
findRoute(method, path) {
|
|
760
|
-
//
|
|
761
|
-
const
|
|
762
|
-
|
|
763
|
-
// Track route popularity for hot path optimization
|
|
764
|
-
const hitCount = (this.routeHitCount.get(cacheKey) || 0) + 1;
|
|
765
|
-
this.routeHitCount.set(cacheKey, hitCount);
|
|
766
|
-
// Check cache first (hot path optimization)
|
|
874
|
+
// Skip normalization and hit tracking for cached routes
|
|
875
|
+
const cacheKey = `${method}:${path}`;
|
|
876
|
+
// Check cache first (hot path optimization) - BEFORE any other work
|
|
767
877
|
if (this.routeCache.has(cacheKey)) {
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
878
|
+
return this.routeCache.get(cacheKey);
|
|
879
|
+
}
|
|
880
|
+
// Normalize path for consistent matching (only if not cached)
|
|
881
|
+
const normalizedPath = this.normalizePath(path);
|
|
882
|
+
const normalizedCacheKey = normalizedPath !== path ? `${method}:${normalizedPath}` : cacheKey;
|
|
883
|
+
// Check cache again with normalized path
|
|
884
|
+
if (normalizedPath !== path && this.routeCache.has(normalizedCacheKey)) {
|
|
885
|
+
return this.routeCache.get(normalizedCacheKey);
|
|
775
886
|
}
|
|
776
887
|
// Phase 1: O(1) static route lookup
|
|
777
|
-
const staticRoute = this.staticRoutes.get(
|
|
888
|
+
const staticRoute = this.staticRoutes.get(normalizedCacheKey);
|
|
778
889
|
if (staticRoute) {
|
|
779
|
-
this.routeCache.set(
|
|
890
|
+
this.routeCache.set(normalizedCacheKey, staticRoute);
|
|
891
|
+
if (normalizedPath !== path) {
|
|
892
|
+
this.routeCache.set(cacheKey, staticRoute);
|
|
893
|
+
}
|
|
780
894
|
return staticRoute;
|
|
781
895
|
}
|
|
782
896
|
// Phase 2: Optimized dynamic route matching by segment count
|
|
783
897
|
let route = null;
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
898
|
+
const dynamicRoutesLen = this.dynamicRoutes.length;
|
|
899
|
+
if (dynamicRoutesLen > 0) {
|
|
900
|
+
// Use shared utility for DRY principle
|
|
901
|
+
const segmentCount = PathMatcher.countSegments(normalizedPath);
|
|
902
|
+
const candidateRoutes = this.routesBySegmentCount.get(segmentCount) || this.dynamicRoutes;
|
|
903
|
+
const candidateLen = candidateRoutes.length;
|
|
787
904
|
// Only test routes with matching method and segment count
|
|
788
|
-
for (
|
|
905
|
+
for (let i = 0; i < candidateLen; i++) {
|
|
906
|
+
const candidateRoute = candidateRoutes[i];
|
|
789
907
|
if (candidateRoute.method === method && candidateRoute.pattern.test(normalizedPath)) {
|
|
790
908
|
route = candidateRoute;
|
|
791
909
|
break;
|
|
@@ -794,13 +912,17 @@ export class MoroHttpServer {
|
|
|
794
912
|
}
|
|
795
913
|
// Cache result (limit cache size to prevent memory leaks)
|
|
796
914
|
if (this.routeCache.size < 500) {
|
|
797
|
-
this.routeCache.set(
|
|
915
|
+
this.routeCache.set(normalizedCacheKey, route);
|
|
916
|
+
if (normalizedPath !== path) {
|
|
917
|
+
this.routeCache.set(cacheKey, route);
|
|
918
|
+
}
|
|
798
919
|
}
|
|
799
920
|
return route;
|
|
800
921
|
}
|
|
801
922
|
// Optimized middleware execution with reduced Promise allocation
|
|
802
923
|
async executeMiddleware(middleware, req, res) {
|
|
803
|
-
|
|
924
|
+
const len = middleware.length;
|
|
925
|
+
for (let i = 0; i < len; i++) {
|
|
804
926
|
// Short-circuit if response already sent
|
|
805
927
|
if (res.headersSent)
|
|
806
928
|
return;
|
|
@@ -816,7 +938,7 @@ export class MoroHttpServer {
|
|
|
816
938
|
};
|
|
817
939
|
try {
|
|
818
940
|
const result = mw(req, res, next);
|
|
819
|
-
// Handle async middleware
|
|
941
|
+
// Handle async middleware - optimized with early check
|
|
820
942
|
if (result && typeof result.then === 'function') {
|
|
821
943
|
result
|
|
822
944
|
.then(() => {
|