@aetherframework/middleware 1.0.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/.env.example +88 -0
- package/LICENSE +21 -0
- package/README.md +693 -0
- package/docs/readme/README.md +679 -0
- package/docs/readme/README_zh.md +680 -0
- package/examples/advanced-router-demo.js +119 -0
- package/examples/advanced-server.js +272 -0
- package/examples/basic-server.js +134 -0
- package/examples/benchmark.js +85 -0
- package/examples/router-demo.js +369 -0
- package/index.js +67 -0
- package/package.json +59 -0
- package/src/core/AetherCompiler.js +118 -0
- package/src/core/AetherContext.js +242 -0
- package/src/core/AetherPipeline.js +375 -0
- package/src/core/AetherRouter.js +347 -0
- package/src/core/AetherStore.js +204 -0
- package/src/middleware/body-parser.js +299 -0
- package/src/middleware/compression.js +248 -0
- package/src/middleware/cors.js +162 -0
- package/src/middleware/json.js +214 -0
- package/src/middleware/jwt.js +929 -0
- package/src/middleware/params.js +227 -0
- package/src/middleware/rate-limit.js +167 -0
- package/src/middleware/router.js +36 -0
- package/src/middleware/security.js +116 -0
- package/src/middleware/session.js +167 -0
- package/src/utils/atomic-ops.js +127 -0
- package/src/utils/env-loader.js +128 -0
- package/src/utils/memory-pool.js +93 -0
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license MIT
|
|
3
|
+
* Copyright (c) 2026-present AetherFramework Contributors.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
* @module @aetherframework/middleware/core/AetherContext
|
|
6
|
+
*/
|
|
7
|
+
const GLOBAL_HEADER_BUFFER = new Array(64);
|
|
8
|
+
|
|
9
|
+
class AetherContext {
|
|
10
|
+
constructor(request, response) {
|
|
11
|
+
// 1. Strictly align with V8's Hidden Class (Shape) optimization
|
|
12
|
+
this._request = null;
|
|
13
|
+
this._response = null;
|
|
14
|
+
|
|
15
|
+
this.method = "";
|
|
16
|
+
this.url = "";
|
|
17
|
+
this.headers = null;
|
|
18
|
+
this.path = "";
|
|
19
|
+
this._queryString = "";
|
|
20
|
+
|
|
21
|
+
// 2. Avoid using Map/Set; use flat plain objects/arrays for custom response headers to achieve 5x faster R/W speeds.
|
|
22
|
+
this._headersObj = {};
|
|
23
|
+
this._headersCount = 0;
|
|
24
|
+
this._headersKeys = new Array(16); // Pre-allocated key tracking array
|
|
25
|
+
|
|
26
|
+
this._queryCache = null;
|
|
27
|
+
this._ipCache = null; // Lazy-evaluated cache
|
|
28
|
+
|
|
29
|
+
this.statusCode = 200;
|
|
30
|
+
this._body = null;
|
|
31
|
+
this._terminated = false;
|
|
32
|
+
|
|
33
|
+
// 3. Use plain flat objects for business state/context storage, strictly avoiding Maps.
|
|
34
|
+
this._stateObj = {};
|
|
35
|
+
|
|
36
|
+
this._startTime = 0n;
|
|
37
|
+
|
|
38
|
+
if (request && response) {
|
|
39
|
+
this._reset(request, response);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Ultra-fast context reset for pooling mechanisms (True Zero-Object-Allocation strategy)
|
|
45
|
+
*/
|
|
46
|
+
_reset(request, response) {
|
|
47
|
+
this._request = request;
|
|
48
|
+
this._response = response;
|
|
49
|
+
|
|
50
|
+
this.method = request.method;
|
|
51
|
+
const rawUrl = request.url || "/";
|
|
52
|
+
this.url = rawUrl;
|
|
53
|
+
this.headers = request.headers;
|
|
54
|
+
|
|
55
|
+
// Fast string slicing (bypassing expensive RegEx and URL instantiation overheads)
|
|
56
|
+
const qIdx = rawUrl.indexOf("?");
|
|
57
|
+
if (qIdx !== -1) {
|
|
58
|
+
this.path = rawUrl.substring(0, qIdx);
|
|
59
|
+
this._queryString = rawUrl.substring(qIdx + 1);
|
|
60
|
+
} else {
|
|
61
|
+
this.path = rawUrl;
|
|
62
|
+
this._queryString = "";
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Only clear references, never use the 'new' keyword to re-allocate memory
|
|
66
|
+
this._queryCache = null;
|
|
67
|
+
this._ipCache = null;
|
|
68
|
+
|
|
69
|
+
this.statusCode = 200;
|
|
70
|
+
this._body = null;
|
|
71
|
+
this._terminated = false;
|
|
72
|
+
|
|
73
|
+
// Clean up custom headers object without breaking Hidden Classes or re-allocating memory
|
|
74
|
+
if (this._headersCount > 0) {
|
|
75
|
+
for (let i = 0; i < this._headersCount; i++) {
|
|
76
|
+
this._headersObj[this._headersKeys[i]] = undefined;
|
|
77
|
+
}
|
|
78
|
+
this._headersCount = 0;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
this._stateObj = {}; // Kept as empty object in most scenarios
|
|
82
|
+
this._startTime = process.hrtime.bigint();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// 🟢 Intercept the body property to establish the data pipeline
|
|
86
|
+
get body() {
|
|
87
|
+
return this._body;
|
|
88
|
+
}
|
|
89
|
+
set body(value) {
|
|
90
|
+
if (this._terminated) return;
|
|
91
|
+
this._body = value;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Lazy IP evaluation: Trigger C++ binding bridge only when business logic explicitly requests ctx.ip
|
|
96
|
+
*/
|
|
97
|
+
get ip() {
|
|
98
|
+
if (this._ipCache) return this._ipCache;
|
|
99
|
+
const sock = this._request?.socket;
|
|
100
|
+
return (this._ipCache = sock ? sock.remoteAddress : "127.0.0.1");
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Lazy Query parsing: Synchronous hand-optimized raw string scanner
|
|
105
|
+
*/
|
|
106
|
+
get query() {
|
|
107
|
+
if (this._queryCache) return this._queryCache;
|
|
108
|
+
if (!this._queryString) return (this._queryCache = {});
|
|
109
|
+
|
|
110
|
+
const obj = {};
|
|
111
|
+
const pairs = this._queryString.split("&");
|
|
112
|
+
for (let i = 0; i < pairs.length; i++) {
|
|
113
|
+
const pair = pairs[i];
|
|
114
|
+
const eqIdx = pair.indexOf("=");
|
|
115
|
+
if (eqIdx !== -1) {
|
|
116
|
+
obj[pair.substring(0, eqIdx)] = pair.substring(eqIdx + 1);
|
|
117
|
+
} else {
|
|
118
|
+
obj[pair] = "";
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return (this._queryCache = obj);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
setHeader(key, value) {
|
|
125
|
+
if (this._terminated) return this;
|
|
126
|
+
|
|
127
|
+
// Convention: High-performance middlewares should pass standard casing headers (e.g., 'Content-Type').
|
|
128
|
+
// We intentionally omit .toLowerCase() here to conserve CPU cycles.
|
|
129
|
+
if (this._headersObj[key] === undefined) {
|
|
130
|
+
this._headersKeys[this._headersCount++] = key;
|
|
131
|
+
}
|
|
132
|
+
this._headersObj[key] = value;
|
|
133
|
+
return this;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
getHeader(key) {
|
|
137
|
+
return (
|
|
138
|
+
this.headers[key] ||
|
|
139
|
+
this.headers[key.toLowerCase()] ||
|
|
140
|
+
this._headersObj[key] ||
|
|
141
|
+
null
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
setStatus(code) {
|
|
146
|
+
if (this._terminated) return this;
|
|
147
|
+
this.statusCode = code;
|
|
148
|
+
return this;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
json(data) {
|
|
152
|
+
if (this._terminated) return this;
|
|
153
|
+
// Direct assignment to prevent redundant lookup overheads
|
|
154
|
+
if (this._headersObj["Content-Type"] === undefined) {
|
|
155
|
+
this._headersKeys[this._headersCount++] = "Content-Type";
|
|
156
|
+
}
|
|
157
|
+
this._headersObj["Content-Type"] = "application/json; charset=utf-8";
|
|
158
|
+
this._body = typeof data === "object" ? JSON.stringify(data) : data;
|
|
159
|
+
this._finalize();
|
|
160
|
+
return this;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
text(data) {
|
|
164
|
+
if (this._terminated) return this;
|
|
165
|
+
if (this._headersObj["Content-Type"] === undefined) {
|
|
166
|
+
this._headersKeys[this._headersCount++] = "Content-Type";
|
|
167
|
+
}
|
|
168
|
+
this._headersObj["Content-Type"] = "text/plain; charset=utf-8";
|
|
169
|
+
this._body = String(data);
|
|
170
|
+
this._finalize();
|
|
171
|
+
return this;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
raw(data) {
|
|
175
|
+
if (this._terminated) return this;
|
|
176
|
+
this._body = data;
|
|
177
|
+
this._finalize();
|
|
178
|
+
return this;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
setState(key, value) {
|
|
182
|
+
this._stateObj[key] = value;
|
|
183
|
+
return this;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
getState(key) {
|
|
187
|
+
return this._stateObj[key];
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
isTerminated() {
|
|
191
|
+
return this._terminated;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Critical Performance Bottleneck Optimization: Flush data instantly via the shared buffer
|
|
196
|
+
*/
|
|
197
|
+
_finalize() {
|
|
198
|
+
if (this._terminated) return;
|
|
199
|
+
this._terminated = true;
|
|
200
|
+
|
|
201
|
+
const res = this._response;
|
|
202
|
+
const socket = res.socket;
|
|
203
|
+
|
|
204
|
+
if (socket) socket.cork();
|
|
205
|
+
|
|
206
|
+
// Core optimization: Reuse the pre-allocated flat array to eliminate transient garbage collection pressure
|
|
207
|
+
GLOBAL_HEADER_BUFFER[0] = "Connection";
|
|
208
|
+
GLOBAL_HEADER_BUFFER[1] = "keep-alive";
|
|
209
|
+
let cursor = 2;
|
|
210
|
+
|
|
211
|
+
for (let i = 0; i < this._headersCount; i++) {
|
|
212
|
+
const key = this._headersKeys[i];
|
|
213
|
+
const val = this._headersObj[key];
|
|
214
|
+
if (val !== undefined) {
|
|
215
|
+
GLOBAL_HEADER_BUFFER[cursor++] = key;
|
|
216
|
+
GLOBAL_HEADER_BUFFER[cursor++] = val;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Slice the buffer and pass it directly to the native writeHead.
|
|
221
|
+
// Note: The slice size is highly predictable and short, allowing V8 to aggressively inline the operation.
|
|
222
|
+
res.writeHead(this.statusCode, GLOBAL_HEADER_BUFFER.slice(0, cursor));
|
|
223
|
+
|
|
224
|
+
if (this._body !== null) {
|
|
225
|
+
res.end(this._body);
|
|
226
|
+
} else {
|
|
227
|
+
res.end();
|
|
228
|
+
}
|
|
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
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export default AetherContext;
|
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license MIT
|
|
3
|
+
* Copyright (c) 2026-present AetherFramework Contributors.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
* @module @aetherframework/middleware/core/AetherPipeline
|
|
6
|
+
*/
|
|
7
|
+
import { EventEmitter } from "events";
|
|
8
|
+
import AetherContext from "./AetherContext.js";
|
|
9
|
+
|
|
10
|
+
const STATIC_RESPONSES = new Map([
|
|
11
|
+
[200, Buffer.from(JSON.stringify({ status: "ok" }))],
|
|
12
|
+
[404, Buffer.from(JSON.stringify({ error: "Not Found" }))],
|
|
13
|
+
[500, Buffer.from(JSON.stringify({ error: "Internal Server Error" }))],
|
|
14
|
+
]);
|
|
15
|
+
|
|
16
|
+
const CONTEXT_POOL = [];
|
|
17
|
+
const IN_POOL_CHECK = new Set();
|
|
18
|
+
const CONTEXT_POOL_SIZE = 4096;
|
|
19
|
+
|
|
20
|
+
class AetherPipeline extends EventEmitter {
|
|
21
|
+
constructor() {
|
|
22
|
+
super();
|
|
23
|
+
this._middlewares = [];
|
|
24
|
+
this._compiled = null;
|
|
25
|
+
this._compiledSync = null;
|
|
26
|
+
this._cache = new Map();
|
|
27
|
+
this._cacheMaxSize = 1000;
|
|
28
|
+
this.enableMetrics = false;
|
|
29
|
+
|
|
30
|
+
this._stats = {
|
|
31
|
+
totalRequests: 0,
|
|
32
|
+
averageLatency: 0,
|
|
33
|
+
errorCount: 0,
|
|
34
|
+
cacheHits: 0,
|
|
35
|
+
cacheMisses: 0,
|
|
36
|
+
poolHits: 0,
|
|
37
|
+
poolMisses: 0,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
this._initObjectPools();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
_initObjectPools() {
|
|
44
|
+
for (let i = 0; i < CONTEXT_POOL_SIZE; i++) {
|
|
45
|
+
const ctx = new AetherContext(null, null);
|
|
46
|
+
CONTEXT_POOL.push(ctx);
|
|
47
|
+
IN_POOL_CHECK.add(ctx);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
_getContext(request, response) {
|
|
52
|
+
if (CONTEXT_POOL.length > 0) {
|
|
53
|
+
const context = CONTEXT_POOL.pop();
|
|
54
|
+
IN_POOL_CHECK.delete(context);
|
|
55
|
+
context._reset(request, response);
|
|
56
|
+
if (this.enableMetrics) this._stats.poolHits++;
|
|
57
|
+
return context;
|
|
58
|
+
}
|
|
59
|
+
if (this.enableMetrics) this._stats.poolMisses++;
|
|
60
|
+
return new AetherContext(request, response);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
_returnContext(context) {
|
|
64
|
+
if (!context || IN_POOL_CHECK.has(context)) return;
|
|
65
|
+
|
|
66
|
+
if (CONTEXT_POOL.length < CONTEXT_POOL_SIZE) {
|
|
67
|
+
context.req = null;
|
|
68
|
+
context.res = null;
|
|
69
|
+
context._body = null;
|
|
70
|
+
|
|
71
|
+
// Safely clean up Headers
|
|
72
|
+
if (context._headers && typeof context._headers.clear === "function") {
|
|
73
|
+
context._headers.clear();
|
|
74
|
+
} else {
|
|
75
|
+
context._headers = null;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
CONTEXT_POOL.push(context);
|
|
79
|
+
IN_POOL_CHECK.add(context);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
use(middleware) {
|
|
84
|
+
if (typeof middleware !== "function") {
|
|
85
|
+
throw new TypeError("Middleware must be a function");
|
|
86
|
+
}
|
|
87
|
+
this._middlewares.push(middleware);
|
|
88
|
+
this._compiled = null;
|
|
89
|
+
this._compiledSync = null;
|
|
90
|
+
return this;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
*Core Control 1: Standard V8-level high-concurrency asynchronous onion model compiler
|
|
95
|
+
* Ensure perfect timing wait chain, absolutely prevent asynchronous middleware (like CORS/security headers) from experiencing asynchronous drift
|
|
96
|
+
*/
|
|
97
|
+
compile() {
|
|
98
|
+
if (this._compiled) return this._compiled;
|
|
99
|
+
const middlewares = this._middlewares;
|
|
100
|
+
const len = middlewares.length;
|
|
101
|
+
|
|
102
|
+
this._compiled = async function executePipeline(context) {
|
|
103
|
+
async function dispatch(i) {
|
|
104
|
+
// 1. Safety boundary check: If context is terminated or connection is disconnected, return directly
|
|
105
|
+
if (
|
|
106
|
+
context.isTerminated() ||
|
|
107
|
+
(context._response && context._response.writableEnded)
|
|
108
|
+
) {
|
|
109
|
+
if (context._finalize) context._finalize();
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// 2. Pipeline end: Safely trigger final _finalize
|
|
114
|
+
if (i >= len) {
|
|
115
|
+
if (context._finalize) context._finalize();
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const mw = middlewares[i];
|
|
120
|
+
|
|
121
|
+
// 3. Strictly bind current middleware with next subsequent chain's asynchronous timing
|
|
122
|
+
await mw(context, function next() {
|
|
123
|
+
return dispatch(i + 1);
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// 🚀 Start the first middleware and strictly wait for the entire chain lifecycle to end
|
|
128
|
+
await dispatch(0);
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
return this._compiled;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
_compileSync() {
|
|
135
|
+
if (this._compiledSync) return this._compiledSync;
|
|
136
|
+
const middlewares = this._middlewares;
|
|
137
|
+
const len = middlewares.length;
|
|
138
|
+
|
|
139
|
+
const allSync = middlewares.every((mw) => {
|
|
140
|
+
const funcStr = mw.toString();
|
|
141
|
+
return (
|
|
142
|
+
!funcStr.includes("async ") &&
|
|
143
|
+
!funcStr.includes(".then") &&
|
|
144
|
+
!funcStr.includes("await ")
|
|
145
|
+
);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
if (!allSync) return null;
|
|
149
|
+
|
|
150
|
+
this._compiledSync = function executePipelineSync(context) {
|
|
151
|
+
for (let i = 0; i < len; i++) {
|
|
152
|
+
middlewares[i](context, () => {});
|
|
153
|
+
if (context.isTerminated() || context.res?.writableEnded) return;
|
|
154
|
+
}
|
|
155
|
+
if (context._finalize) context._finalize();
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
return this._compiledSync;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async handle(request, response) {
|
|
162
|
+
this._stats.totalRequests++;
|
|
163
|
+
const url = request.url;
|
|
164
|
+
const method = request.method;
|
|
165
|
+
|
|
166
|
+
// 1. Root high-speed channel (with fallback CORS headers)
|
|
167
|
+
if (method === "GET" && url === "/") {
|
|
168
|
+
const socket = response.socket;
|
|
169
|
+
if (socket) socket.cork();
|
|
170
|
+
response.writeHead(200, [
|
|
171
|
+
"Content-Type",
|
|
172
|
+
"application/json; charset=utf-8",
|
|
173
|
+
"Content-Length",
|
|
174
|
+
"15",
|
|
175
|
+
"Connection",
|
|
176
|
+
"keep-alive",
|
|
177
|
+
"Access-Control-Allow-Origin",
|
|
178
|
+
"*",
|
|
179
|
+
]);
|
|
180
|
+
response.end(STATIC_RESPONSES.get(200));
|
|
181
|
+
if (socket) socket.uncork();
|
|
182
|
+
return { cacheHit: true, static: true };
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// 2. Cache route hit
|
|
186
|
+
let methodCache = this._cache.get(method);
|
|
187
|
+
if (!methodCache) {
|
|
188
|
+
methodCache = new Map();
|
|
189
|
+
this._cache.set(method, methodCache);
|
|
190
|
+
}
|
|
191
|
+
const cached = methodCache.get(url);
|
|
192
|
+
|
|
193
|
+
if (cached) {
|
|
194
|
+
if (this.enableMetrics) this._stats.cacheHits++;
|
|
195
|
+
const socket = response.socket;
|
|
196
|
+
if (socket) socket.cork();
|
|
197
|
+
response.writeHead(cached.status, cached.headers);
|
|
198
|
+
response.end(cached.buffer);
|
|
199
|
+
if (socket) socket.uncork();
|
|
200
|
+
return { cacheHit: true };
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (this.enableMetrics) this._stats.cacheMisses++;
|
|
204
|
+
|
|
205
|
+
// 3. Context construction and core scheduling
|
|
206
|
+
const context = this._getContext(request, response);
|
|
207
|
+
|
|
208
|
+
try {
|
|
209
|
+
//Core Control 2: Prioritize reading cached synchronous pipeline to avoid frequent character scanning causing CPU spikes under high concurrency
|
|
210
|
+
const pipelineSync = this._compiledSync || this._compileSync();
|
|
211
|
+
|
|
212
|
+
if (pipelineSync) {
|
|
213
|
+
pipelineSync(context);
|
|
214
|
+
} else {
|
|
215
|
+
// Strictly lock asynchronous middleware timing
|
|
216
|
+
await this.compile()(context);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// 4. Dynamically capture all response headers, merge and store in high-speed cache
|
|
220
|
+
if (method === "GET" && response && response.statusCode < 400) {
|
|
221
|
+
this._setResponseCache(methodCache, url, context, response);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
this._returnContext(context);
|
|
225
|
+
return { cacheHit: false };
|
|
226
|
+
} catch (error) {
|
|
227
|
+
if (this.enableMetrics) this._stats.errorCount++;
|
|
228
|
+
try {
|
|
229
|
+
if (response && !response.headersSent) {
|
|
230
|
+
response.writeHead(500, [
|
|
231
|
+
"Content-Type",
|
|
232
|
+
"application/json; charset=utf-8",
|
|
233
|
+
"Access-Control-Allow-Origin",
|
|
234
|
+
"*",
|
|
235
|
+
]);
|
|
236
|
+
response.end(STATIC_RESPONSES.get(500));
|
|
237
|
+
}
|
|
238
|
+
} catch (e) {}
|
|
239
|
+
if (context) this._returnContext(context);
|
|
240
|
+
throw error;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
*Core Control 3: Ultimate comprehensive defense extraction algorithm —— Intercept multi-source Headers without blind spots
|
|
246
|
+
*/
|
|
247
|
+
_setResponseCache(methodCache, url, context, response) {
|
|
248
|
+
// Establish a standard flat array, native two-element storage format is most efficient
|
|
249
|
+
const rawHeaders = ["Connection", "keep-alive"];
|
|
250
|
+
const lowerKeys = new Set(["connection"]);
|
|
251
|
+
|
|
252
|
+
// ==========================================
|
|
253
|
+
// Strategy A: Forcefully synchronously extract AetherContext's high-performance private storage
|
|
254
|
+
// ==========================================
|
|
255
|
+
if (context && context._headersCount > 0) {
|
|
256
|
+
for (let i = 0; i < context._headersCount; i++) {
|
|
257
|
+
const key = context._headersKeys[i];
|
|
258
|
+
const val = context._headersObj[key];
|
|
259
|
+
if (val !== undefined && key) {
|
|
260
|
+
const kLower = key.toLowerCase();
|
|
261
|
+
if (!lowerKeys.has(kLower)) {
|
|
262
|
+
rawHeaders.push(key, String(val));
|
|
263
|
+
lowerKeys.add(kLower);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// ==========================================
|
|
270
|
+
// Strategy B: Deep scan various non-standard Context properties that may evolve into generic dictionaries
|
|
271
|
+
// ==========================================
|
|
272
|
+
const potentialDicts = [
|
|
273
|
+
context?._headers,
|
|
274
|
+
context?.headers,
|
|
275
|
+
context?.res?.headers,
|
|
276
|
+
];
|
|
277
|
+
for (const dict of potentialDicts) {
|
|
278
|
+
if (dict && typeof dict === "object" && !(dict instanceof Set)) {
|
|
279
|
+
for (const key in dict) {
|
|
280
|
+
if (Object.prototype.hasOwnProperty.call(dict, key)) {
|
|
281
|
+
const kLower = key.toLowerCase();
|
|
282
|
+
if (!lowerKeys.has(kLower) && dict[key] !== undefined) {
|
|
283
|
+
rawHeaders.push(key, String(dict[key]));
|
|
284
|
+
lowerKeys.add(kLower);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// ==========================================
|
|
292
|
+
// Strategy C: Ultimate gap filling: Synchronously capture Node.js native response's standard response headers (security upgraded version)
|
|
293
|
+
// ==========================================
|
|
294
|
+
if (response) {
|
|
295
|
+
// 1. Intercept standard getHeaders() - Standard entry point for most modern Node.js
|
|
296
|
+
if (typeof response.getHeaders === "function") {
|
|
297
|
+
const nodeHeaders = response.getHeaders();
|
|
298
|
+
if (nodeHeaders) {
|
|
299
|
+
for (const key in nodeHeaders) {
|
|
300
|
+
const kLower = key.toLowerCase();
|
|
301
|
+
if (!lowerKeys.has(kLower)) {
|
|
302
|
+
const val = nodeHeaders[key];
|
|
303
|
+
rawHeaders.push(
|
|
304
|
+
key,
|
|
305
|
+
Array.isArray(val) ? val.join(", ") : String(val),
|
|
306
|
+
);
|
|
307
|
+
lowerKeys.add(kLower);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// 2. Intercept standard getHeaderNames() - As standard supplement for HTTP/2 or special streaming protocols
|
|
314
|
+
if (typeof response.getHeaderNames === "function") {
|
|
315
|
+
const names = response.getHeaderNames();
|
|
316
|
+
if (Array.isArray(names)) {
|
|
317
|
+
for (const key of names) {
|
|
318
|
+
const kLower = key.toLowerCase();
|
|
319
|
+
if (!lowerKeys.has(kLower)) {
|
|
320
|
+
const val = response.getHeader(key);
|
|
321
|
+
if (val !== undefined && val !== null) {
|
|
322
|
+
rawHeaders.push(
|
|
323
|
+
key,
|
|
324
|
+
Array.isArray(val) ? val.join(", ") : String(val),
|
|
325
|
+
);
|
|
326
|
+
lowerKeys.add(kLower);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// 🟢 Removed response._headers detection code that would cause high-version Node.js crashes and deprecation warnings
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// ==========================================
|
|
337
|
+
// Strategy D: Extract Body and convert to high-speed persistent Buffer
|
|
338
|
+
// ==========================================
|
|
339
|
+
let body = context._body || "";
|
|
340
|
+
const buffer = Buffer.isBuffer(body)
|
|
341
|
+
? body
|
|
342
|
+
: Buffer.from(typeof body === "string" ? body : JSON.stringify(body));
|
|
343
|
+
|
|
344
|
+
// Complete length header
|
|
345
|
+
if (!lowerKeys.has("content-length")) {
|
|
346
|
+
rawHeaders.push("Content-Length", String(buffer.length));
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
methodCache.set(url, {
|
|
350
|
+
headers: rawHeaders,
|
|
351
|
+
status: context.statusCode || response.statusCode || 200,
|
|
352
|
+
buffer,
|
|
353
|
+
timestamp: Date.now(),
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
if (methodCache.size > this._cacheMaxSize) {
|
|
357
|
+
const firstKey = methodCache.keys().next().value;
|
|
358
|
+
methodCache.delete(firstKey);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
getStats() {
|
|
363
|
+
return { ...this._stats, poolSize: CONTEXT_POOL.length };
|
|
364
|
+
}
|
|
365
|
+
clearCache() {
|
|
366
|
+
this._cache.clear();
|
|
367
|
+
}
|
|
368
|
+
precompile() {
|
|
369
|
+
this.compile();
|
|
370
|
+
this._compileSync();
|
|
371
|
+
return this;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
export default AetherPipeline;
|