tspace-spear 1.2.2 → 1.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -4
- package/dist/lib/core/server/index.d.ts +12 -2
- package/dist/lib/core/server/index.js +310 -127
- package/dist/lib/core/server/index.js.map +1 -1
- package/dist/lib/core/server/parser-factory.d.ts +17 -8
- package/dist/lib/core/server/parser-factory.js +269 -4
- package/dist/lib/core/server/parser-factory.js.map +1 -1
- package/dist/lib/core/server/router.d.ts +10 -0
- package/dist/lib/core/server/router.js +12 -0
- package/dist/lib/core/server/router.js.map +1 -1
- package/dist/lib/core/types/index.d.ts +25 -5
- package/package.json +18 -7
- package/dist/tests/benchmark.test.d.ts +0 -1
- package/dist/tests/benchmark.test.js +0 -145
- package/dist/tests/benchmark.test.js.map +0 -1
|
@@ -43,7 +43,6 @@ const cluster_1 = __importDefault(require("cluster"));
|
|
|
43
43
|
const os_1 = __importDefault(require("os"));
|
|
44
44
|
const fs_1 = __importDefault(require("fs"));
|
|
45
45
|
const path_1 = __importDefault(require("path"));
|
|
46
|
-
const url_1 = require("url");
|
|
47
46
|
const on_finished_1 = __importDefault(require("on-finished"));
|
|
48
47
|
const ws_1 = __importDefault(require("ws"));
|
|
49
48
|
const parser_factory_1 = require("./parser-factory");
|
|
@@ -69,6 +68,7 @@ class Spear {
|
|
|
69
68
|
_globalPrefix;
|
|
70
69
|
_router = (0, find_my_way_1.default)();
|
|
71
70
|
_parser = new parser_factory_1.ParserFactory();
|
|
71
|
+
_adapter = http_1.default;
|
|
72
72
|
_cluster;
|
|
73
73
|
_cors;
|
|
74
74
|
_swagger = {
|
|
@@ -102,11 +102,13 @@ class Spear {
|
|
|
102
102
|
ms: 1000 * 60 * 10
|
|
103
103
|
}
|
|
104
104
|
};
|
|
105
|
-
constructor({ controllers, middlewares, globalPrefix, logger, cluster } = {}) {
|
|
105
|
+
constructor({ controllers, middlewares, globalPrefix, logger, cluster, adapter } = {}) {
|
|
106
106
|
if (logger)
|
|
107
107
|
this.useLogger();
|
|
108
108
|
if (cluster)
|
|
109
109
|
this.useCluster(cluster);
|
|
110
|
+
if (adapter)
|
|
111
|
+
this.useAdater(adapter);
|
|
110
112
|
this._controllers = controllers;
|
|
111
113
|
this._middlewares = middlewares;
|
|
112
114
|
this._globalPrefix = globalPrefix == null ? '' : globalPrefix;
|
|
@@ -151,6 +153,18 @@ class Spear {
|
|
|
151
153
|
this._globalMiddlewares.push(middleware);
|
|
152
154
|
return this;
|
|
153
155
|
}
|
|
156
|
+
/**
|
|
157
|
+
* The 'useAdater' method is used to switch between different server implementations,
|
|
158
|
+
* such as the native Node.js HTTP server or uWebSockets.js (uWS).
|
|
159
|
+
*
|
|
160
|
+
* @param {T.Adapter} adapter - The adapter instance (e.g., HTTP or uWS).
|
|
161
|
+
* @returns {this} Returns the current instance for chaining
|
|
162
|
+
*/
|
|
163
|
+
useAdater(adapter) {
|
|
164
|
+
this._adapter = adapter;
|
|
165
|
+
this._parser.useAdater(adapter);
|
|
166
|
+
return this;
|
|
167
|
+
}
|
|
154
168
|
/**
|
|
155
169
|
* The 'useCluster' method is used cluster run the server
|
|
156
170
|
*
|
|
@@ -219,7 +233,7 @@ class Spear {
|
|
|
219
233
|
*/
|
|
220
234
|
useBodyParser({ except } = {}) {
|
|
221
235
|
this._globalMiddlewares.push((ctx, next) => {
|
|
222
|
-
const { req } = ctx;
|
|
236
|
+
const { req, res } = ctx;
|
|
223
237
|
const contentType = req?.headers['content-type'] ?? '';
|
|
224
238
|
const isFileUpload = contentType && contentType.startsWith('multipart/form-data');
|
|
225
239
|
const isCanParserBody = contentType.includes('application/json') ||
|
|
@@ -235,7 +249,7 @@ class Spear {
|
|
|
235
249
|
return next();
|
|
236
250
|
if (req?.body != null)
|
|
237
251
|
return next();
|
|
238
|
-
Promise.resolve(this._parser.body(req))
|
|
252
|
+
Promise.resolve(this._parser.body(req, res))
|
|
239
253
|
.then(r => {
|
|
240
254
|
req.body = r;
|
|
241
255
|
return next();
|
|
@@ -268,7 +282,7 @@ class Spear {
|
|
|
268
282
|
this._fileUploadOptions.removeTempFile = removeTempFile;
|
|
269
283
|
}
|
|
270
284
|
this._globalMiddlewares.push((ctx, next) => {
|
|
271
|
-
const { req } = ctx;
|
|
285
|
+
const { req, res } = ctx;
|
|
272
286
|
if (req.method === 'GET') {
|
|
273
287
|
return next();
|
|
274
288
|
}
|
|
@@ -279,7 +293,7 @@ class Spear {
|
|
|
279
293
|
if (req?.files != null)
|
|
280
294
|
return next();
|
|
281
295
|
Promise
|
|
282
|
-
.resolve(this._parser.files(req, this._fileUploadOptions))
|
|
296
|
+
.resolve(this._parser.files({ req, res, options: this._fileUploadOptions }))
|
|
283
297
|
.then(r => {
|
|
284
298
|
req.files = r.files;
|
|
285
299
|
req.body = r.body;
|
|
@@ -359,6 +373,22 @@ class Spear {
|
|
|
359
373
|
});
|
|
360
374
|
return server;
|
|
361
375
|
}
|
|
376
|
+
if ('App' in this._adapter) {
|
|
377
|
+
const handler = () => {
|
|
378
|
+
this._onListeners.forEach(listener => listener());
|
|
379
|
+
if (this._swagger.use) {
|
|
380
|
+
this._swaggerHandler();
|
|
381
|
+
}
|
|
382
|
+
callback?.({ server, port });
|
|
383
|
+
};
|
|
384
|
+
if (hostname) {
|
|
385
|
+
server.listen(port, String(hostname), handler);
|
|
386
|
+
}
|
|
387
|
+
else {
|
|
388
|
+
server.listen(port, handler);
|
|
389
|
+
}
|
|
390
|
+
return server;
|
|
391
|
+
}
|
|
362
392
|
const args = hostname
|
|
363
393
|
? [port, hostname, () => callback?.({ server, port: port })]
|
|
364
394
|
: [port, () => callback?.({ server, port: port })];
|
|
@@ -369,10 +399,6 @@ class Spear {
|
|
|
369
399
|
this._swaggerHandler();
|
|
370
400
|
}
|
|
371
401
|
});
|
|
372
|
-
server.on('error', (_) => {
|
|
373
|
-
port = Math.floor(Math.random() * 8999) + 1000;
|
|
374
|
-
server.listen(port);
|
|
375
|
-
});
|
|
376
402
|
return server;
|
|
377
403
|
}
|
|
378
404
|
/**
|
|
@@ -451,7 +477,8 @@ class Spear {
|
|
|
451
477
|
files: {},
|
|
452
478
|
body: {},
|
|
453
479
|
params: {},
|
|
454
|
-
cookies: {}
|
|
480
|
+
cookies: {},
|
|
481
|
+
ip: null
|
|
455
482
|
});
|
|
456
483
|
};
|
|
457
484
|
this._onListeners.push(() => {
|
|
@@ -594,37 +621,6 @@ class Spear {
|
|
|
594
621
|
});
|
|
595
622
|
return this;
|
|
596
623
|
}
|
|
597
|
-
_clusterMode({ server, port, hostname, callback }) {
|
|
598
|
-
if (cluster_1.default.isPrimary) {
|
|
599
|
-
const numCPUs = os_1.default.cpus().length;
|
|
600
|
-
const maxWorkers = typeof this._cluster === 'boolean' || this._cluster == null
|
|
601
|
-
? numCPUs
|
|
602
|
-
: this._cluster;
|
|
603
|
-
for (let i = 0; i < maxWorkers; i++) {
|
|
604
|
-
cluster_1.default.fork();
|
|
605
|
-
}
|
|
606
|
-
cluster_1.default.on('exit', () => {
|
|
607
|
-
cluster_1.default.fork();
|
|
608
|
-
});
|
|
609
|
-
}
|
|
610
|
-
if (cluster_1.default.isWorker) {
|
|
611
|
-
const args = hostname
|
|
612
|
-
? [port, hostname, () => callback?.({ server, port: port })]
|
|
613
|
-
: [port, () => callback?.({ server, port: port })];
|
|
614
|
-
server.listen(...args);
|
|
615
|
-
server.on('listening', () => {
|
|
616
|
-
this._onListeners.forEach(listener => listener());
|
|
617
|
-
if (this._swagger.use) {
|
|
618
|
-
this._swaggerHandler();
|
|
619
|
-
}
|
|
620
|
-
});
|
|
621
|
-
server.on('error', (_) => {
|
|
622
|
-
port = Math.floor(Math.random() * 8999) + 1000;
|
|
623
|
-
server.listen(port);
|
|
624
|
-
});
|
|
625
|
-
}
|
|
626
|
-
return;
|
|
627
|
-
}
|
|
628
624
|
async _import(dir, pattern) {
|
|
629
625
|
const directories = fs_1.default.readdirSync(dir, { withFileTypes: true });
|
|
630
626
|
const files = (await Promise.all(directories.map((directory) => {
|
|
@@ -754,18 +750,18 @@ class Spear {
|
|
|
754
750
|
}
|
|
755
751
|
if (results == null) {
|
|
756
752
|
if (this._formatResponse != null) {
|
|
757
|
-
return res.end(JSON.stringify(this._formatResponse(null, res.statusCode)
|
|
753
|
+
return res.end(JSON.stringify(this._formatResponse(null, res.statusCode)));
|
|
758
754
|
}
|
|
759
755
|
return res.end();
|
|
760
756
|
}
|
|
761
757
|
if (this._formatResponse != null) {
|
|
762
758
|
return res.end(JSON.stringify(this._formatResponse({
|
|
763
759
|
...results
|
|
764
|
-
}, res.statusCode)
|
|
760
|
+
}, res.statusCode)));
|
|
765
761
|
}
|
|
766
762
|
return res.end(JSON.stringify({
|
|
767
763
|
...results,
|
|
768
|
-
}
|
|
764
|
+
}));
|
|
769
765
|
};
|
|
770
766
|
response.send = (results) => {
|
|
771
767
|
if (res.writableEnded)
|
|
@@ -822,81 +818,81 @@ class Spear {
|
|
|
822
818
|
response.status(400);
|
|
823
819
|
message = message ?? `The request '${req.url}' resulted in a bad request. Please review the data and try again.`;
|
|
824
820
|
if (this._formatResponse != null) {
|
|
825
|
-
return res.end(JSON.stringify(this._formatResponse({ message }, 400)
|
|
821
|
+
return res.end(JSON.stringify(this._formatResponse({ message }, 400)));
|
|
826
822
|
}
|
|
827
823
|
return res.end(JSON.stringify({
|
|
828
824
|
message: message
|
|
829
|
-
}
|
|
825
|
+
}));
|
|
830
826
|
};
|
|
831
827
|
response.unauthorized = (message) => {
|
|
832
828
|
response.status(401);
|
|
833
829
|
message = message ?? `The request '${req.url}' is unauthorized. Please verify.`;
|
|
834
830
|
if (this._formatResponse != null) {
|
|
835
|
-
return res.end(JSON.stringify(this._formatResponse({ message }, 401)
|
|
831
|
+
return res.end(JSON.stringify(this._formatResponse({ message }, 401)));
|
|
836
832
|
}
|
|
837
833
|
return res.end(JSON.stringify({
|
|
838
834
|
message
|
|
839
|
-
}
|
|
835
|
+
}));
|
|
840
836
|
};
|
|
841
837
|
response.paymentRequired = (message) => {
|
|
842
838
|
response.status(402);
|
|
843
839
|
message = message ?? `The request '${req.url}' requires payment. Please proceed with payment.`;
|
|
844
840
|
if (this._formatResponse != null) {
|
|
845
|
-
return res.end(JSON.stringify(this._formatResponse({ message }, 402)
|
|
841
|
+
return res.end(JSON.stringify(this._formatResponse({ message }, 402)));
|
|
846
842
|
}
|
|
847
843
|
return res.end(JSON.stringify({
|
|
848
844
|
message
|
|
849
|
-
}
|
|
845
|
+
}));
|
|
850
846
|
};
|
|
851
847
|
response.forbidden = (message) => {
|
|
852
848
|
response.status(403);
|
|
853
849
|
message = message ?? `The request '${req.url}' is forbidden. Please check the permissions or access rights.`;
|
|
854
850
|
if (this._formatResponse != null) {
|
|
855
|
-
return res.end(JSON.stringify(this._formatResponse({ message }, 403)
|
|
851
|
+
return res.end(JSON.stringify(this._formatResponse({ message }, 403)));
|
|
856
852
|
}
|
|
857
853
|
return res.end(JSON.stringify({
|
|
858
854
|
message
|
|
859
|
-
}
|
|
855
|
+
}));
|
|
860
856
|
};
|
|
861
857
|
response.notFound = (message) => {
|
|
862
858
|
response.status(404);
|
|
863
859
|
message = message ?? `The request '${req.url}' was not found. Please re-check the your url again.`;
|
|
864
860
|
if (this._formatResponse != null) {
|
|
865
|
-
return res.end(JSON.stringify(this._formatResponse({ message }, 404)
|
|
861
|
+
return res.end(JSON.stringify(this._formatResponse({ message }, 404)));
|
|
866
862
|
}
|
|
867
863
|
return res.end(JSON.stringify({
|
|
868
864
|
message
|
|
869
|
-
}
|
|
865
|
+
}));
|
|
870
866
|
};
|
|
871
867
|
response.unprocessable = (message) => {
|
|
872
868
|
response.status(422);
|
|
873
869
|
message = message ?? `The request to '${req.url}' failed validation.`;
|
|
874
870
|
if (this._formatResponse != null) {
|
|
875
|
-
return res.end(JSON.stringify(this._formatResponse({ message }, 422)
|
|
871
|
+
return res.end(JSON.stringify(this._formatResponse({ message }, 422)));
|
|
876
872
|
}
|
|
877
873
|
return res.end(JSON.stringify({
|
|
878
874
|
message
|
|
879
|
-
}
|
|
875
|
+
}));
|
|
880
876
|
};
|
|
881
877
|
response.tooManyRequests = (message) => {
|
|
882
878
|
response.status(429);
|
|
883
879
|
message = message ?? `The request '${req.url}' is too many request. Please wait and try agian.`;
|
|
884
880
|
if (this._formatResponse != null) {
|
|
885
|
-
return res.end(JSON.stringify(this._formatResponse({ message }, 429)
|
|
881
|
+
return res.end(JSON.stringify(this._formatResponse({ message }, 429)));
|
|
886
882
|
}
|
|
887
883
|
return res.end(JSON.stringify({
|
|
888
884
|
message
|
|
889
|
-
}
|
|
885
|
+
}));
|
|
890
886
|
};
|
|
891
887
|
response.serverError = (message) => {
|
|
892
888
|
response.status(500);
|
|
893
889
|
message = message ?? `The request '${req.url}' resulted in a server error. Please investigate.`;
|
|
894
890
|
if (this._formatResponse != null) {
|
|
895
|
-
return res.end(JSON.stringify(this._formatResponse({ message }, 500)
|
|
891
|
+
return res.end(JSON.stringify(this._formatResponse({ message }, 500)));
|
|
896
892
|
}
|
|
897
893
|
return res.end(JSON.stringify({
|
|
898
894
|
message
|
|
899
|
-
}
|
|
895
|
+
}));
|
|
900
896
|
};
|
|
901
897
|
response.status = (code) => {
|
|
902
898
|
res.writeHead(code, { 'Content-Type': 'application/json' });
|
|
@@ -932,58 +928,48 @@ class Spear {
|
|
|
932
928
|
return response;
|
|
933
929
|
}
|
|
934
930
|
_wrapHandlers(...handlers) {
|
|
935
|
-
return (req, res,
|
|
936
|
-
|
|
931
|
+
return (req, res, ps) => {
|
|
932
|
+
if (res.writableEnded)
|
|
933
|
+
return;
|
|
934
|
+
const request = req;
|
|
935
|
+
const response = this._customizeResponse(req, res);
|
|
936
|
+
const params = ps;
|
|
937
|
+
const headers = req.headers;
|
|
938
|
+
const query = this._parser.queryString(req.url);
|
|
939
|
+
const xff = headers['x-forwarded-for'];
|
|
940
|
+
const ip = (Array.isArray(xff) ? xff[0] : xff)?.split(',')[0]?.trim()
|
|
941
|
+
|| (Array.isArray(headers['x-real-ip']) ? headers['x-real-ip'][0] : headers['x-real-ip'])
|
|
942
|
+
|| (Array.isArray(headers['cf-connecting-ip']) ? headers['cf-connecting-ip'][0] : headers['cf-connecting-ip'])
|
|
943
|
+
|| null;
|
|
944
|
+
const dispatch = (index = 0) => {
|
|
945
|
+
const body = request.body;
|
|
946
|
+
const files = request.files;
|
|
947
|
+
const cookies = request.cookies;
|
|
948
|
+
const ctx = {
|
|
949
|
+
req: request,
|
|
950
|
+
res: response,
|
|
951
|
+
headers: headers ?? {},
|
|
952
|
+
params: params ?? {},
|
|
953
|
+
query: query ?? {},
|
|
954
|
+
body: body ?? {},
|
|
955
|
+
files: files ?? {},
|
|
956
|
+
cookies: cookies ?? {},
|
|
957
|
+
ip: ip ?? null
|
|
958
|
+
};
|
|
937
959
|
try {
|
|
938
|
-
const
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
const body = request.body;
|
|
942
|
-
const files = request.files;
|
|
943
|
-
const cookies = request.cookies;
|
|
944
|
-
const headers = request.headers;
|
|
945
|
-
const url = new url_1.URL(req.url, "http://localhost");
|
|
946
|
-
const query = Object.fromEntries(url.searchParams);
|
|
947
|
-
const RecordOrEmptyRecord = (data) => {
|
|
948
|
-
if (data == null)
|
|
949
|
-
return {};
|
|
950
|
-
return Object.keys(data).length ? data : {};
|
|
951
|
-
};
|
|
952
|
-
const ctx = {
|
|
953
|
-
req: request,
|
|
954
|
-
res: response,
|
|
955
|
-
headers: RecordOrEmptyRecord(headers),
|
|
956
|
-
params: RecordOrEmptyRecord(params),
|
|
957
|
-
query: RecordOrEmptyRecord(query),
|
|
958
|
-
body: RecordOrEmptyRecord(body),
|
|
959
|
-
files: RecordOrEmptyRecord(files),
|
|
960
|
-
cookies: RecordOrEmptyRecord(cookies)
|
|
961
|
-
};
|
|
960
|
+
const handler = handlers[index];
|
|
961
|
+
if (!handler)
|
|
962
|
+
return;
|
|
962
963
|
if (index === handlers.length - 1) {
|
|
963
|
-
return this._wrapResponse(
|
|
964
|
-
.bind(handlers[index]))(ctx, this._nextFunction(ctx));
|
|
964
|
+
return Promise.resolve(this._wrapResponse(handler)(ctx, this._nextFunction(ctx))).catch(err => this._nextFunction(ctx)(err));
|
|
965
965
|
}
|
|
966
|
-
return
|
|
967
|
-
return nextHandler(index + 1);
|
|
968
|
-
});
|
|
966
|
+
return Promise.resolve(handler(ctx, () => dispatch(index + 1))).catch(err => this._nextFunction(ctx)(err));
|
|
969
967
|
}
|
|
970
968
|
catch (err) {
|
|
971
|
-
const ctx = {
|
|
972
|
-
req,
|
|
973
|
-
res: this._customizeResponse(req, res),
|
|
974
|
-
params: Object.keys(params).length ? params : {},
|
|
975
|
-
headers: {},
|
|
976
|
-
query: {},
|
|
977
|
-
body: {},
|
|
978
|
-
files: {},
|
|
979
|
-
cookies: {}
|
|
980
|
-
};
|
|
981
969
|
return this._nextFunction(ctx)(err);
|
|
982
970
|
}
|
|
983
971
|
};
|
|
984
|
-
|
|
985
|
-
return;
|
|
986
|
-
return nextHandler();
|
|
972
|
+
return dispatch();
|
|
987
973
|
};
|
|
988
974
|
}
|
|
989
975
|
_wrapResponse(handler) {
|
|
@@ -1006,9 +992,6 @@ class Spear {
|
|
|
1006
992
|
return;
|
|
1007
993
|
}
|
|
1008
994
|
if (typeof result === 'string') {
|
|
1009
|
-
if (!ctx.res.headersSent) {
|
|
1010
|
-
ctx.res.writeHead(200, { 'Content-Type': 'text/plain' });
|
|
1011
|
-
}
|
|
1012
995
|
ctx.res.end(result ?? '');
|
|
1013
996
|
return;
|
|
1014
997
|
}
|
|
@@ -1048,7 +1031,7 @@ class Spear {
|
|
|
1048
1031
|
ctx.res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
1049
1032
|
return ctx.res.end(JSON.stringify({
|
|
1050
1033
|
message: err?.message
|
|
1051
|
-
}
|
|
1034
|
+
}));
|
|
1052
1035
|
}
|
|
1053
1036
|
return callback;
|
|
1054
1037
|
}
|
|
@@ -1056,39 +1039,109 @@ class Spear {
|
|
|
1056
1039
|
if (this._formatResponse != null) {
|
|
1057
1040
|
return ctx.res.end(JSON.stringify(this._formatResponse({
|
|
1058
1041
|
message: err?.message,
|
|
1059
|
-
}, ctx.res.statusCode)
|
|
1042
|
+
}, ctx.res.statusCode)));
|
|
1060
1043
|
}
|
|
1061
|
-
|
|
1044
|
+
ctx.res.end(JSON.stringify({
|
|
1062
1045
|
message: err?.message
|
|
1063
|
-
}
|
|
1046
|
+
}));
|
|
1047
|
+
return;
|
|
1064
1048
|
}
|
|
1065
1049
|
if (this._errorHandler != null) {
|
|
1066
1050
|
return this._errorHandler(new Error(NEXT_MESSAGE), ctx);
|
|
1067
1051
|
}
|
|
1068
1052
|
ctx.res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
1069
1053
|
if (this._formatResponse != null) {
|
|
1070
|
-
|
|
1054
|
+
ctx.res.end(JSON.stringify(this._formatResponse({
|
|
1071
1055
|
message: NEXT_MESSAGE
|
|
1072
|
-
}, ctx.res.statusCode)
|
|
1056
|
+
}, ctx.res.statusCode)));
|
|
1057
|
+
return;
|
|
1073
1058
|
}
|
|
1074
|
-
|
|
1059
|
+
ctx.res.end(JSON.stringify({
|
|
1075
1060
|
message: NEXT_MESSAGE
|
|
1076
|
-
}
|
|
1061
|
+
}));
|
|
1062
|
+
return;
|
|
1077
1063
|
};
|
|
1078
1064
|
}
|
|
1065
|
+
_clusterMode({ server, port, hostname, callback }) {
|
|
1066
|
+
if (cluster_1.default.isPrimary) {
|
|
1067
|
+
const numCPUs = os_1.default.cpus().length;
|
|
1068
|
+
const maxWorkers = typeof this._cluster === 'boolean' || this._cluster == null
|
|
1069
|
+
? numCPUs
|
|
1070
|
+
: this._cluster;
|
|
1071
|
+
for (let i = 0; i < maxWorkers; i++) {
|
|
1072
|
+
cluster_1.default.fork();
|
|
1073
|
+
}
|
|
1074
|
+
cluster_1.default.on('exit', () => {
|
|
1075
|
+
cluster_1.default.fork();
|
|
1076
|
+
});
|
|
1077
|
+
}
|
|
1078
|
+
if (cluster_1.default.isWorker) {
|
|
1079
|
+
if ('App' in this._adapter) {
|
|
1080
|
+
const handler = () => {
|
|
1081
|
+
this._onListeners.forEach(listener => listener());
|
|
1082
|
+
if (this._swagger.use) {
|
|
1083
|
+
this._swaggerHandler();
|
|
1084
|
+
}
|
|
1085
|
+
callback?.({ server, port });
|
|
1086
|
+
};
|
|
1087
|
+
if (hostname) {
|
|
1088
|
+
server.listen(port, hostname, handler);
|
|
1089
|
+
return server;
|
|
1090
|
+
}
|
|
1091
|
+
server.listen(port, handler);
|
|
1092
|
+
return server;
|
|
1093
|
+
}
|
|
1094
|
+
const args = hostname
|
|
1095
|
+
? [port, hostname, () => callback?.({ server, port: port })]
|
|
1096
|
+
: [port, () => callback?.({ server, port: port })];
|
|
1097
|
+
server.listen(...args);
|
|
1098
|
+
server.on('listening', () => {
|
|
1099
|
+
this._onListeners.forEach(listener => listener());
|
|
1100
|
+
if (this._swagger.use) {
|
|
1101
|
+
this._swaggerHandler();
|
|
1102
|
+
}
|
|
1103
|
+
});
|
|
1104
|
+
server.on('error', (_) => {
|
|
1105
|
+
port = Math.floor(Math.random() * 8999) + 1000;
|
|
1106
|
+
server.listen(port);
|
|
1107
|
+
});
|
|
1108
|
+
}
|
|
1109
|
+
return;
|
|
1110
|
+
}
|
|
1079
1111
|
async _createServer() {
|
|
1080
1112
|
await this._registerMiddlewares();
|
|
1081
1113
|
await this._registerControllers();
|
|
1082
1114
|
const lookup = this._router.lookup.bind(this._router);
|
|
1083
1115
|
const cors = this._cors;
|
|
1084
|
-
const
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1116
|
+
const adapter = this._adapter;
|
|
1117
|
+
if ('App' in adapter) {
|
|
1118
|
+
const server = adapter.App();
|
|
1119
|
+
server.any('/*', (uwsRes, uwsReq) => {
|
|
1120
|
+
const { req, res } = this._uWSRequestResponse(uwsReq, uwsRes);
|
|
1121
|
+
if (cors)
|
|
1122
|
+
cors(req, res);
|
|
1090
1123
|
return lookup(req, res);
|
|
1091
1124
|
});
|
|
1125
|
+
if (this._ws) {
|
|
1126
|
+
server.ws('/*', {
|
|
1127
|
+
open: (ws) => {
|
|
1128
|
+
this._ws?.connection?.(ws);
|
|
1129
|
+
},
|
|
1130
|
+
message: (ws, message) => {
|
|
1131
|
+
this._ws?.message?.(ws, Buffer.from(message));
|
|
1132
|
+
},
|
|
1133
|
+
close: (ws, code, message) => {
|
|
1134
|
+
this._ws?.close?.(ws, code, Buffer.from(message));
|
|
1135
|
+
}
|
|
1136
|
+
});
|
|
1137
|
+
}
|
|
1138
|
+
return server;
|
|
1139
|
+
}
|
|
1140
|
+
const server = http_1.default.createServer((req, res) => {
|
|
1141
|
+
if (cors)
|
|
1142
|
+
cors(req, res);
|
|
1143
|
+
return lookup(req, res);
|
|
1144
|
+
});
|
|
1092
1145
|
if (this._ws) {
|
|
1093
1146
|
this._wss = new ws_1.default.Server({ server, ...this._wsOptions });
|
|
1094
1147
|
this._wss.on('connection', (ws) => {
|
|
@@ -1096,9 +1149,7 @@ class Spear {
|
|
|
1096
1149
|
this._ws.connection(ws);
|
|
1097
1150
|
}
|
|
1098
1151
|
ws.on('message', (data) => {
|
|
1099
|
-
|
|
1100
|
-
this._ws.message(ws, data);
|
|
1101
|
-
}
|
|
1152
|
+
this._ws?.message?.(ws, data);
|
|
1102
1153
|
});
|
|
1103
1154
|
ws.on('close', (code, reason) => {
|
|
1104
1155
|
if (this._ws?.close) {
|
|
@@ -1114,6 +1165,138 @@ class Spear {
|
|
|
1114
1165
|
}
|
|
1115
1166
|
return server;
|
|
1116
1167
|
}
|
|
1168
|
+
_uWSRequestResponse(uwsReq, uwsRes) {
|
|
1169
|
+
const req = {
|
|
1170
|
+
method: String(uwsReq.getMethod()).toLocaleUpperCase(),
|
|
1171
|
+
url: uwsReq.getUrl() + (uwsReq.getQuery() ? `?${uwsReq.getQuery()}` : ''),
|
|
1172
|
+
headers: {}
|
|
1173
|
+
};
|
|
1174
|
+
uwsReq.forEach((key, value) => req.headers[key] = value);
|
|
1175
|
+
const res = {
|
|
1176
|
+
writeHeader: (key, value) => {
|
|
1177
|
+
if (!res.aborted) {
|
|
1178
|
+
uwsRes.writeHeader(key, value);
|
|
1179
|
+
}
|
|
1180
|
+
return res;
|
|
1181
|
+
},
|
|
1182
|
+
setHeader: (key, value) => {
|
|
1183
|
+
if (!res.aborted) {
|
|
1184
|
+
uwsRes.writeHeader(key, value);
|
|
1185
|
+
}
|
|
1186
|
+
return res;
|
|
1187
|
+
},
|
|
1188
|
+
writeHead(status, context) {
|
|
1189
|
+
res.writeHeaders = {
|
|
1190
|
+
...res.writeHeaders,
|
|
1191
|
+
[status]: context
|
|
1192
|
+
};
|
|
1193
|
+
res.headersSent = true;
|
|
1194
|
+
res.statusCode = status;
|
|
1195
|
+
return res;
|
|
1196
|
+
},
|
|
1197
|
+
_writeHead(status, context) {
|
|
1198
|
+
const statusMessages = {
|
|
1199
|
+
100: 'Continue',
|
|
1200
|
+
101: 'Switching Protocols',
|
|
1201
|
+
102: 'Processing',
|
|
1202
|
+
200: 'OK',
|
|
1203
|
+
201: 'Created',
|
|
1204
|
+
202: 'Accepted',
|
|
1205
|
+
203: 'Non-Authoritative Information',
|
|
1206
|
+
204: 'No Content',
|
|
1207
|
+
205: 'Reset Content',
|
|
1208
|
+
206: 'Partial Content',
|
|
1209
|
+
207: 'Multi-Status',
|
|
1210
|
+
208: 'Already Reported',
|
|
1211
|
+
226: 'IM Used',
|
|
1212
|
+
300: 'Multiple Choices',
|
|
1213
|
+
301: 'Moved Permanently',
|
|
1214
|
+
302: 'Found',
|
|
1215
|
+
303: 'See Other',
|
|
1216
|
+
304: 'Not Modified',
|
|
1217
|
+
305: 'Use Proxy',
|
|
1218
|
+
306: '(Unused)',
|
|
1219
|
+
307: 'Temporary Redirect',
|
|
1220
|
+
308: 'Permanent Redirect',
|
|
1221
|
+
400: 'Bad Request',
|
|
1222
|
+
401: 'Unauthorized',
|
|
1223
|
+
402: 'Payment Required',
|
|
1224
|
+
403: 'Forbidden',
|
|
1225
|
+
404: 'Not Found',
|
|
1226
|
+
405: 'Method Not Allowed',
|
|
1227
|
+
406: 'Not Acceptable',
|
|
1228
|
+
407: 'Proxy Authentication Required',
|
|
1229
|
+
408: 'Request Timeout',
|
|
1230
|
+
409: 'Conflict',
|
|
1231
|
+
410: 'Gone',
|
|
1232
|
+
411: 'Length Required',
|
|
1233
|
+
412: 'Precondition Failed',
|
|
1234
|
+
413: 'Payload Too Large',
|
|
1235
|
+
414: 'URI Too Long',
|
|
1236
|
+
415: 'Unsupported Media Type',
|
|
1237
|
+
416: 'Range Not Satisfiable',
|
|
1238
|
+
417: 'Expectation Failed',
|
|
1239
|
+
418: 'I\'m a teapot',
|
|
1240
|
+
421: 'Misdirected Request',
|
|
1241
|
+
422: 'Unprocessable Entity',
|
|
1242
|
+
423: 'Locked',
|
|
1243
|
+
424: 'Failed Dependency',
|
|
1244
|
+
425: 'Too Early',
|
|
1245
|
+
426: 'Upgrade Required',
|
|
1246
|
+
428: 'Precondition Required',
|
|
1247
|
+
429: 'Too Many Requests',
|
|
1248
|
+
431: 'Request Header Fields Too Large',
|
|
1249
|
+
451: 'Unavailable For Legal Reasons',
|
|
1250
|
+
500: 'Internal Server Error',
|
|
1251
|
+
501: 'Not Implemented',
|
|
1252
|
+
502: 'Bad Gateway',
|
|
1253
|
+
503: 'Service Unavailable',
|
|
1254
|
+
504: 'Gateway Timeout',
|
|
1255
|
+
505: 'HTTP Version Not Supported',
|
|
1256
|
+
506: 'Variant Also Negotiates',
|
|
1257
|
+
507: 'Insufficient Storage',
|
|
1258
|
+
508: 'Loop Detected',
|
|
1259
|
+
510: 'Not Extended',
|
|
1260
|
+
511: 'Network Authentication Required'
|
|
1261
|
+
};
|
|
1262
|
+
const statusMessage = statusMessages[status] || statusMessages[500];
|
|
1263
|
+
res.uwsRes.writeStatus(`${status} ${statusMessage}`);
|
|
1264
|
+
res.uwsRes.writeHeader(Object.keys(context)[0], Object.values(context)[0]);
|
|
1265
|
+
return res;
|
|
1266
|
+
},
|
|
1267
|
+
writeStatus: (status) => {
|
|
1268
|
+
if (!res.aborted) {
|
|
1269
|
+
res.uwsRes.writeStatus(status);
|
|
1270
|
+
}
|
|
1271
|
+
return res;
|
|
1272
|
+
},
|
|
1273
|
+
end: (str) => {
|
|
1274
|
+
if (res.aborted) {
|
|
1275
|
+
return;
|
|
1276
|
+
}
|
|
1277
|
+
uwsRes.cork(() => {
|
|
1278
|
+
if (!res.aborted) {
|
|
1279
|
+
res.aborted = true;
|
|
1280
|
+
for (const h in res.writeHeaders) {
|
|
1281
|
+
//@ts-ignore
|
|
1282
|
+
res._writeHead(h, res.writeHeaders[h]);
|
|
1283
|
+
}
|
|
1284
|
+
uwsRes.end(str);
|
|
1285
|
+
return;
|
|
1286
|
+
}
|
|
1287
|
+
});
|
|
1288
|
+
},
|
|
1289
|
+
aborted: false,
|
|
1290
|
+
writeHeaders: {},
|
|
1291
|
+
headersSent: false,
|
|
1292
|
+
statusCode: 200,
|
|
1293
|
+
uwsRes,
|
|
1294
|
+
};
|
|
1295
|
+
uwsRes.onAborted(() => {
|
|
1296
|
+
res.aborted = true;
|
|
1297
|
+
});
|
|
1298
|
+
return { req, res };
|
|
1299
|
+
}
|
|
1117
1300
|
_normalizePath(...paths) {
|
|
1118
1301
|
const path = paths
|
|
1119
1302
|
.join('/')
|
|
@@ -1143,8 +1326,8 @@ class Spear {
|
|
|
1143
1326
|
this._router.get(staticUrl, staticSwaggerHandler);
|
|
1144
1327
|
this._router.get(path, (_, res) => {
|
|
1145
1328
|
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
1146
|
-
res.
|
|
1147
|
-
return
|
|
1329
|
+
res.end(html);
|
|
1330
|
+
return;
|
|
1148
1331
|
});
|
|
1149
1332
|
return;
|
|
1150
1333
|
}
|