qhttpx 1.9.4 → 2.0.0
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/CHANGELOG.md +21 -0
- package/README.md +17 -12
- package/dist/examples/api-server.js +38 -35
- package/dist/examples/basic.js +3 -4
- package/dist/examples/compression.js +6 -8
- package/dist/examples/cors.js +5 -6
- package/dist/examples/errors.js +12 -11
- package/dist/examples/file-upload.js +4 -6
- package/dist/examples/fusion.js +6 -6
- package/dist/examples/rate-limiting.js +10 -10
- package/dist/examples/validation.js +5 -6
- package/dist/examples/websockets.js +3 -4
- package/dist/package.json +3 -8
- package/dist/src/benchmarks/quantam-users.js +2 -2
- package/dist/src/benchmarks/quick-bench.js +57 -0
- package/dist/src/benchmarks/simple-json.js +133 -22
- package/dist/src/benchmarks/ultra-mode.js +8 -38
- package/dist/src/core/context-pool.d.ts +12 -0
- package/dist/src/core/context-pool.js +34 -0
- package/dist/src/core/fusion.js +0 -2
- package/dist/src/core/metrics.d.ts +1 -0
- package/dist/src/core/metrics.js +3 -0
- package/dist/src/core/scheduler.d.ts +4 -0
- package/dist/src/core/scheduler.js +75 -34
- package/dist/src/core/scope.d.ts +23 -8
- package/dist/src/core/scope.js +53 -14
- package/dist/src/core/serializer.d.ts +1 -1
- package/dist/src/core/serializer.js +45 -7
- package/dist/src/core/server.d.ts +51 -10
- package/dist/src/core/server.js +695 -259
- package/dist/src/core/timer.d.ts +11 -0
- package/dist/src/core/timer.js +29 -0
- package/dist/src/core/types.d.ts +41 -13
- package/dist/src/core/types.js +6 -6
- package/dist/src/index.d.ts +6 -4
- package/dist/src/index.js +19 -20
- package/dist/src/middleware/security.js +6 -1
- package/dist/src/router/radix-tree.d.ts +5 -2
- package/dist/src/router/radix-tree.js +58 -14
- package/dist/src/router/router.d.ts +5 -2
- package/dist/src/router/router.js +80 -63
- package/dist/tests/fusion.test.js +4 -4
- package/dist/tests/rate-limit.test.js +2 -2
- package/dist/tests/schema-routes.test.js +3 -1
- package/docs/AEGIS.md +18 -28
- package/docs/BENCHMARKS.md +8 -6
- package/docs/DATABASE.md +4 -4
- package/docs/MIDDLEWARE.md +3 -3
- package/docs/ROUTING.md +21 -13
- package/docs/VALIDATION.md +9 -31
- package/package.json +3 -8
- package/binding.gyp +0 -18
- package/dist/src/benchmarks/compare-frameworks.js +0 -119
- package/dist/src/benchmarks/compare.js +0 -288
- package/dist/src/buffer-pool.js +0 -70
- package/dist/src/config.js +0 -50
- package/dist/src/cookies.js +0 -59
- package/dist/src/core/native-adapter.d.ts +0 -11
- package/dist/src/core/native-adapter.js +0 -211
- package/dist/src/cors.js +0 -66
- package/dist/src/logger.js +0 -45
- package/dist/src/metrics.js +0 -111
- package/dist/src/native/index.d.ts +0 -32
- package/dist/src/native/index.js +0 -141
- package/dist/src/presets.js +0 -33
- package/dist/src/radix-router.js +0 -89
- package/dist/src/radix-tree.js +0 -81
- package/dist/src/resources.js +0 -25
- package/dist/src/router.js +0 -138
- package/dist/src/scheduler.js +0 -85
- package/dist/src/security.js +0 -69
- package/dist/src/server.js +0 -685
- package/dist/src/signals.js +0 -31
- package/dist/src/static.js +0 -107
- package/dist/src/stream.js +0 -71
- package/dist/src/tasks.js +0 -87
- package/dist/src/testing.js +0 -40
- package/dist/src/types.js +0 -19
- package/dist/src/utils/testing.js +0 -40
- package/dist/src/worker-queue.js +0 -73
- package/dist/tests/native-adapter.test.d.ts +0 -1
- package/dist/tests/native-adapter.test.js +0 -71
- package/prebuilds/darwin-arm64/qhttpx.node +0 -0
- package/prebuilds/linux-x64/qhttpx.node +0 -0
- package/prebuilds/win32-x64/qhttpx.node +0 -0
- package/scripts/install-native.js +0 -26
- package/src/native/README.md +0 -31
- package/src/native/addon.cc +0 -8
- package/src/native/index.ts +0 -158
- package/src/native/picohttpparser.c +0 -608
- package/src/native/picohttpparser.h +0 -76
- package/src/native/server.cc +0 -264
- package/src/native/server.h +0 -30
- /package/dist/src/benchmarks/{compare.d.ts → quick-bench.d.ts} +0 -0
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const autocannon_1 = __importDefault(require("autocannon"));
|
|
7
|
-
const fastify_1 = __importDefault(require("fastify"));
|
|
8
|
-
const express_1 = __importDefault(require("express"));
|
|
9
|
-
const index_1 = require("../index");
|
|
10
|
-
async function runAutocannon(name, url) {
|
|
11
|
-
const result = await (0, autocannon_1.default)({
|
|
12
|
-
url,
|
|
13
|
-
connections: 200,
|
|
14
|
-
pipelining: 10,
|
|
15
|
-
duration: 10,
|
|
16
|
-
});
|
|
17
|
-
const total = result.requests.total;
|
|
18
|
-
const sent = result.requests.sent;
|
|
19
|
-
const rps = result.requests.average;
|
|
20
|
-
const p99 = result.latency.p99;
|
|
21
|
-
console.log(`${name} bench: total=${total} (sent=${sent}) req, ` +
|
|
22
|
-
`${rps.toFixed(0)} req/sec, p99=${p99.toFixed(1)}ms, connections=${result.connections}, pipelining=${result.pipelining}`);
|
|
23
|
-
return {
|
|
24
|
-
name,
|
|
25
|
-
total,
|
|
26
|
-
sent,
|
|
27
|
-
rps,
|
|
28
|
-
p99,
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
async function startQHTTPX() {
|
|
32
|
-
const payloadBuffer = Buffer.from(JSON.stringify({ message: 'hello from qhttpx' }));
|
|
33
|
-
const app = new index_1.QHTTPX({
|
|
34
|
-
maxConcurrency: 1024,
|
|
35
|
-
metricsEnabled: false,
|
|
36
|
-
jsonSerializer: () => payloadBuffer,
|
|
37
|
-
});
|
|
38
|
-
app.get('/json', (ctx) => {
|
|
39
|
-
ctx.json({ message: 'hello from qhttpx' });
|
|
40
|
-
});
|
|
41
|
-
const { port } = await app.listen(0, '127.0.0.1');
|
|
42
|
-
const url = `http://127.0.0.1:${port}/json`;
|
|
43
|
-
return { app, url };
|
|
44
|
-
}
|
|
45
|
-
async function startFastify() {
|
|
46
|
-
const app = (0, fastify_1.default)();
|
|
47
|
-
app.get('/json', async () => {
|
|
48
|
-
return { message: 'hello from fastify' };
|
|
49
|
-
});
|
|
50
|
-
await app.listen({ port: 0, host: '127.0.0.1' });
|
|
51
|
-
const address = app.server.address();
|
|
52
|
-
if (!address || typeof address === 'string') {
|
|
53
|
-
throw new Error('Fastify address not available');
|
|
54
|
-
}
|
|
55
|
-
const url = `http://127.0.0.1:${address.port}/json`;
|
|
56
|
-
return { app, url };
|
|
57
|
-
}
|
|
58
|
-
async function startExpress() {
|
|
59
|
-
const app = (0, express_1.default)();
|
|
60
|
-
app.get('/json', (_req, res) => {
|
|
61
|
-
res.json({ message: 'hello from express' });
|
|
62
|
-
});
|
|
63
|
-
const server = await new Promise((resolve) => {
|
|
64
|
-
const s = app.listen(0, '127.0.0.1', () => {
|
|
65
|
-
resolve(s);
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
const address = server.address();
|
|
69
|
-
if (!address || typeof address === 'string') {
|
|
70
|
-
throw new Error('Express address not available');
|
|
71
|
-
}
|
|
72
|
-
const url = `http://127.0.0.1:${address.port}/json`;
|
|
73
|
-
return { app, url, server };
|
|
74
|
-
}
|
|
75
|
-
async function run() {
|
|
76
|
-
const results = [];
|
|
77
|
-
const qhttpx = await startQHTTPX();
|
|
78
|
-
try {
|
|
79
|
-
const r = await runAutocannon('QHTTPX', qhttpx.url);
|
|
80
|
-
results.push(r);
|
|
81
|
-
}
|
|
82
|
-
finally {
|
|
83
|
-
await qhttpx.app.close();
|
|
84
|
-
}
|
|
85
|
-
const fast = await startFastify();
|
|
86
|
-
try {
|
|
87
|
-
const r = await runAutocannon('Fastify', fast.url);
|
|
88
|
-
results.push(r);
|
|
89
|
-
}
|
|
90
|
-
finally {
|
|
91
|
-
await fast.app.close();
|
|
92
|
-
}
|
|
93
|
-
const exp = await startExpress();
|
|
94
|
-
try {
|
|
95
|
-
const r = await runAutocannon('Express', exp.url);
|
|
96
|
-
results.push(r);
|
|
97
|
-
}
|
|
98
|
-
finally {
|
|
99
|
-
await new Promise((resolve, reject) => {
|
|
100
|
-
exp.server.close((err) => {
|
|
101
|
-
if (err) {
|
|
102
|
-
reject(err);
|
|
103
|
-
}
|
|
104
|
-
else {
|
|
105
|
-
resolve();
|
|
106
|
-
}
|
|
107
|
-
});
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
console.log('\nSummary:');
|
|
111
|
-
for (const r of results) {
|
|
112
|
-
console.log(`${r.name}: ${r.rps.toFixed(0)} req/sec, p99=${r.p99.toFixed(1)}ms, total=${r.total}`);
|
|
113
|
-
}
|
|
114
|
-
process.exit(0);
|
|
115
|
-
}
|
|
116
|
-
run().catch((err) => {
|
|
117
|
-
console.error(err);
|
|
118
|
-
process.exit(1);
|
|
119
|
-
});
|
|
@@ -1,288 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const autocannon_1 = __importDefault(require("autocannon"));
|
|
7
|
-
const index_1 = require("../index");
|
|
8
|
-
const fastify_1 = __importDefault(require("fastify"));
|
|
9
|
-
const express_1 = __importDefault(require("express"));
|
|
10
|
-
const hono_1 = require("hono");
|
|
11
|
-
const node_server_1 = require("@hono/node-server");
|
|
12
|
-
const koa_1 = __importDefault(require("koa"));
|
|
13
|
-
const polka_1 = __importDefault(require("polka"));
|
|
14
|
-
const http_1 = __importDefault(require("http"));
|
|
15
|
-
const PORT = 3000;
|
|
16
|
-
const DURATION = 300; // Testing duration
|
|
17
|
-
const CONNECTIONS = 500; // Connections
|
|
18
|
-
const PIPELINING = 20; // Request pipelining
|
|
19
|
-
const results = [];
|
|
20
|
-
function printProgress(name, timeLeft, total) {
|
|
21
|
-
const percent = Math.max(0, Math.min(100, Math.round(((total - timeLeft) / total) * 100)));
|
|
22
|
-
const barLength = 30;
|
|
23
|
-
const filled = Math.round((barLength * percent) / 100);
|
|
24
|
-
const bar = '█'.repeat(filled) + '░'.repeat(barLength - filled);
|
|
25
|
-
// Clear line and move cursor to start
|
|
26
|
-
process.stdout.clearLine(0);
|
|
27
|
-
process.stdout.cursorTo(0);
|
|
28
|
-
process.stdout.write(`${name} [${bar}] ${percent}% | Time Left: ${timeLeft}s`);
|
|
29
|
-
}
|
|
30
|
-
const frameworks = [
|
|
31
|
-
{
|
|
32
|
-
name: 'QHTTPX (Native)',
|
|
33
|
-
control: () => {
|
|
34
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
35
|
-
let app;
|
|
36
|
-
return {
|
|
37
|
-
start: async () => {
|
|
38
|
-
return new Promise((resolve) => {
|
|
39
|
-
app = (0, index_1.createHttpApp)();
|
|
40
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
41
|
-
app.get('/', ({ json }) => json({ hello: 'world' }));
|
|
42
|
-
app.listen(PORT, () => resolve());
|
|
43
|
-
});
|
|
44
|
-
},
|
|
45
|
-
stop: async () => {
|
|
46
|
-
await app.close();
|
|
47
|
-
},
|
|
48
|
-
};
|
|
49
|
-
},
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
name: 'Fastify',
|
|
53
|
-
control: () => {
|
|
54
|
-
const app = (0, fastify_1.default)();
|
|
55
|
-
app.get('/', async () => ({ hello: 'world' }));
|
|
56
|
-
return {
|
|
57
|
-
start: async () => {
|
|
58
|
-
await app.listen({ port: PORT });
|
|
59
|
-
},
|
|
60
|
-
stop: async () => {
|
|
61
|
-
await app.close();
|
|
62
|
-
},
|
|
63
|
-
};
|
|
64
|
-
},
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
name: 'Hono (Node)',
|
|
68
|
-
control: () => {
|
|
69
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
70
|
-
let server;
|
|
71
|
-
return {
|
|
72
|
-
start: async () => {
|
|
73
|
-
const app = new hono_1.Hono();
|
|
74
|
-
app.get('/', (c) => c.json({ hello: 'world' }));
|
|
75
|
-
return new Promise((resolve, reject) => {
|
|
76
|
-
try {
|
|
77
|
-
server = (0, node_server_1.serve)({ fetch: app.fetch, port: PORT }, () => resolve());
|
|
78
|
-
server.on('error', reject);
|
|
79
|
-
}
|
|
80
|
-
catch (e) {
|
|
81
|
-
reject(e);
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
},
|
|
85
|
-
stop: async () => {
|
|
86
|
-
return new Promise((resolve) => {
|
|
87
|
-
if (server && typeof server.close === 'function') {
|
|
88
|
-
server.removeAllListeners('error');
|
|
89
|
-
server.close(() => resolve());
|
|
90
|
-
}
|
|
91
|
-
else {
|
|
92
|
-
resolve();
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
},
|
|
96
|
-
};
|
|
97
|
-
},
|
|
98
|
-
},
|
|
99
|
-
{
|
|
100
|
-
name: 'Express',
|
|
101
|
-
control: () => {
|
|
102
|
-
let server;
|
|
103
|
-
return {
|
|
104
|
-
start: async () => {
|
|
105
|
-
return new Promise((resolve) => {
|
|
106
|
-
const app = (0, express_1.default)();
|
|
107
|
-
app.disable('etag');
|
|
108
|
-
app.disable('x-powered-by');
|
|
109
|
-
app.get('/', (req, res) => res.json({ hello: 'world' }));
|
|
110
|
-
server = app.listen(PORT, () => resolve());
|
|
111
|
-
});
|
|
112
|
-
},
|
|
113
|
-
stop: async () => {
|
|
114
|
-
return new Promise((resolve) => {
|
|
115
|
-
server.close(() => resolve());
|
|
116
|
-
});
|
|
117
|
-
},
|
|
118
|
-
};
|
|
119
|
-
},
|
|
120
|
-
},
|
|
121
|
-
{
|
|
122
|
-
name: 'Koa',
|
|
123
|
-
control: () => {
|
|
124
|
-
let server;
|
|
125
|
-
return {
|
|
126
|
-
start: async () => {
|
|
127
|
-
return new Promise((resolve) => {
|
|
128
|
-
const app = new koa_1.default();
|
|
129
|
-
app.use((ctx) => {
|
|
130
|
-
ctx.body = { hello: 'world' };
|
|
131
|
-
});
|
|
132
|
-
server = app.listen(PORT, () => resolve());
|
|
133
|
-
});
|
|
134
|
-
},
|
|
135
|
-
stop: async () => {
|
|
136
|
-
return new Promise((resolve) => {
|
|
137
|
-
server.close(() => resolve());
|
|
138
|
-
});
|
|
139
|
-
},
|
|
140
|
-
};
|
|
141
|
-
},
|
|
142
|
-
},
|
|
143
|
-
{
|
|
144
|
-
name: 'Polka',
|
|
145
|
-
control: () => {
|
|
146
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
147
|
-
let server;
|
|
148
|
-
return {
|
|
149
|
-
start: async () => {
|
|
150
|
-
return new Promise((resolve) => {
|
|
151
|
-
const app = (0, polka_1.default)();
|
|
152
|
-
app.get('/', (req, res) => {
|
|
153
|
-
res.setHeader('Content-Type', 'application/json');
|
|
154
|
-
res.end(JSON.stringify({ hello: 'world' }));
|
|
155
|
-
});
|
|
156
|
-
server = app.listen(PORT, () => resolve());
|
|
157
|
-
});
|
|
158
|
-
},
|
|
159
|
-
stop: async () => {
|
|
160
|
-
return new Promise((resolve) => {
|
|
161
|
-
server.server.close(() => resolve());
|
|
162
|
-
});
|
|
163
|
-
},
|
|
164
|
-
};
|
|
165
|
-
},
|
|
166
|
-
},
|
|
167
|
-
{
|
|
168
|
-
name: 'Restify',
|
|
169
|
-
control: () => {
|
|
170
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
171
|
-
let server;
|
|
172
|
-
return {
|
|
173
|
-
start: async () => {
|
|
174
|
-
return new Promise((resolve, reject) => {
|
|
175
|
-
try {
|
|
176
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
177
|
-
const restify = require('restify');
|
|
178
|
-
server = restify.createServer();
|
|
179
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
180
|
-
server.get('/', (req, res, next) => {
|
|
181
|
-
res.send({ hello: 'world' });
|
|
182
|
-
next();
|
|
183
|
-
});
|
|
184
|
-
server.listen(PORT, () => resolve());
|
|
185
|
-
}
|
|
186
|
-
catch (e) {
|
|
187
|
-
reject(e);
|
|
188
|
-
}
|
|
189
|
-
});
|
|
190
|
-
},
|
|
191
|
-
stop: async () => {
|
|
192
|
-
return new Promise((resolve) => {
|
|
193
|
-
if (server)
|
|
194
|
-
server.close(() => resolve());
|
|
195
|
-
else
|
|
196
|
-
resolve();
|
|
197
|
-
});
|
|
198
|
-
},
|
|
199
|
-
};
|
|
200
|
-
},
|
|
201
|
-
},
|
|
202
|
-
{
|
|
203
|
-
name: 'Node Native',
|
|
204
|
-
control: () => {
|
|
205
|
-
let server;
|
|
206
|
-
return {
|
|
207
|
-
start: async () => {
|
|
208
|
-
return new Promise((resolve) => {
|
|
209
|
-
server = http_1.default.createServer((req, res) => {
|
|
210
|
-
res.setHeader('Content-Type', 'application/json');
|
|
211
|
-
res.end(JSON.stringify({ hello: 'world' }));
|
|
212
|
-
});
|
|
213
|
-
server.listen(PORT, () => resolve());
|
|
214
|
-
});
|
|
215
|
-
},
|
|
216
|
-
stop: async () => {
|
|
217
|
-
return new Promise((resolve) => {
|
|
218
|
-
server.close(() => resolve());
|
|
219
|
-
});
|
|
220
|
-
},
|
|
221
|
-
};
|
|
222
|
-
},
|
|
223
|
-
},
|
|
224
|
-
];
|
|
225
|
-
async function run() {
|
|
226
|
-
const origLog = console.log;
|
|
227
|
-
const origError = console.error;
|
|
228
|
-
const origWarn = console.warn;
|
|
229
|
-
origLog(`\nStarting Benchmark Comparison`);
|
|
230
|
-
origLog(`Duration: ${DURATION}s | Connections: ${CONNECTIONS}\n`);
|
|
231
|
-
for (const fw of frameworks) {
|
|
232
|
-
origLog(`Preparing ${fw.name}...`);
|
|
233
|
-
const ctrl = fw.control();
|
|
234
|
-
try {
|
|
235
|
-
await ctrl.start();
|
|
236
|
-
// Wait for server to be fully ready
|
|
237
|
-
await new Promise((r) => setTimeout(r, 500));
|
|
238
|
-
const startTime = Date.now();
|
|
239
|
-
const interval = setInterval(() => {
|
|
240
|
-
const elapsed = (Date.now() - startTime) / 1000;
|
|
241
|
-
const timeLeft = Math.max(0, Math.round(DURATION - elapsed));
|
|
242
|
-
printProgress(fw.name, timeLeft, DURATION);
|
|
243
|
-
}, 200);
|
|
244
|
-
// Suppress autocannon logs
|
|
245
|
-
console.log = () => { };
|
|
246
|
-
console.error = () => { };
|
|
247
|
-
console.warn = () => { };
|
|
248
|
-
const result = await (0, autocannon_1.default)({
|
|
249
|
-
url: `http://localhost:${PORT}`,
|
|
250
|
-
connections: CONNECTIONS,
|
|
251
|
-
pipelining: PIPELINING,
|
|
252
|
-
duration: DURATION,
|
|
253
|
-
});
|
|
254
|
-
console.log = origLog;
|
|
255
|
-
console.error = origError;
|
|
256
|
-
console.warn = origWarn;
|
|
257
|
-
clearInterval(interval);
|
|
258
|
-
printProgress(fw.name, 0, DURATION);
|
|
259
|
-
origLog('\n');
|
|
260
|
-
results.push({
|
|
261
|
-
name: fw.name,
|
|
262
|
-
reqs: result.requests.average,
|
|
263
|
-
latency: result.latency.average,
|
|
264
|
-
p50: result.latency.p50,
|
|
265
|
-
p90: result.latency.p90,
|
|
266
|
-
p99: result.latency.p99,
|
|
267
|
-
max: result.latency.max,
|
|
268
|
-
throughput: (result.throughput.average / 1024 / 1024).toFixed(2),
|
|
269
|
-
});
|
|
270
|
-
await ctrl.stop();
|
|
271
|
-
}
|
|
272
|
-
catch (err) {
|
|
273
|
-
console.log = origLog;
|
|
274
|
-
console.error = origError;
|
|
275
|
-
console.warn = origWarn;
|
|
276
|
-
origError(`Error benchmarking ${fw.name}:`, err);
|
|
277
|
-
try {
|
|
278
|
-
await ctrl.stop();
|
|
279
|
-
}
|
|
280
|
-
catch { }
|
|
281
|
-
}
|
|
282
|
-
// Cool down - allow time for port to be released
|
|
283
|
-
await new Promise((r) => setTimeout(r, 3000));
|
|
284
|
-
}
|
|
285
|
-
origLog('\n--- Final Results ---');
|
|
286
|
-
console.table(results.sort((a, b) => b.reqs - a.reqs));
|
|
287
|
-
}
|
|
288
|
-
run().catch(console.error);
|
package/dist/src/buffer-pool.js
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Buffer pool for response bodies (small, medium, large).
|
|
4
|
-
* Reuses allocated buffers to reduce GC pressure.
|
|
5
|
-
*/
|
|
6
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.BufferPool = void 0;
|
|
8
|
-
class BufferPool {
|
|
9
|
-
constructor(config = {}) {
|
|
10
|
-
this.smallBuffers = [];
|
|
11
|
-
this.mediumBuffers = [];
|
|
12
|
-
this.largeBuffers = [];
|
|
13
|
-
this.smallSize = config.smallSize ?? 4096; // 4KB
|
|
14
|
-
this.mediumSize = config.mediumSize ?? 65536; // 64KB
|
|
15
|
-
this.largeSize = config.largeSize ?? 262144; // 256KB
|
|
16
|
-
this.smallPoolSize = config.smallPoolSize ?? 32;
|
|
17
|
-
this.mediumPoolSize = config.mediumPoolSize ?? 8;
|
|
18
|
-
this.largePoolSize = config.largePoolSize ?? 2;
|
|
19
|
-
// Preallocate buffers
|
|
20
|
-
for (let i = 0; i < this.smallPoolSize; i += 1) {
|
|
21
|
-
this.smallBuffers.push(Buffer.allocUnsafe(this.smallSize));
|
|
22
|
-
}
|
|
23
|
-
for (let i = 0; i < this.mediumPoolSize; i += 1) {
|
|
24
|
-
this.mediumBuffers.push(Buffer.allocUnsafe(this.mediumSize));
|
|
25
|
-
}
|
|
26
|
-
for (let i = 0; i < this.largePoolSize; i += 1) {
|
|
27
|
-
this.largeBuffers.push(Buffer.allocUnsafe(this.largeSize));
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Acquire a buffer suitable for the given size.
|
|
32
|
-
* Returns a buffer from the appropriate pool.
|
|
33
|
-
*/
|
|
34
|
-
acquire(size) {
|
|
35
|
-
if (size <= this.smallSize) {
|
|
36
|
-
return this.smallBuffers.pop() || Buffer.allocUnsafe(this.smallSize);
|
|
37
|
-
}
|
|
38
|
-
if (size <= this.mediumSize) {
|
|
39
|
-
return this.mediumBuffers.pop() || Buffer.allocUnsafe(this.mediumSize);
|
|
40
|
-
}
|
|
41
|
-
return this.largeBuffers.pop() || Buffer.allocUnsafe(this.largeSize);
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Release a buffer back to the appropriate pool.
|
|
45
|
-
*/
|
|
46
|
-
release(buffer) {
|
|
47
|
-
if (buffer.length === this.smallSize && this.smallBuffers.length < this.smallPoolSize) {
|
|
48
|
-
this.smallBuffers.push(buffer);
|
|
49
|
-
}
|
|
50
|
-
else if (buffer.length === this.mediumSize &&
|
|
51
|
-
this.mediumBuffers.length < this.mediumPoolSize) {
|
|
52
|
-
this.mediumBuffers.push(buffer);
|
|
53
|
-
}
|
|
54
|
-
else if (buffer.length === this.largeSize &&
|
|
55
|
-
this.largeBuffers.length < this.largePoolSize) {
|
|
56
|
-
this.largeBuffers.push(buffer);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Get the number of available buffers in each pool.
|
|
61
|
-
*/
|
|
62
|
-
getPoolStatus() {
|
|
63
|
-
return {
|
|
64
|
-
small: this.smallBuffers.length,
|
|
65
|
-
medium: this.mediumBuffers.length,
|
|
66
|
-
large: this.largeBuffers.length,
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
exports.BufferPool = BufferPool;
|
package/dist/src/config.js
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.loadConfig = loadConfig;
|
|
4
|
-
function loadConfig(options = {}) {
|
|
5
|
-
const env = options.env ?? process.env;
|
|
6
|
-
const defaults = options.defaults ?? {};
|
|
7
|
-
const prefix = options.prefix ?? 'QHTTPX_';
|
|
8
|
-
const getNumber = (key) => {
|
|
9
|
-
const value = env[prefix + key];
|
|
10
|
-
if (!value) {
|
|
11
|
-
return undefined;
|
|
12
|
-
}
|
|
13
|
-
const n = Number(value);
|
|
14
|
-
if (!Number.isFinite(n) || n <= 0) {
|
|
15
|
-
return undefined;
|
|
16
|
-
}
|
|
17
|
-
return n;
|
|
18
|
-
};
|
|
19
|
-
const getBoolean = (key) => {
|
|
20
|
-
const value = env[prefix + key];
|
|
21
|
-
if (!value) {
|
|
22
|
-
return undefined;
|
|
23
|
-
}
|
|
24
|
-
const lower = value.toLowerCase();
|
|
25
|
-
if (lower === 'true' || lower === '1') {
|
|
26
|
-
return true;
|
|
27
|
-
}
|
|
28
|
-
if (lower === 'false' || lower === '0') {
|
|
29
|
-
return false;
|
|
30
|
-
}
|
|
31
|
-
return undefined;
|
|
32
|
-
};
|
|
33
|
-
const maxConcurrency = getNumber('MAX_CONCURRENCY');
|
|
34
|
-
const requestTimeoutMs = getNumber('REQUEST_TIMEOUT_MS');
|
|
35
|
-
const maxMemoryBytes = getNumber('MAX_MEMORY_BYTES');
|
|
36
|
-
const maxBodyBytes = getNumber('MAX_BODY_BYTES');
|
|
37
|
-
const keepAliveTimeoutMs = getNumber('KEEP_ALIVE_TIMEOUT_MS');
|
|
38
|
-
const headersTimeoutMs = getNumber('HEADERS_TIMEOUT_MS');
|
|
39
|
-
const metricsEnabled = getBoolean('METRICS_ENABLED');
|
|
40
|
-
return {
|
|
41
|
-
...defaults,
|
|
42
|
-
maxConcurrency: maxConcurrency ?? defaults.maxConcurrency,
|
|
43
|
-
requestTimeoutMs: requestTimeoutMs ?? defaults.requestTimeoutMs,
|
|
44
|
-
maxMemoryBytes: maxMemoryBytes ?? defaults.maxMemoryBytes,
|
|
45
|
-
maxBodyBytes: maxBodyBytes ?? defaults.maxBodyBytes,
|
|
46
|
-
keepAliveTimeoutMs: keepAliveTimeoutMs ?? defaults.keepAliveTimeoutMs,
|
|
47
|
-
headersTimeoutMs: headersTimeoutMs ?? defaults.headersTimeoutMs,
|
|
48
|
-
metricsEnabled: metricsEnabled ?? defaults.metricsEnabled,
|
|
49
|
-
};
|
|
50
|
-
}
|
package/dist/src/cookies.js
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.parseCookies = parseCookies;
|
|
4
|
-
exports.serializeCookie = serializeCookie;
|
|
5
|
-
function parseCookies(header) {
|
|
6
|
-
const list = {};
|
|
7
|
-
if (!header) {
|
|
8
|
-
return list;
|
|
9
|
-
}
|
|
10
|
-
header.split(';').forEach((cookie) => {
|
|
11
|
-
const parts = cookie.split('=');
|
|
12
|
-
const name = parts.shift()?.trim();
|
|
13
|
-
if (name) {
|
|
14
|
-
const value = parts.join('=');
|
|
15
|
-
list[name] = decodeURIComponent(value);
|
|
16
|
-
}
|
|
17
|
-
});
|
|
18
|
-
return list;
|
|
19
|
-
}
|
|
20
|
-
function serializeCookie(name, value, options = {}) {
|
|
21
|
-
let str = `${name}=${encodeURIComponent(value)}`;
|
|
22
|
-
if (options.maxAge) {
|
|
23
|
-
str += `; Max-Age=${Math.floor(options.maxAge)}`;
|
|
24
|
-
}
|
|
25
|
-
if (options.domain) {
|
|
26
|
-
str += `; Domain=${options.domain}`;
|
|
27
|
-
}
|
|
28
|
-
if (options.path) {
|
|
29
|
-
str += `; Path=${options.path}`;
|
|
30
|
-
}
|
|
31
|
-
else {
|
|
32
|
-
str += '; Path=/';
|
|
33
|
-
}
|
|
34
|
-
if (options.expires) {
|
|
35
|
-
str += `; Expires=${options.expires.toUTCString()}`;
|
|
36
|
-
}
|
|
37
|
-
if (options.httpOnly) {
|
|
38
|
-
str += '; HttpOnly';
|
|
39
|
-
}
|
|
40
|
-
if (options.secure) {
|
|
41
|
-
str += '; Secure';
|
|
42
|
-
}
|
|
43
|
-
if (options.sameSite) {
|
|
44
|
-
switch (options.sameSite) {
|
|
45
|
-
case 'lax':
|
|
46
|
-
str += '; SameSite=Lax';
|
|
47
|
-
break;
|
|
48
|
-
case 'strict':
|
|
49
|
-
str += '; SameSite=Strict';
|
|
50
|
-
break;
|
|
51
|
-
case 'none':
|
|
52
|
-
str += '; SameSite=None';
|
|
53
|
-
break;
|
|
54
|
-
default:
|
|
55
|
-
break;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
return str;
|
|
59
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import net from 'net';
|
|
2
|
-
import { QHTTPX } from './server';
|
|
3
|
-
export declare class NativeAdapter {
|
|
4
|
-
private app;
|
|
5
|
-
private nativeServer;
|
|
6
|
-
private responsePool;
|
|
7
|
-
constructor(app: QHTTPX);
|
|
8
|
-
listen(port: number, cb?: () => void): net.Server | Promise<{
|
|
9
|
-
port: number;
|
|
10
|
-
}>;
|
|
11
|
-
}
|