@aetherframework/middleware 1.0.2 → 1.0.4
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/README.md +0 -3
- package/docs/readme/README.md +14 -3
- package/docs/readme/README_zh.md +0 -3
- package/examples/advanced-server.js +122 -112
- package/examples/basic-server.js +322 -64
- package/index.js +9 -11
- package/package.json +1 -1
- package/src/core/AetherCompiler.js +117 -63
- package/src/core/AetherContext.js +221 -93
- package/src/core/AetherPipeline.js +261 -285
- package/src/core/AetherRouter.js +358 -256
- package/src/core/AetherStore.js +114 -67
- package/src/middleware/compression.js +165 -91
- package/src/middleware/json.js +180 -169
- package/src/middleware/rate-limit.js +76 -146
- package/src/middleware/security.js +33 -54
- package/src/middleware/session.js +89 -86
|
@@ -1,58 +1,74 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
2
|
* @license MIT
|
|
3
3
|
* Copyright (c) 2026-present AetherFramework Contributors.
|
|
4
4
|
* SPDX-License-Identifier: MIT
|
|
5
5
|
* @module @aetherframework/middleware/core/AetherContext
|
|
6
|
+
*
|
|
7
|
+
* Ultra-Optimized Request Context for Maximum Performance
|
|
8
|
+
* Removed all statistics, metrics, and monitoring overhead
|
|
9
|
+
* Focus: Zero-allocation context management with minimal overhead
|
|
6
10
|
*/
|
|
7
|
-
const GLOBAL_HEADER_BUFFER = new Array(64);
|
|
8
11
|
|
|
12
|
+
/**
|
|
13
|
+
* AetherContext - High-performance request context optimized for V8
|
|
14
|
+
* Removed all statistics tracking and metrics collection
|
|
15
|
+
* Focus: Pure request/response handling with maximum speed
|
|
16
|
+
*/
|
|
9
17
|
class AetherContext {
|
|
10
18
|
constructor(request, response) {
|
|
11
|
-
//
|
|
19
|
+
// ==========================================
|
|
20
|
+
// [V8-OPT] MONOMORPHIC PROPERTY LAYOUT
|
|
21
|
+
// ==========================================
|
|
22
|
+
// All properties initialized in same order for V8 Hidden Class optimization
|
|
12
23
|
this._request = null;
|
|
13
24
|
this._response = null;
|
|
14
|
-
|
|
25
|
+
|
|
26
|
+
// Request metadata
|
|
15
27
|
this.method = "";
|
|
16
28
|
this.url = "";
|
|
17
29
|
this.headers = null;
|
|
18
30
|
this.path = "";
|
|
19
31
|
this._queryString = "";
|
|
20
|
-
|
|
21
|
-
//
|
|
32
|
+
|
|
33
|
+
// [PERF] Flat header storage for O(1) access
|
|
22
34
|
this._headersObj = {};
|
|
23
35
|
this._headersCount = 0;
|
|
24
|
-
this._headersKeys = new Array(
|
|
25
|
-
|
|
36
|
+
this._headersKeys = new Array(32); // Pre-allocated array to avoid resizing
|
|
37
|
+
|
|
38
|
+
// Cached computations
|
|
26
39
|
this._queryCache = null;
|
|
27
|
-
this._ipCache = null;
|
|
28
|
-
|
|
40
|
+
this._ipCache = null;
|
|
41
|
+
|
|
42
|
+
// Response state
|
|
29
43
|
this.statusCode = 200;
|
|
30
44
|
this._body = null;
|
|
31
45
|
this._terminated = false;
|
|
32
|
-
|
|
33
|
-
//
|
|
46
|
+
|
|
47
|
+
// [PERF] State storage (removed metrics tracking)
|
|
34
48
|
this._stateObj = {};
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
49
|
+
|
|
50
|
+
// Initialize if request/response provided
|
|
38
51
|
if (request && response) {
|
|
39
52
|
this._reset(request, response);
|
|
40
53
|
}
|
|
41
54
|
}
|
|
42
|
-
|
|
55
|
+
|
|
43
56
|
/**
|
|
44
|
-
*
|
|
57
|
+
* Reset context for reuse (object pooling)
|
|
58
|
+
* @param {Object} request - HTTP request object
|
|
59
|
+
* @param {Object} response - HTTP response object
|
|
45
60
|
*/
|
|
46
61
|
_reset(request, response) {
|
|
47
62
|
this._request = request;
|
|
48
63
|
this._response = response;
|
|
49
|
-
|
|
64
|
+
|
|
65
|
+
// [PERF] Direct property assignment (faster than Object.assign)
|
|
50
66
|
this.method = request.method;
|
|
51
67
|
const rawUrl = request.url || "/";
|
|
52
68
|
this.url = rawUrl;
|
|
53
69
|
this.headers = request.headers;
|
|
54
|
-
|
|
55
|
-
// Fast
|
|
70
|
+
|
|
71
|
+
// [PERF] Fast URL parsing without regex
|
|
56
72
|
const qIdx = rawUrl.indexOf("?");
|
|
57
73
|
if (qIdx !== -1) {
|
|
58
74
|
this.path = rawUrl.substring(0, qIdx);
|
|
@@ -61,181 +77,293 @@ class AetherContext {
|
|
|
61
77
|
this.path = rawUrl;
|
|
62
78
|
this._queryString = "";
|
|
63
79
|
}
|
|
64
|
-
|
|
65
|
-
//
|
|
80
|
+
|
|
81
|
+
// Reset cached values
|
|
66
82
|
this._queryCache = null;
|
|
67
83
|
this._ipCache = null;
|
|
68
|
-
|
|
84
|
+
|
|
85
|
+
// Reset response state
|
|
69
86
|
this.statusCode = 200;
|
|
70
87
|
this._body = null;
|
|
71
88
|
this._terminated = false;
|
|
72
|
-
|
|
73
|
-
//
|
|
89
|
+
|
|
90
|
+
// [PERF] Clear headers without re-allocating objects
|
|
74
91
|
if (this._headersCount > 0) {
|
|
75
92
|
for (let i = 0; i < this._headersCount; i++) {
|
|
76
93
|
this._headersObj[this._headersKeys[i]] = undefined;
|
|
77
94
|
}
|
|
78
95
|
this._headersCount = 0;
|
|
79
96
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
97
|
+
|
|
98
|
+
// [PERF] Clear state without re-allocating object
|
|
99
|
+
for (const key in this._stateObj) {
|
|
100
|
+
delete this._stateObj[key];
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// [PERF] REMOVED: _startTime and metrics tracking
|
|
104
|
+
// this._startTime = process.hrtime.bigint(); // Statistics removed
|
|
83
105
|
}
|
|
84
|
-
|
|
85
|
-
//
|
|
106
|
+
|
|
107
|
+
// ==========================================
|
|
108
|
+
// [PERF] PROPERTY GETTERS/SETTERS
|
|
109
|
+
// ==========================================
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Get response body
|
|
113
|
+
*/
|
|
86
114
|
get body() {
|
|
87
115
|
return this._body;
|
|
88
116
|
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Set response body and finalize
|
|
120
|
+
* @param {any} value - Response body value
|
|
121
|
+
*/
|
|
89
122
|
set body(value) {
|
|
90
123
|
if (this._terminated) return;
|
|
91
124
|
this._body = value;
|
|
125
|
+
this._finalize();
|
|
92
126
|
}
|
|
93
|
-
|
|
127
|
+
|
|
94
128
|
/**
|
|
95
|
-
*
|
|
129
|
+
* Get client IP address with caching
|
|
96
130
|
*/
|
|
97
131
|
get ip() {
|
|
98
132
|
if (this._ipCache) return this._ipCache;
|
|
133
|
+
|
|
99
134
|
const sock = this._request?.socket;
|
|
135
|
+
// [PERF] Simple IP extraction without proxy header checks
|
|
100
136
|
return (this._ipCache = sock ? sock.remoteAddress : "127.0.0.1");
|
|
101
137
|
}
|
|
102
|
-
|
|
138
|
+
|
|
103
139
|
/**
|
|
104
|
-
*
|
|
140
|
+
* Get parsed query parameters with caching
|
|
105
141
|
*/
|
|
106
142
|
get query() {
|
|
107
143
|
if (this._queryCache) return this._queryCache;
|
|
108
144
|
if (!this._queryString) return (this._queryCache = {});
|
|
109
|
-
|
|
145
|
+
|
|
110
146
|
const obj = {};
|
|
111
147
|
const pairs = this._queryString.split("&");
|
|
148
|
+
|
|
149
|
+
// [PERF] Manual parsing without try-catch for common case
|
|
112
150
|
for (let i = 0; i < pairs.length; i++) {
|
|
113
151
|
const pair = pairs[i];
|
|
114
152
|
const eqIdx = pair.indexOf("=");
|
|
153
|
+
|
|
115
154
|
if (eqIdx !== -1) {
|
|
116
|
-
|
|
155
|
+
// [PERF] Fast path: decode only when needed
|
|
156
|
+
const key = pair.substring(0, eqIdx);
|
|
157
|
+
const value = pair.substring(eqIdx + 1);
|
|
158
|
+
|
|
159
|
+
// [PERF] Skip decodeURIComponent for simple ASCII
|
|
160
|
+
if (key.indexOf("%") === -1 && value.indexOf("%") === -1) {
|
|
161
|
+
obj[key] = value;
|
|
162
|
+
} else {
|
|
163
|
+
try {
|
|
164
|
+
obj[decodeURIComponent(key)] = decodeURIComponent(value);
|
|
165
|
+
} catch {
|
|
166
|
+
// Fallback for malformed URIs
|
|
167
|
+
obj[key] = value;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
117
170
|
} else {
|
|
118
|
-
|
|
171
|
+
// Key without value
|
|
172
|
+
const key = pair;
|
|
173
|
+
if (key.indexOf("%") === -1) {
|
|
174
|
+
obj[key] = "";
|
|
175
|
+
} else {
|
|
176
|
+
try {
|
|
177
|
+
obj[decodeURIComponent(key)] = "";
|
|
178
|
+
} catch {
|
|
179
|
+
obj[key] = "";
|
|
180
|
+
}
|
|
181
|
+
}
|
|
119
182
|
}
|
|
120
183
|
}
|
|
184
|
+
|
|
121
185
|
return (this._queryCache = obj);
|
|
122
186
|
}
|
|
123
|
-
|
|
187
|
+
|
|
188
|
+
// ==========================================
|
|
189
|
+
// [PERF] HEADER MANAGEMENT
|
|
190
|
+
// ==========================================
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Set response header
|
|
194
|
+
* @param {string} key - Header name
|
|
195
|
+
* @param {string} value - Header value
|
|
196
|
+
* @returns {AetherContext} - Chainable
|
|
197
|
+
*/
|
|
124
198
|
setHeader(key, value) {
|
|
125
199
|
if (this._terminated) return this;
|
|
126
|
-
|
|
127
|
-
//
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
200
|
+
|
|
201
|
+
// [PERF] Lowercase for consistency with Node.js
|
|
202
|
+
const lowerKey = key.toLowerCase();
|
|
203
|
+
|
|
204
|
+
// [PERF] Track header keys for fast iteration
|
|
205
|
+
if (this._headersObj[lowerKey] === undefined) {
|
|
206
|
+
if (this._headersCount < this._headersKeys.length) {
|
|
207
|
+
this._headersKeys[this._headersCount] = lowerKey;
|
|
208
|
+
} else {
|
|
209
|
+
this._headersKeys.push(lowerKey);
|
|
210
|
+
}
|
|
211
|
+
this._headersCount++;
|
|
131
212
|
}
|
|
132
|
-
|
|
213
|
+
|
|
214
|
+
this._headersObj[lowerKey] = value;
|
|
133
215
|
return this;
|
|
134
216
|
}
|
|
135
|
-
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Get header value (request headers first, then response headers)
|
|
220
|
+
* @param {string} key - Header name
|
|
221
|
+
* @returns {string|null} - Header value or null
|
|
222
|
+
*/
|
|
136
223
|
getHeader(key) {
|
|
224
|
+
const lowerKey = key.toLowerCase();
|
|
137
225
|
return (
|
|
138
|
-
this.headers[
|
|
139
|
-
this.
|
|
140
|
-
this._headersObj[key] ||
|
|
226
|
+
this.headers?.[lowerKey] ||
|
|
227
|
+
this._headersObj[lowerKey] ||
|
|
141
228
|
null
|
|
142
229
|
);
|
|
143
230
|
}
|
|
144
|
-
|
|
231
|
+
|
|
232
|
+
// ==========================================
|
|
233
|
+
// [PERF] RESPONSE METHODS
|
|
234
|
+
// ==========================================
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Set HTTP status code
|
|
238
|
+
* @param {number} code - Status code
|
|
239
|
+
* @returns {AetherContext} - Chainable
|
|
240
|
+
*/
|
|
145
241
|
setStatus(code) {
|
|
146
242
|
if (this._terminated) return this;
|
|
147
243
|
this.statusCode = code;
|
|
148
244
|
return this;
|
|
149
245
|
}
|
|
150
|
-
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Send JSON response
|
|
249
|
+
* @param {any} data - Data to send as JSON
|
|
250
|
+
* @returns {AetherContext} - Chainable
|
|
251
|
+
*/
|
|
151
252
|
json(data) {
|
|
152
253
|
if (this._terminated) return this;
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
254
|
+
|
|
255
|
+
this.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
256
|
+
|
|
257
|
+
// [PERF] Fast JSON stringification check
|
|
258
|
+
if (typeof data === "object") {
|
|
259
|
+
this._body = JSON.stringify(data);
|
|
260
|
+
} else {
|
|
261
|
+
this._body = String(data);
|
|
156
262
|
}
|
|
157
|
-
|
|
158
|
-
this._body = typeof data === "object" ? JSON.stringify(data) : data;
|
|
263
|
+
|
|
159
264
|
this._finalize();
|
|
160
265
|
return this;
|
|
161
266
|
}
|
|
162
|
-
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Send text response
|
|
270
|
+
* @param {string} data - Text to send
|
|
271
|
+
* @returns {AetherContext} - Chainable
|
|
272
|
+
*/
|
|
163
273
|
text(data) {
|
|
164
274
|
if (this._terminated) return this;
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
}
|
|
168
|
-
this._headersObj["Content-Type"] = "text/plain; charset=utf-8";
|
|
275
|
+
|
|
276
|
+
this.setHeader("Content-Type", "text/plain; charset=utf-8");
|
|
169
277
|
this._body = String(data);
|
|
170
278
|
this._finalize();
|
|
171
279
|
return this;
|
|
172
280
|
}
|
|
173
|
-
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Send raw response (no content-type header)
|
|
284
|
+
* @param {any} data - Raw data to send
|
|
285
|
+
* @returns {AetherContext} - Chainable
|
|
286
|
+
*/
|
|
174
287
|
raw(data) {
|
|
175
288
|
if (this._terminated) return this;
|
|
176
289
|
this._body = data;
|
|
177
290
|
this._finalize();
|
|
178
291
|
return this;
|
|
179
292
|
}
|
|
180
|
-
|
|
293
|
+
|
|
294
|
+
// ==========================================
|
|
295
|
+
// [PERF] STATE MANAGEMENT
|
|
296
|
+
// ==========================================
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Set state value (for middleware communication)
|
|
300
|
+
* @param {string} key - State key
|
|
301
|
+
* @param {any} value - State value
|
|
302
|
+
* @returns {AetherContext} - Chainable
|
|
303
|
+
*/
|
|
181
304
|
setState(key, value) {
|
|
182
305
|
this._stateObj[key] = value;
|
|
183
306
|
return this;
|
|
184
307
|
}
|
|
185
|
-
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Get state value
|
|
311
|
+
* @param {string} key - State key
|
|
312
|
+
* @returns {any} - State value
|
|
313
|
+
*/
|
|
186
314
|
getState(key) {
|
|
187
315
|
return this._stateObj[key];
|
|
188
316
|
}
|
|
189
|
-
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Check if response has been sent
|
|
320
|
+
* @returns {boolean} - True if terminated
|
|
321
|
+
*/
|
|
190
322
|
isTerminated() {
|
|
191
323
|
return this._terminated;
|
|
192
324
|
}
|
|
193
|
-
|
|
325
|
+
|
|
326
|
+
// ==========================================
|
|
327
|
+
// [PERF] RESPONSE FINALIZATION
|
|
328
|
+
// ==========================================
|
|
329
|
+
|
|
194
330
|
/**
|
|
195
|
-
*
|
|
331
|
+
* Finalize and send response
|
|
332
|
+
* @private
|
|
196
333
|
*/
|
|
197
334
|
_finalize() {
|
|
198
335
|
if (this._terminated) return;
|
|
199
336
|
this._terminated = true;
|
|
200
|
-
|
|
337
|
+
|
|
201
338
|
const res = this._response;
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
GLOBAL_HEADER_BUFFER[1] = "keep-alive";
|
|
209
|
-
let cursor = 2;
|
|
210
|
-
|
|
339
|
+
if (!res || res.headersSent) return;
|
|
340
|
+
|
|
341
|
+
// [PERF] Build headers object directly (faster than array manipulation)
|
|
342
|
+
const headers = {};
|
|
343
|
+
|
|
344
|
+
// [PERF] Manual header iteration without Object.keys()
|
|
211
345
|
for (let i = 0; i < this._headersCount; i++) {
|
|
212
346
|
const key = this._headersKeys[i];
|
|
213
347
|
const val = this._headersObj[key];
|
|
214
348
|
if (val !== undefined) {
|
|
215
|
-
|
|
216
|
-
GLOBAL_HEADER_BUFFER[cursor++] = val;
|
|
349
|
+
headers[key] = val;
|
|
217
350
|
}
|
|
218
351
|
}
|
|
219
|
-
|
|
220
|
-
//
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
352
|
+
|
|
353
|
+
// [PERF] Set keep-alive header if not already set
|
|
354
|
+
if (!headers["connection"]) {
|
|
355
|
+
headers["connection"] = "keep-alive";
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// [PERF] Direct writeHead call without extra checks
|
|
359
|
+
res.writeHead(this.statusCode, headers);
|
|
360
|
+
|
|
361
|
+
// [PERF] Direct end call (Node.js handles socket optimization internally)
|
|
224
362
|
if (this._body !== null) {
|
|
225
363
|
res.end(this._body);
|
|
226
364
|
} else {
|
|
227
365
|
res.end();
|
|
228
366
|
}
|
|
229
|
-
|
|
230
|
-
if (socket) {
|
|
231
|
-
process.nextTick(() => socket.uncork());
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
getMetrics() {
|
|
236
|
-
return {
|
|
237
|
-
duration: Number(process.hrtime.bigint() - this._startTime) / 1e6,
|
|
238
|
-
};
|
|
239
367
|
}
|
|
240
368
|
}
|
|
241
369
|
|