@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,119 @@
|
|
|
1
|
+
// examples/advanced-router-demo.js
|
|
2
|
+
import { AetherPipeline, middleware } from '../index.js';
|
|
3
|
+
import http from "http";
|
|
4
|
+
|
|
5
|
+
const app = new AetherPipeline();
|
|
6
|
+
const router = new middleware.router.Router();
|
|
7
|
+
|
|
8
|
+
// Use global middlewares
|
|
9
|
+
app.use(middleware.cors());
|
|
10
|
+
app.use(middleware.security());
|
|
11
|
+
app.use(middleware.bodyParser());
|
|
12
|
+
app.use(middleware.json());
|
|
13
|
+
app.use(middleware.params()); // Add parameter parsing middleware
|
|
14
|
+
|
|
15
|
+
// ========== ROUTE WITH VALIDATION ==========
|
|
16
|
+
const userValidation = {
|
|
17
|
+
name: {
|
|
18
|
+
type: "string",
|
|
19
|
+
required: true,
|
|
20
|
+
minLength: 2,
|
|
21
|
+
maxLength: 50
|
|
22
|
+
},
|
|
23
|
+
email: {
|
|
24
|
+
type: "string",
|
|
25
|
+
required: true,
|
|
26
|
+
pattern: "^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$"
|
|
27
|
+
},
|
|
28
|
+
age: {
|
|
29
|
+
type: "number",
|
|
30
|
+
min: 18,
|
|
31
|
+
max: 100
|
|
32
|
+
},
|
|
33
|
+
tags: {
|
|
34
|
+
type: "array"
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
router.post("/api/users", (ctx) => {
|
|
39
|
+
// Parameters are already validated and parsed
|
|
40
|
+
const { name, email, age, tags } = ctx.allParams;
|
|
41
|
+
|
|
42
|
+
ctx.json({
|
|
43
|
+
success: true,
|
|
44
|
+
data: {
|
|
45
|
+
name,
|
|
46
|
+
email,
|
|
47
|
+
age,
|
|
48
|
+
tags,
|
|
49
|
+
id: Date.now()
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Add validation rules to route
|
|
55
|
+
router.routes.get("POST:/api/users").validationRules = userValidation;
|
|
56
|
+
|
|
57
|
+
// ========== COMPLEX ROUTE EXAMPLE ==========
|
|
58
|
+
router.group("/api/v1", (v1) => {
|
|
59
|
+
v1.use(middleware.jwt()); // JWT auth for all v1 routes
|
|
60
|
+
|
|
61
|
+
v1.group("/admin", (admin) => {
|
|
62
|
+
admin.get("/dashboard", (ctx) => {
|
|
63
|
+
const user = ctx.getState("user");
|
|
64
|
+
ctx.json({
|
|
65
|
+
admin: true,
|
|
66
|
+
user,
|
|
67
|
+
timestamp: new Date().toISOString()
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
admin.get("/users", (ctx) => {
|
|
72
|
+
const { page = 1, limit = 20, sort = "desc" } = ctx.query;
|
|
73
|
+
ctx.json({
|
|
74
|
+
page: parseInt(page),
|
|
75
|
+
limit: parseInt(limit),
|
|
76
|
+
sort,
|
|
77
|
+
users: []
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// 修复:将 'public' 改为非保留字名称
|
|
83
|
+
v1.group("/public", (publicGroup) => {
|
|
84
|
+
publicGroup.get("/products", (ctx) => {
|
|
85
|
+
const { category, minPrice, maxPrice } = ctx.query;
|
|
86
|
+
ctx.json({
|
|
87
|
+
category,
|
|
88
|
+
minPrice: minPrice ? parseFloat(minPrice) : null,
|
|
89
|
+
maxPrice: maxPrice ? parseFloat(maxPrice) : null,
|
|
90
|
+
products: []
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Add router to pipeline
|
|
97
|
+
app.use(router.middleware());
|
|
98
|
+
|
|
99
|
+
// ========== 404 HANDLER ==========
|
|
100
|
+
app.use(async (ctx) => {
|
|
101
|
+
if (!ctx.isTerminated()) {
|
|
102
|
+
ctx.setStatus(404).json({
|
|
103
|
+
error: "Not Found",
|
|
104
|
+
message: `Cannot ${ctx.method} ${ctx.url}`,
|
|
105
|
+
timestamp: new Date().toISOString()
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// Start server using http.createServer
|
|
111
|
+
const server = http.createServer(async (req, res) => {
|
|
112
|
+
await app.handle(req, res);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
server.listen(3000, () => {
|
|
116
|
+
console.log("🚀 Advanced AetherJS Server running on http://localhost:3000");
|
|
117
|
+
console.log("📋 Registered routes:");
|
|
118
|
+
console.log(router.getRoutes());
|
|
119
|
+
});
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license MIT
|
|
3
|
+
* Copyright (c) 2026-present, AetherFramework Contributors.
|
|
4
|
+
* Response compression middleware for AetherFramework framework.
|
|
5
|
+
* Supports gzip, deflate, and brotli compression with zero-copy operations.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import http from "http";
|
|
9
|
+
import { AetherPipeline } from "../index.js";
|
|
10
|
+
|
|
11
|
+
// Import optimized middleware
|
|
12
|
+
import security from "../src/middleware/security.js";
|
|
13
|
+
import rateLimit from "../src/middleware/rate-limit.js";
|
|
14
|
+
import cors from "../src/middleware/cors.js";
|
|
15
|
+
import jwt from "../src/middleware/jwt.js";
|
|
16
|
+
import bodyParser from "../src/middleware/body-parser.js";
|
|
17
|
+
import compression from "../src/middleware/compression.js";
|
|
18
|
+
import SessionManager from "../src/middleware/session.js";
|
|
19
|
+
|
|
20
|
+
// Load configuration
|
|
21
|
+
const config = {
|
|
22
|
+
jwtSecret: process.env.JWT_SECRET || "dev-secret",
|
|
23
|
+
port: process.env.PORT || 3001,
|
|
24
|
+
sessionSecret: process.env.SESSION_SECRET || "aether-session-secret",
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// Initialize Aether pipeline
|
|
28
|
+
const pipeline = new AetherPipeline();
|
|
29
|
+
|
|
30
|
+
// ==========================================================
|
|
31
|
+
// Middleware adapter: Convert standard (ctx, next) middleware to AetherPipeline (ctx, signal) format
|
|
32
|
+
// ==========================================================
|
|
33
|
+
function adaptMiddleware(standardMiddleware) {
|
|
34
|
+
return async function adaptedMiddleware(ctx, signal) {
|
|
35
|
+
// Define a standard next function that calls signal.next()
|
|
36
|
+
const next = async () => {
|
|
37
|
+
if (signal && typeof signal.next === "function") {
|
|
38
|
+
return await signal.next();
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// Call the original middleware with ctx and the new next function
|
|
43
|
+
// Note: Some middleware may not return a Promise, so use await to ensure execution completes
|
|
44
|
+
try {
|
|
45
|
+
const result = standardMiddleware(ctx, next);
|
|
46
|
+
if (result && typeof result.then === "function") {
|
|
47
|
+
await result;
|
|
48
|
+
}
|
|
49
|
+
} catch (error) {
|
|
50
|
+
console.error("Middleware error:", error);
|
|
51
|
+
// Optionally handle errors here or let the global error handler catch them
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// ==========================================================
|
|
57
|
+
// Pre-initialize all middleware (never create in request callbacks)
|
|
58
|
+
// ==========================================================
|
|
59
|
+
const securityMiddleware = security({
|
|
60
|
+
hsts: { enabled: true, maxAge: 31536000 },
|
|
61
|
+
noSniff: { enabled: true },
|
|
62
|
+
frameguard: { enabled: true, action: "DENY" },
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const corsMiddleware = cors({
|
|
66
|
+
origin: "*",
|
|
67
|
+
credentials: true,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const compressionMiddleware = compression({
|
|
71
|
+
enabled: process.env.COMPRESSION_ENABLED === "true",
|
|
72
|
+
threshold: parseInt(process.env.COMPRESSION_THRESHOLD) || 1024,
|
|
73
|
+
gzip: process.env.COMPRESSION_GZIP !== "false",
|
|
74
|
+
deflate: process.env.COMPRESSION_DEFLATE === "true",
|
|
75
|
+
brotli: process.env.COMPRESSION_BROTLI === "true",
|
|
76
|
+
types: "application/json,text/plain",
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const sessionManager = new SessionManager({
|
|
80
|
+
enabled: true,
|
|
81
|
+
secret: config.sessionSecret,
|
|
82
|
+
maxAge: parseInt(process.env.SESSION_MAX_AGE) || 86400000,
|
|
83
|
+
cookieName: "aether_session",
|
|
84
|
+
store: "memory",
|
|
85
|
+
});
|
|
86
|
+
const sessionMiddleware = sessionManager.middleware();
|
|
87
|
+
|
|
88
|
+
const rateLimitMiddleware = rateLimit({
|
|
89
|
+
windowMs: 15 * 60 * 1000,
|
|
90
|
+
max: 100,
|
|
91
|
+
enabled: true,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const bodyParserMiddleware = bodyParser({
|
|
95
|
+
json: { enabled: true, limit: "1mb" },
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
const jwtMiddlewareInstance = jwt({
|
|
99
|
+
secret: config.jwtSecret,
|
|
100
|
+
algorithms: ["HS256"],
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// ==========================================================
|
|
104
|
+
// Mount middleware pipeline (explicitly pass control to next)
|
|
105
|
+
// ==========================================================
|
|
106
|
+
|
|
107
|
+
// 1. Security Headers
|
|
108
|
+
pipeline.use(securityMiddleware);
|
|
109
|
+
|
|
110
|
+
// 2. CORS
|
|
111
|
+
pipeline.use(corsMiddleware);
|
|
112
|
+
|
|
113
|
+
// 3. Response Compression
|
|
114
|
+
pipeline.use(compressionMiddleware);
|
|
115
|
+
|
|
116
|
+
// 4. Session Management
|
|
117
|
+
pipeline.use(sessionMiddleware);
|
|
118
|
+
|
|
119
|
+
// 5. Rate Limiting
|
|
120
|
+
pipeline.use(rateLimitMiddleware);
|
|
121
|
+
|
|
122
|
+
// 6. Body Parsing
|
|
123
|
+
pipeline.use(bodyParserMiddleware);
|
|
124
|
+
|
|
125
|
+
// 7. Public Routes
|
|
126
|
+
pipeline.use((ctx, next) => {
|
|
127
|
+
if (ctx.isTerminated() || ctx.res?.writableEnded) return;
|
|
128
|
+
if (ctx.url === "/public/info") {
|
|
129
|
+
ctx.setStatus(200);
|
|
130
|
+
ctx.json({ message: "This is public information" });
|
|
131
|
+
if (typeof ctx.terminate === "function") ctx.terminate();
|
|
132
|
+
return; // Endpoint reached, do not call next()
|
|
133
|
+
}
|
|
134
|
+
return next(); // Route not matched, must pass control
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// 8. Session Example Route
|
|
138
|
+
pipeline.use((ctx, next) => {
|
|
139
|
+
if (ctx.isTerminated() || ctx.res?.writableEnded) return;
|
|
140
|
+
|
|
141
|
+
if (ctx.url === "/session/example") {
|
|
142
|
+
const visitCount = ctx.session?.get("visitCount") || 0;
|
|
143
|
+
ctx.session?.set("visitCount", visitCount + 1);
|
|
144
|
+
ctx.session?.set("lastVisit", new Date().toISOString());
|
|
145
|
+
|
|
146
|
+
ctx.setStatus(200);
|
|
147
|
+
ctx.json({
|
|
148
|
+
message: "Session example",
|
|
149
|
+
sessionId: ctx.state?.session?.id,
|
|
150
|
+
visitCount: visitCount + 1,
|
|
151
|
+
});
|
|
152
|
+
if (typeof ctx.terminate === "function") ctx.terminate();
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
return next(); // Pass control
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// 9. Login Route
|
|
159
|
+
pipeline.use((ctx, next) => {
|
|
160
|
+
if (ctx.isTerminated() || ctx.res?.writableEnded) return;
|
|
161
|
+
|
|
162
|
+
if (ctx.url === "/api/login" && ctx.method === "POST") {
|
|
163
|
+
const { username, password } = ctx._request?.body || ctx.body || {};
|
|
164
|
+
|
|
165
|
+
if (username === "admin" && password === "password") {
|
|
166
|
+
ctx.session?.set("user", {
|
|
167
|
+
id: 1,
|
|
168
|
+
username,
|
|
169
|
+
role: "admin",
|
|
170
|
+
loggedIn: true,
|
|
171
|
+
});
|
|
172
|
+
ctx.session?.save();
|
|
173
|
+
|
|
174
|
+
ctx.setStatus(200);
|
|
175
|
+
ctx.json({ message: "Login successful" });
|
|
176
|
+
} else {
|
|
177
|
+
ctx.setStatus(401);
|
|
178
|
+
ctx.json({ error: "Invalid credentials" });
|
|
179
|
+
}
|
|
180
|
+
if (typeof ctx.terminate === "function") ctx.terminate();
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
return next(); // Pass control
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// 10. Logout Route
|
|
187
|
+
pipeline.use((ctx, next) => {
|
|
188
|
+
if (ctx.isTerminated() || ctx.res?.writableEnded) return;
|
|
189
|
+
|
|
190
|
+
if (ctx.url === "/api/logout" && ctx.method === "POST") {
|
|
191
|
+
ctx.session?.destroy();
|
|
192
|
+
ctx.setStatus(200);
|
|
193
|
+
ctx.json({ message: "Logged out successfully" });
|
|
194
|
+
if (typeof ctx.terminate === "function") ctx.terminate();
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
return next(); // Pass control
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// 11. JWT Protection for /api/* routes
|
|
201
|
+
pipeline.use(async (ctx, next) => {
|
|
202
|
+
if (ctx.isTerminated() || ctx.res?.writableEnded) return;
|
|
203
|
+
if (ctx.url.startsWith("/api/")) {
|
|
204
|
+
// Standard parameter passing: pass Aether's next-level async dispatch pointer
|
|
205
|
+
await jwtMiddlewareInstance(ctx, next);
|
|
206
|
+
} else {
|
|
207
|
+
return next(); // Skip non-/api/ routes directly
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
// 12. Profile Route
|
|
212
|
+
pipeline.use((ctx, next) => {
|
|
213
|
+
if (ctx.isTerminated() || ctx.res?.writableEnded) return;
|
|
214
|
+
if (ctx.url === "/api/profile" && ctx.method === "GET") {
|
|
215
|
+
ctx.setStatus(200);
|
|
216
|
+
ctx.json({
|
|
217
|
+
user: { id: 1, name: "Mock User" },
|
|
218
|
+
message: "Access granted",
|
|
219
|
+
});
|
|
220
|
+
if (typeof ctx.terminate === "function") ctx.terminate();
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
return next(); // Pass control
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
// 13. POST Example
|
|
227
|
+
pipeline.use((ctx, next) => {
|
|
228
|
+
if (ctx.isTerminated() || ctx.res?.writableEnded) return;
|
|
229
|
+
|
|
230
|
+
if (ctx.url === "/api/data" && ctx.method === "POST") {
|
|
231
|
+
const reqBody = ctx._request?.body || ctx.body;
|
|
232
|
+
ctx.setStatus(200);
|
|
233
|
+
ctx.json({
|
|
234
|
+
received: reqBody,
|
|
235
|
+
message: "Data created successfully",
|
|
236
|
+
});
|
|
237
|
+
if (typeof ctx.terminate === "function") ctx.terminate();
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
return next(); // Pass control
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
// 14. Fallback 404
|
|
244
|
+
pipeline.use((ctx) => {
|
|
245
|
+
if (ctx.isTerminated() || ctx.res?.writableEnded) return;
|
|
246
|
+
ctx.setStatus(404);
|
|
247
|
+
ctx.json({ error: "Route not found" });
|
|
248
|
+
if (typeof ctx.terminate === "function") ctx.terminate();
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
// 15. Precompile for optimization
|
|
252
|
+
pipeline.precompile();
|
|
253
|
+
|
|
254
|
+
// 16. Create HTTP server
|
|
255
|
+
const server = http.createServer(async (req, res) => {
|
|
256
|
+
try {
|
|
257
|
+
await pipeline.handle(req, res);
|
|
258
|
+
} catch (err) {
|
|
259
|
+
console.error("Pipeline Error:", err);
|
|
260
|
+
if (!res.headersSent) {
|
|
261
|
+
res.statusCode = 500;
|
|
262
|
+
res.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
263
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
264
|
+
res.end(JSON.stringify({ error: "Internal Server Error" }));
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
// Start server
|
|
270
|
+
server.listen(config.port, () => {
|
|
271
|
+
console.log(`🔒 Advanced Server running on http://localhost:${config.port}`);
|
|
272
|
+
});
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simplified Advanced Server - Clean and Performant Edition
|
|
3
|
+
*/
|
|
4
|
+
import http from "http";
|
|
5
|
+
import { AetherPipeline } from "../index.js";
|
|
6
|
+
|
|
7
|
+
// 1. Create pipeline
|
|
8
|
+
const pipeline = new AetherPipeline();
|
|
9
|
+
|
|
10
|
+
// 2. Basic middleware: Logging
|
|
11
|
+
const logger = (ctx) => {
|
|
12
|
+
console.log(`[${new Date().toISOString()}] ${ctx.method} ${ctx.url}`);
|
|
13
|
+
};
|
|
14
|
+
pipeline.use(logger);
|
|
15
|
+
|
|
16
|
+
// 3. Basic middleware: CORS headers
|
|
17
|
+
pipeline.use((ctx) => {
|
|
18
|
+
ctx.setHeader("Access-Control-Allow-Origin", "*");
|
|
19
|
+
ctx.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
|
|
20
|
+
ctx.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// 4. Basic middleware: Security headers
|
|
24
|
+
pipeline.use((ctx) => {
|
|
25
|
+
ctx.setHeader("X-Content-Type-Options", "nosniff");
|
|
26
|
+
ctx.setHeader("X-Frame-Options", "DENY");
|
|
27
|
+
ctx.setHeader("X-XSS-Protection", "1; mode=block");
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// 5. Route handling
|
|
31
|
+
pipeline.use((ctx) => {
|
|
32
|
+
if (ctx.url === "/health") {
|
|
33
|
+
ctx.setStatus(200);
|
|
34
|
+
ctx.setHeader("Content-Type", "application/json");
|
|
35
|
+
ctx.body = JSON.stringify({
|
|
36
|
+
status: "ok",
|
|
37
|
+
timestamp: Date.now(),
|
|
38
|
+
server: "Simplified AetherJS"
|
|
39
|
+
});
|
|
40
|
+
ctx._isHandled = true;
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
pipeline.use((ctx) => {
|
|
46
|
+
if (ctx.url === "/public/info") {
|
|
47
|
+
ctx.setStatus(200);
|
|
48
|
+
ctx.setHeader("Content-Type", "application/json");
|
|
49
|
+
ctx.body = JSON.stringify({
|
|
50
|
+
message: "This is public information",
|
|
51
|
+
version: "1.0.0",
|
|
52
|
+
endpoints: ["/health", "/public/info", "/api/data"]
|
|
53
|
+
});
|
|
54
|
+
ctx._isHandled = true;
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
pipeline.use((ctx) => {
|
|
60
|
+
if (ctx.url === "/api/data" && ctx.method === "GET") {
|
|
61
|
+
ctx.setStatus(200);
|
|
62
|
+
ctx.setHeader("Content-Type", "application/json");
|
|
63
|
+
ctx.body = JSON.stringify({
|
|
64
|
+
data: [1, 2, 3, 4, 5],
|
|
65
|
+
message: "Sample API data",
|
|
66
|
+
timestamp: Date.now()
|
|
67
|
+
});
|
|
68
|
+
ctx._isHandled = true;
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
pipeline.use((ctx) => {
|
|
74
|
+
if (ctx.url === "/api/data" && ctx.method === "POST") {
|
|
75
|
+
// Simple POST request handling
|
|
76
|
+
let body = "";
|
|
77
|
+
ctx.req.on("data", chunk => {
|
|
78
|
+
body += chunk.toString();
|
|
79
|
+
});
|
|
80
|
+
ctx.req.on("end", () => {
|
|
81
|
+
ctx.setStatus(201);
|
|
82
|
+
ctx.setHeader("Content-Type", "application/json");
|
|
83
|
+
ctx.body = JSON.stringify({
|
|
84
|
+
received: body ? JSON.parse(body) : null,
|
|
85
|
+
message: "Data received successfully",
|
|
86
|
+
id: Date.now()
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
ctx._isHandled = true;
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// 6. 404 handling
|
|
95
|
+
pipeline.use((ctx) => {
|
|
96
|
+
if (ctx._isHandled || ctx.statusCode === 200 || ctx.res?.statusCode === 200) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
ctx.setStatus(404);
|
|
100
|
+
ctx.setHeader("Content-Type", "application/json");
|
|
101
|
+
ctx.body = JSON.stringify({
|
|
102
|
+
error: "Not Found",
|
|
103
|
+
path: ctx.url,
|
|
104
|
+
method: ctx.method
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// 7. Precompile for optimization
|
|
109
|
+
pipeline.precompile();
|
|
110
|
+
|
|
111
|
+
// 8. Create HTTP server
|
|
112
|
+
const server = http.createServer(async (req, res) => {
|
|
113
|
+
try {
|
|
114
|
+
await pipeline.handle(req, res);
|
|
115
|
+
} catch (err) {
|
|
116
|
+
console.error("Pipeline Error:", err);
|
|
117
|
+
if (!res.headersSent) {
|
|
118
|
+
res.statusCode = 500;
|
|
119
|
+
res.setHeader("Content-Type", "application/json");
|
|
120
|
+
res.end(JSON.stringify({ error: "Internal Server Error" }));
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// 9. Start server
|
|
126
|
+
const PORT = process.env.PORT || 3001;
|
|
127
|
+
server.listen(PORT, () => {
|
|
128
|
+
console.log(`🚀 Simplified Advanced Server running on http://localhost:${PORT}`);
|
|
129
|
+
console.log(`📊 Available endpoints:`);
|
|
130
|
+
console.log(` GET /health - Health check`);
|
|
131
|
+
console.log(` GET /public/info - Public information`);
|
|
132
|
+
console.log(` GET /api/data - Get sample data`);
|
|
133
|
+
console.log(` POST /api/data - Submit data`);
|
|
134
|
+
});
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple Benchmark Script
|
|
3
|
+
* Compares raw HTTP vs Aether Pipeline overhead.
|
|
4
|
+
*/
|
|
5
|
+
import http from 'http';
|
|
6
|
+
import { AetherPipeline, AetherContext } from '../index.js';
|
|
7
|
+
import json from "../src/middleware/json.js";
|
|
8
|
+
|
|
9
|
+
const ITERATIONS = 10000;
|
|
10
|
+
|
|
11
|
+
// 1. Raw HTTP Server
|
|
12
|
+
const rawServer = http.createServer((req, res) => {
|
|
13
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
14
|
+
res.end(JSON.stringify({ status: 'ok' }));
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
// 2. Aether Server
|
|
18
|
+
const pipeline = new AetherPipeline();
|
|
19
|
+
pipeline.use(json());
|
|
20
|
+
pipeline.use(async (ctx, signal) => {
|
|
21
|
+
ctx.body = { status: 'ok' };
|
|
22
|
+
await signal.next();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const aetherServer = http.createServer(async (req, res) => {
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
if (!req || !res) {
|
|
29
|
+
console.error('ERROR: req or res is undefined');
|
|
30
|
+
res.writeHead(500);
|
|
31
|
+
res.end('Internal Server Error');
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
await pipeline.handle(req, res);
|
|
36
|
+
} catch (error) {
|
|
37
|
+
console.error('Pipeline execution error:', error);
|
|
38
|
+
if (!res.headersSent) {
|
|
39
|
+
res.writeHead(500);
|
|
40
|
+
res.end('Internal Server Error');
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
async function runBenchmark(server, name) {
|
|
46
|
+
return new Promise((resolve) => {
|
|
47
|
+
server.listen(0, () => {
|
|
48
|
+
const port = server.address().port;
|
|
49
|
+
const start = Date.now();
|
|
50
|
+
let completed = 0;
|
|
51
|
+
|
|
52
|
+
function makeRequest() {
|
|
53
|
+
http.get(`http://localhost:${port}/`, (res) => {
|
|
54
|
+
res.resume();
|
|
55
|
+
res.on('end', () => {
|
|
56
|
+
completed++;
|
|
57
|
+
if (completed < ITERATIONS) {
|
|
58
|
+
makeRequest();
|
|
59
|
+
} else {
|
|
60
|
+
const duration = Date.now() - start;
|
|
61
|
+
const rps = (ITERATIONS / duration) * 1000;
|
|
62
|
+
console.log(`${name}: ${duration}ms for ${ITERATIONS} requests (${rps.toFixed(0)} req/sec)`);
|
|
63
|
+
server.close(() => resolve());
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
}).on('error', (e) => {
|
|
67
|
+
console.error(e);
|
|
68
|
+
server.close(() => resolve());
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
makeRequest();
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function main() {
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
await runBenchmark(rawServer, 'Raw HTTP');
|
|
81
|
+
await runBenchmark(aetherServer, 'Aether Pipeline');
|
|
82
|
+
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
main().catch(console.error);
|