qhttpx 1.8.5 → 1.8.11
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 +52 -0
- package/README.md +72 -52
- package/binding.gyp +18 -0
- package/dist/examples/api-server.js +29 -8
- package/dist/examples/basic.d.ts +1 -0
- package/dist/examples/basic.js +10 -0
- package/dist/examples/compression.d.ts +1 -0
- package/dist/examples/compression.js +17 -0
- package/dist/examples/cors.d.ts +1 -0
- package/dist/examples/cors.js +19 -0
- package/dist/examples/errors.d.ts +1 -0
- package/dist/examples/errors.js +25 -0
- package/dist/examples/file-upload.d.ts +1 -0
- package/dist/examples/file-upload.js +24 -0
- package/dist/examples/fusion.d.ts +1 -0
- package/dist/examples/fusion.js +21 -0
- package/dist/examples/rate-limiting.d.ts +1 -0
- package/dist/examples/rate-limiting.js +17 -0
- package/dist/examples/validation.d.ts +1 -0
- package/dist/examples/validation.js +23 -0
- package/dist/examples/websockets.d.ts +1 -0
- package/dist/examples/websockets.js +20 -0
- package/dist/package.json +11 -1
- package/dist/src/benchmarks/simple-json.js +6 -4
- package/dist/src/cli/index.js +33 -11
- package/dist/src/core/errors.d.ts +34 -0
- package/dist/src/core/errors.js +70 -0
- package/dist/src/core/native-adapter.d.ts +11 -0
- package/dist/src/core/native-adapter.js +211 -0
- package/dist/src/core/server.d.ts +52 -4
- package/dist/src/core/server.js +389 -261
- package/dist/src/core/types.d.ts +37 -0
- package/dist/src/index.d.ts +6 -1
- package/dist/src/index.js +19 -3
- package/dist/src/middleware/compression.d.ts +1 -5
- package/dist/src/middleware/cors.d.ts +1 -10
- package/dist/src/middleware/presets.d.ts +4 -1
- package/dist/src/middleware/presets.js +22 -3
- package/dist/src/middleware/rate-limit.d.ts +1 -19
- package/dist/src/middleware/rate-limit.js +6 -0
- package/dist/src/middleware/security.d.ts +1 -2
- package/dist/src/native/index.d.ts +29 -0
- package/dist/src/native/index.js +64 -0
- package/dist/src/router/radix-tree.d.ts +2 -0
- package/dist/src/router/radix-tree.js +54 -4
- package/dist/src/router/router.d.ts +1 -0
- package/dist/src/router/router.js +42 -2
- package/dist/tests/native-adapter.test.d.ts +1 -0
- package/dist/tests/native-adapter.test.js +71 -0
- package/dist/tests/resources.test.js +3 -0
- package/dist/tests/security.test.js +2 -2
- package/docs/AEGIS.md +34 -9
- package/docs/BENCHMARKS.md +8 -7
- package/docs/ERRORS.md +112 -0
- package/docs/FUSION.md +68 -0
- package/docs/MIDDLEWARE.md +65 -0
- package/docs/ROUTING.md +70 -0
- package/docs/STATIC.md +61 -0
- package/docs/WEBSOCKETS.md +76 -0
- package/package.json +11 -1
- package/src/native/README.md +31 -0
- package/src/native/addon.cc +8 -0
- package/src/native/index.ts +78 -0
- package/src/native/picohttpparser.c +608 -0
- package/src/native/picohttpparser.h +71 -0
- package/src/native/server.cc +262 -0
- package/src/native/server.h +30 -0
- package/.eslintrc.json +0 -22
- package/.github/workflows/ci.yml +0 -32
- package/.github/workflows/npm-publish.yml +0 -37
- package/.github/workflows/release.yml +0 -21
- package/.prettierrc +0 -7
- package/assets/logo.svg +0 -25
- package/eslint.config.cjs +0 -26
- package/examples/api-server.ts +0 -62
- package/src/benchmarks/quantam-users.ts +0 -70
- package/src/benchmarks/simple-json.ts +0 -71
- package/src/benchmarks/ultra-mode.ts +0 -127
- package/src/cli/index.ts +0 -214
- package/src/client/index.ts +0 -93
- package/src/core/batch.ts +0 -110
- package/src/core/body-parser.ts +0 -151
- package/src/core/buffer-pool.ts +0 -96
- package/src/core/config.ts +0 -60
- package/src/core/fusion.ts +0 -210
- package/src/core/logger.ts +0 -70
- package/src/core/metrics.ts +0 -166
- package/src/core/resources.ts +0 -38
- package/src/core/scheduler.ts +0 -126
- package/src/core/scope.ts +0 -87
- package/src/core/serializer.ts +0 -41
- package/src/core/server.ts +0 -1234
- package/src/core/stream.ts +0 -111
- package/src/core/tasks.ts +0 -138
- package/src/core/types.ts +0 -192
- package/src/core/websocket.ts +0 -112
- package/src/core/worker-queue.ts +0 -90
- package/src/database/adapters/memory.ts +0 -99
- package/src/database/adapters/mongo.ts +0 -116
- package/src/database/adapters/postgres.ts +0 -86
- package/src/database/adapters/sqlite.ts +0 -44
- package/src/database/coalescer.ts +0 -153
- package/src/database/manager.ts +0 -97
- package/src/database/types.ts +0 -24
- package/src/index.ts +0 -58
- package/src/middleware/compression.ts +0 -147
- package/src/middleware/cors.ts +0 -98
- package/src/middleware/presets.ts +0 -50
- package/src/middleware/rate-limit.ts +0 -106
- package/src/middleware/security.ts +0 -109
- package/src/middleware/static.ts +0 -216
- package/src/openapi/generator.ts +0 -167
- package/src/router/radix-router.ts +0 -119
- package/src/router/radix-tree.ts +0 -106
- package/src/router/router.ts +0 -190
- package/src/testing/index.ts +0 -104
- package/src/utils/cookies.ts +0 -67
- package/src/utils/logger.ts +0 -59
- package/src/utils/signals.ts +0 -45
- package/src/utils/sse.ts +0 -41
- package/src/validation/index.ts +0 -3
- package/src/validation/simple.ts +0 -93
- package/src/validation/types.ts +0 -38
- package/src/validation/zod.ts +0 -14
- package/src/views/index.ts +0 -1
- package/src/views/types.ts +0 -4
- package/tests/adapters.test.ts +0 -120
- package/tests/batch.test.ts +0 -139
- package/tests/body-parser.test.ts +0 -83
- package/tests/compression-sse.test.ts +0 -98
- package/tests/cookies.test.ts +0 -74
- package/tests/cors.test.ts +0 -79
- package/tests/database.test.ts +0 -90
- package/tests/dx.test.ts +0 -130
- package/tests/ecosystem.test.ts +0 -156
- package/tests/features.test.ts +0 -51
- package/tests/fusion.test.ts +0 -121
- package/tests/http-basic.test.ts +0 -161
- package/tests/logger.test.ts +0 -48
- package/tests/middleware.test.ts +0 -137
- package/tests/observability.test.ts +0 -91
- package/tests/openapi.test.ts +0 -74
- package/tests/plugin.test.ts +0 -85
- package/tests/plugins.test.ts +0 -93
- package/tests/rate-limit.test.ts +0 -97
- package/tests/resources.test.ts +0 -64
- package/tests/scheduler.test.ts +0 -71
- package/tests/schema-routes.test.ts +0 -89
- package/tests/security.test.ts +0 -128
- package/tests/server-db.test.ts +0 -72
- package/tests/smoke.test.ts +0 -9
- package/tests/sqlite-fusion.test.ts +0 -106
- package/tests/static.test.ts +0 -111
- package/tests/stream.test.ts +0 -58
- package/tests/task-metrics.test.ts +0 -78
- package/tests/tasks.test.ts +0 -90
- package/tests/testing.test.ts +0 -53
- package/tests/validation.test.ts +0 -126
- package/tests/websocket.test.ts +0 -132
- package/tsconfig.json +0 -17
- package/vitest.config.ts +0 -9
package/dist/src/cli/index.js
CHANGED
|
@@ -142,26 +142,29 @@ function runNew(projectName) {
|
|
|
142
142
|
const pkgJson = {
|
|
143
143
|
name: projectName,
|
|
144
144
|
version: '0.0.1',
|
|
145
|
+
type: 'module',
|
|
145
146
|
scripts: {
|
|
146
147
|
dev: 'qhttpx dev src/index.ts',
|
|
147
148
|
start: 'qhttpx start dist/index.js',
|
|
148
149
|
build: 'tsc'
|
|
149
150
|
},
|
|
150
151
|
dependencies: {
|
|
151
|
-
'qhttpx': '^1.
|
|
152
|
+
'qhttpx': '^1.8.6',
|
|
153
|
+
'dotenv': '^16.4.1'
|
|
152
154
|
},
|
|
153
155
|
devDependencies: {
|
|
154
|
-
'typescript': '^5.
|
|
155
|
-
'@types/node': '^20.
|
|
156
|
-
'tsx': '^4.
|
|
156
|
+
'typescript': '^5.3.3',
|
|
157
|
+
'@types/node': '^20.11.16',
|
|
158
|
+
'tsx': '^4.7.0'
|
|
157
159
|
}
|
|
158
160
|
};
|
|
159
161
|
fs_1.default.writeFileSync(path_1.default.join(projectDir, 'package.json'), JSON.stringify(pkgJson, null, 2));
|
|
160
162
|
// tsconfig.json
|
|
161
163
|
const tsConfig = {
|
|
162
164
|
compilerOptions: {
|
|
163
|
-
target: '
|
|
164
|
-
module: '
|
|
165
|
+
target: 'ES2022',
|
|
166
|
+
module: 'Node16',
|
|
167
|
+
moduleResolution: 'Node16',
|
|
165
168
|
rootDir: 'src',
|
|
166
169
|
outDir: 'dist',
|
|
167
170
|
strict: true,
|
|
@@ -174,15 +177,34 @@ function runNew(projectName) {
|
|
|
174
177
|
fs_1.default.writeFileSync(path_1.default.join(projectDir, 'tsconfig.json'), JSON.stringify(tsConfig, null, 2));
|
|
175
178
|
// src/index.ts
|
|
176
179
|
const indexTs = `import { createHttpApp } from 'qhttpx';
|
|
180
|
+
import 'dotenv/config';
|
|
177
181
|
|
|
178
|
-
const app = createHttpApp(
|
|
182
|
+
const app = createHttpApp({
|
|
183
|
+
// ⚡ Performance: Auto-coalesce duplicate requests
|
|
184
|
+
enableRequestFusion: true,
|
|
179
185
|
|
|
180
|
-
|
|
181
|
-
|
|
186
|
+
// 🛡️ Security: Built-in CORS & Rate Limiting
|
|
187
|
+
cors: true,
|
|
188
|
+
rateLimit: {
|
|
189
|
+
windowMs: 15 * 60 * 1000, // 15 min
|
|
190
|
+
max: 100, // 100 reqs per IP
|
|
191
|
+
trustProxy: true
|
|
192
|
+
},
|
|
193
|
+
|
|
194
|
+
// 🗜️ Optimization: Gzip/Brotli compression
|
|
195
|
+
compression: true
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
app.get('/', ({ json }) => {
|
|
199
|
+
json({
|
|
200
|
+
message: 'Welcome to QHTTPX 🚀',
|
|
201
|
+
status: 'online',
|
|
202
|
+
timestamp: Date.now()
|
|
203
|
+
});
|
|
182
204
|
});
|
|
183
205
|
|
|
184
|
-
app.listen(
|
|
185
|
-
console.log(\`Server running on
|
|
206
|
+
app.listen(Number(process.env.PORT) || 3000, () => {
|
|
207
|
+
console.log(\`Server running on http://localhost:\${process.env.PORT || 3000}\`);
|
|
186
208
|
});
|
|
187
209
|
`;
|
|
188
210
|
fs_1.default.writeFileSync(path_1.default.join(projectDir, 'src', 'index.ts'), indexTs);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { HttpError } from './types';
|
|
2
|
+
export declare class BadRequestException extends HttpError {
|
|
3
|
+
constructor(message?: string, details?: unknown);
|
|
4
|
+
}
|
|
5
|
+
export declare class UnauthorizedException extends HttpError {
|
|
6
|
+
constructor(message?: string, details?: unknown);
|
|
7
|
+
}
|
|
8
|
+
export declare class ForbiddenException extends HttpError {
|
|
9
|
+
constructor(message?: string, details?: unknown);
|
|
10
|
+
}
|
|
11
|
+
export declare class NotFoundException extends HttpError {
|
|
12
|
+
constructor(message?: string, details?: unknown);
|
|
13
|
+
}
|
|
14
|
+
export declare class MethodNotAllowedException extends HttpError {
|
|
15
|
+
constructor(message?: string, details?: unknown);
|
|
16
|
+
}
|
|
17
|
+
export declare class ConflictException extends HttpError {
|
|
18
|
+
constructor(message?: string, details?: unknown);
|
|
19
|
+
}
|
|
20
|
+
export declare class PayloadTooLargeException extends HttpError {
|
|
21
|
+
constructor(message?: string, details?: unknown);
|
|
22
|
+
}
|
|
23
|
+
export declare class UnsupportedMediaTypeException extends HttpError {
|
|
24
|
+
constructor(message?: string, details?: unknown);
|
|
25
|
+
}
|
|
26
|
+
export declare class TooManyRequestsException extends HttpError {
|
|
27
|
+
constructor(message?: string, details?: unknown);
|
|
28
|
+
}
|
|
29
|
+
export declare class InternalServerErrorException extends HttpError {
|
|
30
|
+
constructor(message?: string, details?: unknown);
|
|
31
|
+
}
|
|
32
|
+
export declare class ServiceUnavailableException extends HttpError {
|
|
33
|
+
constructor(message?: string, details?: unknown);
|
|
34
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ServiceUnavailableException = exports.InternalServerErrorException = exports.TooManyRequestsException = exports.UnsupportedMediaTypeException = exports.PayloadTooLargeException = exports.ConflictException = exports.MethodNotAllowedException = exports.NotFoundException = exports.ForbiddenException = exports.UnauthorizedException = exports.BadRequestException = void 0;
|
|
4
|
+
const types_1 = require("./types");
|
|
5
|
+
class BadRequestException extends types_1.HttpError {
|
|
6
|
+
constructor(message = 'Bad Request', details) {
|
|
7
|
+
super(400, message, { code: 'BAD_REQUEST', details });
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
exports.BadRequestException = BadRequestException;
|
|
11
|
+
class UnauthorizedException extends types_1.HttpError {
|
|
12
|
+
constructor(message = 'Unauthorized', details) {
|
|
13
|
+
super(401, message, { code: 'UNAUTHORIZED', details });
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
exports.UnauthorizedException = UnauthorizedException;
|
|
17
|
+
class ForbiddenException extends types_1.HttpError {
|
|
18
|
+
constructor(message = 'Forbidden', details) {
|
|
19
|
+
super(403, message, { code: 'FORBIDDEN', details });
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
exports.ForbiddenException = ForbiddenException;
|
|
23
|
+
class NotFoundException extends types_1.HttpError {
|
|
24
|
+
constructor(message = 'Not Found', details) {
|
|
25
|
+
super(404, message, { code: 'NOT_FOUND', details });
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
exports.NotFoundException = NotFoundException;
|
|
29
|
+
class MethodNotAllowedException extends types_1.HttpError {
|
|
30
|
+
constructor(message = 'Method Not Allowed', details) {
|
|
31
|
+
super(405, message, { code: 'METHOD_NOT_ALLOWED', details });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
exports.MethodNotAllowedException = MethodNotAllowedException;
|
|
35
|
+
class ConflictException extends types_1.HttpError {
|
|
36
|
+
constructor(message = 'Conflict', details) {
|
|
37
|
+
super(409, message, { code: 'CONFLICT', details });
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
exports.ConflictException = ConflictException;
|
|
41
|
+
class PayloadTooLargeException extends types_1.HttpError {
|
|
42
|
+
constructor(message = 'Payload Too Large', details) {
|
|
43
|
+
super(413, message, { code: 'PAYLOAD_TOO_LARGE', details });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
exports.PayloadTooLargeException = PayloadTooLargeException;
|
|
47
|
+
class UnsupportedMediaTypeException extends types_1.HttpError {
|
|
48
|
+
constructor(message = 'Unsupported Media Type', details) {
|
|
49
|
+
super(415, message, { code: 'UNSUPPORTED_MEDIA_TYPE', details });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
exports.UnsupportedMediaTypeException = UnsupportedMediaTypeException;
|
|
53
|
+
class TooManyRequestsException extends types_1.HttpError {
|
|
54
|
+
constructor(message = 'Too Many Requests', details) {
|
|
55
|
+
super(429, message, { code: 'TOO_MANY_REQUESTS', details });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
exports.TooManyRequestsException = TooManyRequestsException;
|
|
59
|
+
class InternalServerErrorException extends types_1.HttpError {
|
|
60
|
+
constructor(message = 'Internal Server Error', details) {
|
|
61
|
+
super(500, message, { code: 'INTERNAL_SERVER_ERROR', details });
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
exports.InternalServerErrorException = InternalServerErrorException;
|
|
65
|
+
class ServiceUnavailableException extends types_1.HttpError {
|
|
66
|
+
constructor(message = 'Service Unavailable', details) {
|
|
67
|
+
super(503, message, { code: 'SERVICE_UNAVAILABLE', details });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
exports.ServiceUnavailableException = ServiceUnavailableException;
|
|
@@ -0,0 +1,11 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
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
|
+
exports.NativeAdapter = void 0;
|
|
7
|
+
const net_1 = __importDefault(require("net"));
|
|
8
|
+
const stream_1 = require("stream");
|
|
9
|
+
const native_1 = require("../native");
|
|
10
|
+
class MockIncomingMessage extends stream_1.Readable {
|
|
11
|
+
constructor(socket, method, url, headers) {
|
|
12
|
+
super();
|
|
13
|
+
this.httpVersion = '1.1';
|
|
14
|
+
this.socket = socket;
|
|
15
|
+
this.method = method;
|
|
16
|
+
this.url = url;
|
|
17
|
+
this.headers = headers;
|
|
18
|
+
}
|
|
19
|
+
_read() {
|
|
20
|
+
// No-op, data is pushed manually
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
class MockServerResponse extends stream_1.Writable {
|
|
24
|
+
constructor(socket, nativeServer) {
|
|
25
|
+
super();
|
|
26
|
+
this.statusCode = 200;
|
|
27
|
+
this.headersSent = false;
|
|
28
|
+
this.headers = {};
|
|
29
|
+
this.socket = socket;
|
|
30
|
+
this.nativeServer = nativeServer;
|
|
31
|
+
}
|
|
32
|
+
setHeader(name, value) {
|
|
33
|
+
this.headers[name.toLowerCase()] = value;
|
|
34
|
+
return this;
|
|
35
|
+
}
|
|
36
|
+
getHeader(name) {
|
|
37
|
+
return this.headers[name.toLowerCase()];
|
|
38
|
+
}
|
|
39
|
+
getHeaders() {
|
|
40
|
+
return { ...this.headers };
|
|
41
|
+
}
|
|
42
|
+
hasHeader(name) {
|
|
43
|
+
return Object.prototype.hasOwnProperty.call(this.headers, name.toLowerCase());
|
|
44
|
+
}
|
|
45
|
+
removeHeader(name) {
|
|
46
|
+
delete this.headers[name.toLowerCase()];
|
|
47
|
+
}
|
|
48
|
+
writeHead(statusCode, headers) {
|
|
49
|
+
this.statusCode = statusCode;
|
|
50
|
+
if (headers) {
|
|
51
|
+
for (const key in headers) {
|
|
52
|
+
this.setHeader(key, headers[key]);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return this;
|
|
56
|
+
}
|
|
57
|
+
reset(socket) {
|
|
58
|
+
this.socket = socket;
|
|
59
|
+
this.statusCode = 200;
|
|
60
|
+
this.headersSent = false;
|
|
61
|
+
this.headers = {};
|
|
62
|
+
this.removeAllListeners();
|
|
63
|
+
}
|
|
64
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
65
|
+
_write(chunk, encoding, callback) {
|
|
66
|
+
if (!this.headersSent) {
|
|
67
|
+
this.sendHeaders();
|
|
68
|
+
}
|
|
69
|
+
this.socket.write(chunk, encoding, callback);
|
|
70
|
+
}
|
|
71
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
72
|
+
end(arg1, arg2, arg3) {
|
|
73
|
+
if (arg1 && typeof arg1 !== 'function') {
|
|
74
|
+
if (!this.headersSent) {
|
|
75
|
+
// Optimization: Use native createResponse / createJSONResponse if possible
|
|
76
|
+
try {
|
|
77
|
+
let respBuffer;
|
|
78
|
+
// Check for JSON object (not buffer/string)
|
|
79
|
+
if (typeof arg1 === 'object' && !Buffer.isBuffer(arg1)) {
|
|
80
|
+
respBuffer = this.nativeServer.createJSONResponse(arg1);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
const body = arg1;
|
|
84
|
+
const headers = {};
|
|
85
|
+
for (const k in this.headers) {
|
|
86
|
+
const v = this.headers[k];
|
|
87
|
+
headers[k] = Array.isArray(v) ? v.join(', ') : String(v);
|
|
88
|
+
}
|
|
89
|
+
respBuffer = this.nativeServer.createResponse(this.statusCode, headers, body);
|
|
90
|
+
}
|
|
91
|
+
// Try direct writeResponse if FD is available
|
|
92
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
93
|
+
const fd = this.socket._handle?.fd;
|
|
94
|
+
if (typeof fd === 'number' && fd >= 0) {
|
|
95
|
+
this.nativeServer.writeResponse(fd, [respBuffer]);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
this.socket.write(respBuffer);
|
|
99
|
+
}
|
|
100
|
+
this.headersSent = true;
|
|
101
|
+
if (arg2 && typeof arg2 === 'function')
|
|
102
|
+
arg2();
|
|
103
|
+
else if (arg3 && typeof arg3 === 'function')
|
|
104
|
+
arg3();
|
|
105
|
+
return this;
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
// Fallback
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
this.write(arg1, arg2);
|
|
112
|
+
}
|
|
113
|
+
if (!this.headersSent) {
|
|
114
|
+
this.sendHeaders();
|
|
115
|
+
}
|
|
116
|
+
super.end();
|
|
117
|
+
if (arg1 && typeof arg1 === 'function')
|
|
118
|
+
arg1();
|
|
119
|
+
else if (arg2 && typeof arg2 === 'function')
|
|
120
|
+
arg2();
|
|
121
|
+
else if (arg3 && typeof arg3 === 'function')
|
|
122
|
+
arg3();
|
|
123
|
+
return this;
|
|
124
|
+
}
|
|
125
|
+
sendHeaders() {
|
|
126
|
+
let head = `HTTP/1.1 ${this.statusCode} OK\r\n`; // Status msg todo
|
|
127
|
+
for (const key in this.headers) {
|
|
128
|
+
const val = this.headers[key];
|
|
129
|
+
if (Array.isArray(val)) {
|
|
130
|
+
for (const v of val) {
|
|
131
|
+
head += `${key}: ${v}\r\n`;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
head += `${key}: ${val}\r\n`;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
head += '\r\n';
|
|
139
|
+
this.socket.write(head);
|
|
140
|
+
this.headersSent = true;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
class MockResponsePool {
|
|
144
|
+
constructor(nativeServer) {
|
|
145
|
+
this.pool = [];
|
|
146
|
+
this.nativeServer = nativeServer;
|
|
147
|
+
}
|
|
148
|
+
acquire(socket) {
|
|
149
|
+
const res = this.pool.pop();
|
|
150
|
+
if (res) {
|
|
151
|
+
res.reset(socket);
|
|
152
|
+
return res;
|
|
153
|
+
}
|
|
154
|
+
return new MockServerResponse(socket, this.nativeServer);
|
|
155
|
+
}
|
|
156
|
+
release(res) {
|
|
157
|
+
// Simple cap to prevent memory leak if something goes wrong
|
|
158
|
+
if (this.pool.length < 2000) {
|
|
159
|
+
this.pool.push(res);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
class NativeAdapter {
|
|
164
|
+
constructor(app) {
|
|
165
|
+
this.app = app;
|
|
166
|
+
this.nativeServer = new native_1.NativeServer();
|
|
167
|
+
this.responsePool = new MockResponsePool(this.nativeServer);
|
|
168
|
+
}
|
|
169
|
+
listen(port, cb) {
|
|
170
|
+
if (!this.nativeServer.isAvailable) {
|
|
171
|
+
console.warn('Native server not available, falling back to Node.js HTTP');
|
|
172
|
+
return this.app.listen(port, cb);
|
|
173
|
+
}
|
|
174
|
+
const server = net_1.default.createServer((socket) => {
|
|
175
|
+
let buffer = Buffer.alloc(0);
|
|
176
|
+
socket.on('data', (chunk) => {
|
|
177
|
+
buffer = buffer.length === 0 ? chunk : Buffer.concat([buffer, chunk]);
|
|
178
|
+
// Try parsing
|
|
179
|
+
const res = this.nativeServer.parse(buffer);
|
|
180
|
+
if (res) {
|
|
181
|
+
// Parsed successfully
|
|
182
|
+
const req = new MockIncomingMessage(socket, res.method, res.path, res.headers);
|
|
183
|
+
const response = this.responsePool.acquire(socket);
|
|
184
|
+
response.on('finish', () => {
|
|
185
|
+
this.responsePool.release(response);
|
|
186
|
+
});
|
|
187
|
+
// Push body if any
|
|
188
|
+
if (res.bodyOffset < buffer.length) {
|
|
189
|
+
req.push(buffer.slice(res.bodyOffset));
|
|
190
|
+
}
|
|
191
|
+
req.push(null); // End of body (assuming single packet for now or content-length handling needed for real impl)
|
|
192
|
+
// Reset buffer for pipelining support (not implemented here, assuming connection: close or one req per packet for simplicity)
|
|
193
|
+
buffer = Buffer.alloc(0);
|
|
194
|
+
// Dispatch to QHTTPX
|
|
195
|
+
this.app.handleRequest(req, response);
|
|
196
|
+
}
|
|
197
|
+
else if (res === null) {
|
|
198
|
+
// Error
|
|
199
|
+
socket.destroy();
|
|
200
|
+
}
|
|
201
|
+
// else undefined -> partial, wait for more data
|
|
202
|
+
});
|
|
203
|
+
socket.on('error', () => {
|
|
204
|
+
// console.error(err);
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
server.listen(port, cb);
|
|
208
|
+
return server;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
exports.NativeAdapter = NativeAdapter;
|
|
@@ -1,9 +1,53 @@
|
|
|
1
|
-
import http from 'http';
|
|
1
|
+
import http, { IncomingMessage, ServerResponse, IncomingHttpHeaders } from 'http';
|
|
2
|
+
import { URL } from 'url';
|
|
2
3
|
import { WebSocketManager } from './websocket';
|
|
4
|
+
import { Validator } from '../validation/types';
|
|
3
5
|
import { OpenAPIOptions } from '../openapi/generator';
|
|
4
|
-
import { HTTPMethod, QHTTPXErrorHandler, QHTTPXHandler, QHTTPXMethodNotAllowedHandler, QHTTPXMiddleware, QHTTPXNotFoundHandler, QHTTPXOptions, QHTTPXRouteOptions, QHTTPXPlugin, QHTTPXPluginOptions } from './types';
|
|
6
|
+
import { HTTPMethod, QHTTPXContext, QHTTPXErrorHandler, QHTTPXHandler, QHTTPXMethodNotAllowedHandler, QHTTPXMiddleware, QHTTPXNotFoundHandler, QHTTPXOptions, QHTTPXRouteOptions, QHTTPXPlugin, QHTTPXPluginOptions, CookieOptions, QHTTPXFile } from './types';
|
|
5
7
|
import { Logger } from './logger';
|
|
8
|
+
export declare class QHTTPXContextImpl implements QHTTPXContext {
|
|
9
|
+
req: IncomingMessage;
|
|
10
|
+
res: ServerResponse;
|
|
11
|
+
headers: IncomingHttpHeaders;
|
|
12
|
+
method: HTTPMethod;
|
|
13
|
+
params: Record<string, string>;
|
|
14
|
+
query: Record<string, string | string[]>;
|
|
15
|
+
body: unknown;
|
|
16
|
+
files?: Record<string, QHTTPXFile | QHTTPXFile[]>;
|
|
17
|
+
cookies: Record<string, string>;
|
|
18
|
+
private _state;
|
|
19
|
+
requestId: string | undefined;
|
|
20
|
+
requestStart: number | undefined;
|
|
21
|
+
serializer?: (value: unknown) => string;
|
|
22
|
+
disableAutoEnd?: boolean;
|
|
23
|
+
path: string;
|
|
24
|
+
error?: Error;
|
|
25
|
+
next?: () => Promise<void>;
|
|
26
|
+
private _url;
|
|
27
|
+
private _ip;
|
|
28
|
+
private _app;
|
|
29
|
+
private _appJsonSerializer;
|
|
30
|
+
constructor(app: QHTTPX);
|
|
31
|
+
get bufferPool(): any;
|
|
32
|
+
get db(): any;
|
|
33
|
+
get state(): Record<string, unknown>;
|
|
34
|
+
set state(v: Record<string, unknown>);
|
|
35
|
+
get ip(): string;
|
|
36
|
+
set ip(v: string);
|
|
37
|
+
get url(): URL;
|
|
38
|
+
set url(v: URL);
|
|
39
|
+
json(payload: unknown, status?: number): void;
|
|
40
|
+
send(payload: string | Buffer, status?: number): void;
|
|
41
|
+
html(payload: string, status?: number): void;
|
|
42
|
+
redirect(url: string, status?: number): void;
|
|
43
|
+
setCookie(name: string, value: string, options: CookieOptions | undefined): void;
|
|
44
|
+
render(view: string, locals?: Record<string, unknown>): Promise<void>;
|
|
45
|
+
validate<T>(schema: unknown, data?: unknown): Promise<T>;
|
|
46
|
+
reset(): void;
|
|
47
|
+
}
|
|
6
48
|
export declare class QHTTPX {
|
|
49
|
+
private static readonly EMPTY_PARAMS;
|
|
50
|
+
private static readonly EMPTY_MATCH;
|
|
7
51
|
private readonly options;
|
|
8
52
|
private readonly server;
|
|
9
53
|
readonly logger: Logger;
|
|
@@ -24,11 +68,13 @@ export declare class QHTTPX {
|
|
|
24
68
|
private readonly onBeforeShutdownHooks;
|
|
25
69
|
private readonly onShutdownHooks;
|
|
26
70
|
private nextRequestId;
|
|
71
|
+
private requestCounter;
|
|
27
72
|
private readonly wsManager;
|
|
28
73
|
private readonly ultraMode;
|
|
29
74
|
private readonly batchExecutor?;
|
|
30
75
|
private readonly fusion?;
|
|
31
|
-
|
|
76
|
+
readonly validator: Validator;
|
|
77
|
+
private readonly poolLimit;
|
|
32
78
|
constructor(options?: QHTTPXOptions);
|
|
33
79
|
get serverInstance(): http.Server;
|
|
34
80
|
get websocket(): WebSocketManager;
|
|
@@ -82,7 +128,9 @@ export declare class QHTTPX {
|
|
|
82
128
|
private createContext;
|
|
83
129
|
private acquireContext;
|
|
84
130
|
private releaseContext;
|
|
85
|
-
private
|
|
131
|
+
private handleNoMatch;
|
|
132
|
+
handleRequest(req: IncomingMessage, res: ServerResponse): void;
|
|
133
|
+
private dispatch;
|
|
86
134
|
private handleUpgrade;
|
|
87
135
|
private runLifecycleHooks;
|
|
88
136
|
private handleError;
|