barehttp 0.4.0 → 0.5.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/README.md +45 -12
- package/lib/context/execution.js +1 -1
- package/lib/logger/index.js +15 -29
- package/lib/logger/serializers.js +1 -1
- package/lib/middlewares/cookies/cookie-manager.js +1 -1
- package/lib/request.d.ts +6 -0
- package/lib/request.js +53 -36
- package/lib/schemas/custom-schema.d.ts +32 -0
- package/lib/schemas/custom-schema.js +69 -0
- package/lib/schemas/dirty-tsm.d.ts +1 -0
- package/lib/schemas/dirty-tsm.js +201 -0
- package/lib/schemas/generator.d.ts +7 -0
- package/lib/schemas/generator.js +186 -0
- package/lib/schemas/helpers.d.ts +27 -0
- package/lib/schemas/helpers.js +50 -0
- package/lib/schemas/json-schema.d.ts +2 -0
- package/lib/schemas/json-schema.js +52 -0
- package/lib/schemas/openami-schema.d.ts +2 -0
- package/lib/schemas/openami-schema.js +63 -0
- package/lib/schemas/project.d.ts +0 -0
- package/lib/schemas/project.js +1 -0
- package/lib/server.d.ts +32 -12
- package/lib/server.js +107 -66
- package/lib/websocket.js +12 -10
- package/package.json +33 -27
- package/lib/report.d.ts +0 -2
- package/lib/report.js +0 -20
package/lib/server.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import { ServerOptions } from 'ws';
|
|
3
|
+
import Ajv from 'ajv';
|
|
3
4
|
import { BareRequest, CacheOpts } from './request';
|
|
4
5
|
import { CookiesManagerOptions } from './middlewares/cookies/cookie-manager';
|
|
5
6
|
import { HttpMethodsUnion, StatusCodesUnion } from './utils';
|
|
@@ -17,15 +18,39 @@ declare type RouteOpts<C> = {
|
|
|
17
18
|
* Request timeout handler in `ms`
|
|
18
19
|
*/
|
|
19
20
|
timeout?: number;
|
|
21
|
+
builtInRuntime?: {
|
|
22
|
+
output?: boolean;
|
|
23
|
+
};
|
|
24
|
+
middlewares?: Array<Middleware>;
|
|
20
25
|
};
|
|
21
26
|
declare type BareOptions<A extends IP> = {
|
|
27
|
+
/**
|
|
28
|
+
* Declare a global middlewares array
|
|
29
|
+
* Default: []
|
|
30
|
+
*/
|
|
22
31
|
middlewares?: Array<Middleware>;
|
|
32
|
+
/**
|
|
33
|
+
* Opt-out request body parsing (de-serialization)
|
|
34
|
+
* Default `false`
|
|
35
|
+
*/
|
|
36
|
+
doNotParseBody?: boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Opt-in to have a custom swagger per route generation
|
|
39
|
+
* Default `false`
|
|
40
|
+
*/
|
|
41
|
+
/**
|
|
42
|
+
* Opt-in to have a custom runtime JSON Schema checker per routes
|
|
43
|
+
* Default `false`
|
|
44
|
+
*/
|
|
45
|
+
enableSchemaValidation?: boolean;
|
|
23
46
|
serverPort?: number;
|
|
47
|
+
declaredRoutesPaths?: Array<string>;
|
|
24
48
|
/**
|
|
25
49
|
* Address to bind the web server to
|
|
26
50
|
* Default '0.0.0.0'
|
|
27
51
|
*/
|
|
28
52
|
serverAddress?: A | 'localhost';
|
|
53
|
+
setRandomPort?: boolean;
|
|
29
54
|
/**
|
|
30
55
|
* Enable request context storage
|
|
31
56
|
* Default `false`
|
|
@@ -52,11 +77,6 @@ declare type BareOptions<A extends IP> = {
|
|
|
52
77
|
* Log the resolved reverse DNS first hop for remote ip of the client (first proxy)
|
|
53
78
|
*/
|
|
54
79
|
reverseDns?: boolean;
|
|
55
|
-
/**
|
|
56
|
-
* Exposes a report with the routes usage.
|
|
57
|
-
* Default `false`
|
|
58
|
-
*/
|
|
59
|
-
statisticsReport?: boolean;
|
|
60
80
|
/**
|
|
61
81
|
* WebSocket server exposure
|
|
62
82
|
*/
|
|
@@ -90,24 +110,23 @@ export declare type Routes = {
|
|
|
90
110
|
[K in HttpMethodsUnion | 'declare']: HandlerExposed<K>;
|
|
91
111
|
};
|
|
92
112
|
export declare type BareHttpType<A extends IP = any> = BareServer<A> & Routes;
|
|
93
|
-
export declare type ServerMergedType = {
|
|
94
|
-
new <A extends IP>(args?: BareOptions<A>): BareHttpType<A>;
|
|
95
|
-
};
|
|
96
113
|
export declare class BareServer<A extends IP> {
|
|
97
114
|
#private;
|
|
98
115
|
private bareOptions;
|
|
99
116
|
server: Server;
|
|
100
117
|
ws?: WebSocketServer;
|
|
118
|
+
ajv?: Ajv;
|
|
119
|
+
route: Readonly<Routes>;
|
|
101
120
|
constructor(bareOptions?: BareOptions<A>);
|
|
102
|
-
private
|
|
121
|
+
private applyLaunchOptions;
|
|
103
122
|
private applyMiddlewares;
|
|
104
123
|
/**
|
|
105
124
|
* This handler is used in async generated middlewares runtime function
|
|
106
125
|
*/
|
|
107
126
|
private resolveMiddleware;
|
|
108
127
|
private setRoute;
|
|
109
|
-
private registerReport;
|
|
110
128
|
private handleRoute;
|
|
129
|
+
private resolveResponse;
|
|
111
130
|
private encodeRoute;
|
|
112
131
|
private explodeRoute;
|
|
113
132
|
private basicErrorHandler;
|
|
@@ -117,10 +136,11 @@ export declare class BareServer<A extends IP> {
|
|
|
117
136
|
get runtimeRoute(): Readonly<Routes>;
|
|
118
137
|
start(cb?: (address: string) => void): Promise<void>;
|
|
119
138
|
stop(cb?: (e?: Error) => void): Promise<void>;
|
|
139
|
+
loadRoutesSchemas(): void;
|
|
120
140
|
use(middleware: Middleware): this;
|
|
121
141
|
getMiddlewares(): Middleware[];
|
|
122
142
|
setCustomErrorHandler(eh: ErrorHandler): void;
|
|
143
|
+
getServerPort(): number;
|
|
123
144
|
getRoutes(): string[];
|
|
124
145
|
}
|
|
125
|
-
|
|
126
|
-
export { BareHttp };
|
|
146
|
+
export { BareServer as BareHttp };
|
package/lib/server.js
CHANGED
|
@@ -5,47 +5,61 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.BareHttp = exports.BareServer = void 0;
|
|
7
7
|
const find_my_way_1 = __importDefault(require("find-my-way"));
|
|
8
|
+
const ajv_1 = __importDefault(require("ajv"));
|
|
8
9
|
const request_1 = require("./request");
|
|
9
10
|
const logger_1 = require("./logger");
|
|
10
11
|
const context_1 = require("./context");
|
|
11
|
-
const report_1 = require("./report");
|
|
12
12
|
const utils_1 = require("./utils");
|
|
13
13
|
const cors_1 = require("./middlewares/cors/cors");
|
|
14
14
|
const websocket_1 = require("./websocket");
|
|
15
|
+
const generator_1 = require("./schemas/generator");
|
|
15
16
|
const dns_1 = __importDefault(require("dns"));
|
|
16
17
|
const http_1 = require("http");
|
|
17
18
|
class BareServer {
|
|
18
19
|
bareOptions;
|
|
19
20
|
server;
|
|
20
21
|
ws;
|
|
22
|
+
ajv;
|
|
23
|
+
route = {};
|
|
21
24
|
#middlewares = [];
|
|
22
25
|
#routes = new Map();
|
|
23
26
|
#routesLib = new Map();
|
|
24
|
-
#router = find_my_way_1.default({ ignoreTrailingSlash: true });
|
|
27
|
+
#router = (0, find_my_way_1.default)({ ignoreTrailingSlash: true });
|
|
25
28
|
#errorHandler = this.basicErrorHandler;
|
|
26
29
|
#corsInstance;
|
|
27
30
|
#port = 3000;
|
|
28
31
|
#host = '0.0.0.0';
|
|
29
|
-
#
|
|
32
|
+
#globalMiddlewaresRun = (_) => _;
|
|
33
|
+
#routeMiddlewaresStore = new Map();
|
|
34
|
+
#routeRuntimeSchemas = new Map();
|
|
30
35
|
constructor(bareOptions = {}) {
|
|
31
36
|
this.bareOptions = bareOptions;
|
|
32
37
|
// init
|
|
33
|
-
this.server = http_1.createServer(this.#listener.bind(this));
|
|
38
|
+
this.server = (0, http_1.createServer)(this.#listener.bind(this));
|
|
34
39
|
this.attachGracefulHandlers();
|
|
35
40
|
this.attachRoutesDeclarator();
|
|
36
|
-
this.
|
|
41
|
+
this.applyLaunchOptions();
|
|
42
|
+
this.loadRoutesSchemas();
|
|
37
43
|
return this;
|
|
38
44
|
}
|
|
39
45
|
#listener = (request, response) => {
|
|
40
|
-
const
|
|
41
|
-
const flow = new request_1.BareRequest(request, response, { logging, requestTimeFormat });
|
|
46
|
+
const flow = new request_1.BareRequest(request, response, this.bareOptions);
|
|
42
47
|
// init and attach request uuid to the context
|
|
43
48
|
if (this.bareOptions.context) {
|
|
44
|
-
context_1.newContext('request');
|
|
49
|
+
(0, context_1.newContext)('request');
|
|
45
50
|
context_1.context.current?.store.set('id', flow.ID.code);
|
|
46
51
|
}
|
|
47
|
-
//
|
|
48
|
-
this.applyMiddlewares(flow)
|
|
52
|
+
// execute global middlewares on the request
|
|
53
|
+
this.applyMiddlewares(flow)
|
|
54
|
+
.catch((e) => {
|
|
55
|
+
this.#errorHandler(e, flow, 400);
|
|
56
|
+
})
|
|
57
|
+
.then(() => {
|
|
58
|
+
// if middlewares sent the response back, stop here
|
|
59
|
+
if (flow.sent)
|
|
60
|
+
return;
|
|
61
|
+
this.#router.lookup(flow._originalRequest, flow._originalResponse);
|
|
62
|
+
});
|
|
49
63
|
};
|
|
50
64
|
/**
|
|
51
65
|
* This function generates previously defined middlewares for the sequential execution
|
|
@@ -63,15 +77,20 @@ class BareServer {
|
|
|
63
77
|
}
|
|
64
78
|
}
|
|
65
79
|
const text = lines.join('\n');
|
|
66
|
-
this.#
|
|
80
|
+
this.#globalMiddlewaresRun = new AsyncFunction('flow', text);
|
|
67
81
|
};
|
|
68
|
-
|
|
82
|
+
applyLaunchOptions = () => {
|
|
69
83
|
const { bareOptions: bo } = this;
|
|
70
|
-
|
|
84
|
+
if (bo.setRandomPort) {
|
|
85
|
+
this.#port = undefined;
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
this.#port = +(bo.serverPort || process.env.PORT || 3000);
|
|
89
|
+
}
|
|
71
90
|
this.#host = typeof bo.serverAddress === 'string' ? bo.serverAddress : '0.0.0.0';
|
|
72
91
|
// context setting
|
|
73
92
|
if (bo.context)
|
|
74
|
-
context_1.enableContext();
|
|
93
|
+
(0, context_1.enableContext)();
|
|
75
94
|
// ws attachment
|
|
76
95
|
if (bo.ws) {
|
|
77
96
|
this.ws = new websocket_1.WebSocketServer(this.server, bo.wsOptions);
|
|
@@ -85,18 +104,15 @@ class BareServer {
|
|
|
85
104
|
this.#corsInstance = new cors_1.Cors(corsOpts);
|
|
86
105
|
}
|
|
87
106
|
this.#middlewares.push(...(bo.middlewares || []));
|
|
88
|
-
if (bo.statisticsReport)
|
|
89
|
-
this.registerReport();
|
|
90
107
|
};
|
|
91
108
|
async applyMiddlewares(flow) {
|
|
92
|
-
if (!flow) {
|
|
93
|
-
throw new Error(`No flow been found to apply middlewares for, theres a sync mistake in the server.`); // should NEVER happen
|
|
94
|
-
}
|
|
95
109
|
if (this.bareOptions.cors) {
|
|
96
110
|
this.resolveMiddleware(flow, 0, this.#corsInstance?.corsMiddleware.bind(this.#corsInstance));
|
|
97
111
|
}
|
|
98
|
-
|
|
99
|
-
|
|
112
|
+
if (this.bareOptions.doNotParseBody !== true) {
|
|
113
|
+
// invoke body stream consumption
|
|
114
|
+
await flow['readBody']();
|
|
115
|
+
}
|
|
100
116
|
// attach cookies middleware
|
|
101
117
|
if (this.bareOptions.cookies) {
|
|
102
118
|
flow['attachCookieManager'](this.bareOptions.cookiesOptions);
|
|
@@ -109,11 +125,7 @@ class BareServer {
|
|
|
109
125
|
flow['setRemoteClient'](remoteClient[0]);
|
|
110
126
|
}
|
|
111
127
|
if (this.#middlewares.length)
|
|
112
|
-
await this.#
|
|
113
|
-
// now route the request if middlewares did not send the response back
|
|
114
|
-
if (!flow.sent) {
|
|
115
|
-
this.#router.lookup(flow._originalRequest, flow._originalResponse);
|
|
116
|
-
}
|
|
128
|
+
await this.#globalMiddlewaresRun(flow);
|
|
117
129
|
}
|
|
118
130
|
/**
|
|
119
131
|
* This handler is used in async generated middlewares runtime function
|
|
@@ -129,15 +141,13 @@ class BareServer {
|
|
|
129
141
|
this.#errorHandler(e, flow);
|
|
130
142
|
}
|
|
131
143
|
}
|
|
132
|
-
setRoute(method, route,
|
|
144
|
+
setRoute(method, route, isRuntime, handler, opts) {
|
|
133
145
|
const encode = this.encodeRoute(method, route);
|
|
134
|
-
this.#routes.set(encode, { hits: 0, fails: 0, success: 0 });
|
|
135
146
|
const handleFn = (req, _, routeParams) => {
|
|
136
|
-
this
|
|
137
|
-
this.handleRoute(req, checkParams(routeParams), handler, encode, opts);
|
|
147
|
+
this.handleRoute(req, checkParams(routeParams), handler, opts);
|
|
138
148
|
};
|
|
139
149
|
this.#routesLib.set(encode, handleFn);
|
|
140
|
-
if (
|
|
150
|
+
if (isRuntime) {
|
|
141
151
|
this.#router.reset();
|
|
142
152
|
this.#routesLib.forEach((handlerFn, route) => {
|
|
143
153
|
const [m, r] = this.explodeRoute(route);
|
|
@@ -148,53 +158,63 @@ class BareServer {
|
|
|
148
158
|
this.#router.on(method, route, handleFn);
|
|
149
159
|
}
|
|
150
160
|
}
|
|
151
|
-
|
|
152
|
-
this.setRoute('GET', '/_report', false, (flow) => {
|
|
153
|
-
flow.setHeader('content-type', 'text/html');
|
|
154
|
-
flow.send(report_1.generateReport(this.#routes));
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
handleRoute(req, routeParams, handle, encodedRoute, opts) {
|
|
161
|
+
handleRoute(req, routeParams, handle, routeOpts) {
|
|
158
162
|
const flow = req.flow;
|
|
159
163
|
if (!flow) {
|
|
160
164
|
throw new Error(`No flow been found to route this request, theres a sync mistake in the server.`); // should NEVER happen
|
|
161
165
|
}
|
|
162
|
-
// apply possible route options
|
|
163
|
-
if (opts) {
|
|
164
|
-
if (opts.disableCache)
|
|
165
|
-
flow.disableCache();
|
|
166
|
-
if (opts.cache)
|
|
167
|
-
flow.setCache(opts.cache);
|
|
168
|
-
if (opts.timeout)
|
|
169
|
-
flow['attachTimeout'](opts.timeout);
|
|
170
|
-
}
|
|
171
166
|
// populate with route params
|
|
172
167
|
if (routeParams)
|
|
173
168
|
flow['setParams'](routeParams);
|
|
174
|
-
//
|
|
175
|
-
if (
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
}
|
|
183
|
-
});
|
|
169
|
+
// apply possible route options
|
|
170
|
+
if (routeOpts) {
|
|
171
|
+
if (routeOpts.disableCache)
|
|
172
|
+
flow.disableCache();
|
|
173
|
+
if (routeOpts.cache)
|
|
174
|
+
flow.setCache(routeOpts.cache);
|
|
175
|
+
if (routeOpts.timeout)
|
|
176
|
+
flow['attachTimeout'](routeOpts.timeout);
|
|
184
177
|
}
|
|
178
|
+
// TODO: implement per route middlewares!
|
|
185
179
|
try {
|
|
186
|
-
const routeReturn = handle
|
|
180
|
+
const routeReturn = handle(flow);
|
|
181
|
+
if (flow.sent)
|
|
182
|
+
return;
|
|
187
183
|
if (routeReturn instanceof Promise) {
|
|
188
|
-
routeReturn
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
184
|
+
routeReturn
|
|
185
|
+
.then((result) => this.resolveResponse(flow, result, req.url, req.method?.toLowerCase(), routeOpts?.builtInRuntime?.output))
|
|
186
|
+
.catch((e) => {
|
|
187
|
+
this.#errorHandler(e, flow);
|
|
188
|
+
});
|
|
189
|
+
return;
|
|
192
190
|
}
|
|
191
|
+
this.resolveResponse(flow, routeReturn, req.url, req.method?.toLowerCase(), routeOpts?.builtInRuntime?.output);
|
|
193
192
|
}
|
|
194
193
|
catch (e) {
|
|
195
194
|
this.#errorHandler(e, flow);
|
|
196
195
|
}
|
|
197
196
|
}
|
|
197
|
+
resolveResponse(flow, response, url, method, builtInRuntime) {
|
|
198
|
+
if (!builtInRuntime || !method || !url) {
|
|
199
|
+
flow.send(response);
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
const schema = this.#routeRuntimeSchemas.get(`${method}-${url}`);
|
|
203
|
+
const check = schema?.compiled(response);
|
|
204
|
+
if ((schema && check) || !schema)
|
|
205
|
+
flow.send(response);
|
|
206
|
+
else {
|
|
207
|
+
logger_1.logMe.error('Response schema error!', {
|
|
208
|
+
method,
|
|
209
|
+
url,
|
|
210
|
+
errors: schema?.compiled.errors,
|
|
211
|
+
received: response,
|
|
212
|
+
});
|
|
213
|
+
flow
|
|
214
|
+
.status(500)
|
|
215
|
+
.send({ message: `Response schema error, please communicate to server administrator.` });
|
|
216
|
+
}
|
|
217
|
+
}
|
|
198
218
|
encodeRoute(method, route) {
|
|
199
219
|
if (route.endsWith('/'))
|
|
200
220
|
route = route.slice(0, -1);
|
|
@@ -232,7 +252,7 @@ class BareServer {
|
|
|
232
252
|
}
|
|
233
253
|
attachRoutesDeclarator() {
|
|
234
254
|
for (const method of [...Object.keys(utils_1.HttpMethods), 'declare']) {
|
|
235
|
-
this[method] = (routeSetUp) => {
|
|
255
|
+
this.route[method] = (routeSetUp) => {
|
|
236
256
|
checkRouteSetUp(routeSetUp, method);
|
|
237
257
|
if (method === 'declare') {
|
|
238
258
|
for (const m of new Set(routeSetUp.methods))
|
|
@@ -298,8 +318,10 @@ class BareServer {
|
|
|
298
318
|
// }
|
|
299
319
|
if (!this.ws)
|
|
300
320
|
await this.stopWs();
|
|
321
|
+
if (!this.server?.listening)
|
|
322
|
+
return;
|
|
301
323
|
await new Promise((res, rej) => {
|
|
302
|
-
this.server
|
|
324
|
+
this.server.close((e) => {
|
|
303
325
|
if (e) {
|
|
304
326
|
rej(e);
|
|
305
327
|
cb?.(e);
|
|
@@ -311,6 +333,23 @@ class BareServer {
|
|
|
311
333
|
});
|
|
312
334
|
});
|
|
313
335
|
}
|
|
336
|
+
loadRoutesSchemas() {
|
|
337
|
+
if (!this.bareOptions.enableSchemaValidation) {
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
if (this.bareOptions.declaredRoutesPaths?.length) {
|
|
341
|
+
this.ajv = new ajv_1.default({ strict: true });
|
|
342
|
+
for (const path of this.bareOptions.declaredRoutesPaths) {
|
|
343
|
+
const schemas = (0, generator_1.generateRouteSchema)(path);
|
|
344
|
+
for (const schema of schemas) {
|
|
345
|
+
this.#routeRuntimeSchemas.set(`${schema.methodName}-${schema.route}`, {
|
|
346
|
+
raw: schema.jsonSchema,
|
|
347
|
+
compiled: this.ajv.compile(schema.jsonSchema),
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
314
353
|
use(middleware) {
|
|
315
354
|
this.#middlewares.push(middleware);
|
|
316
355
|
return this;
|
|
@@ -321,11 +360,15 @@ class BareServer {
|
|
|
321
360
|
setCustomErrorHandler(eh) {
|
|
322
361
|
this.#errorHandler = eh;
|
|
323
362
|
}
|
|
363
|
+
getServerPort() {
|
|
364
|
+
return this.server.address().port;
|
|
365
|
+
}
|
|
324
366
|
getRoutes() {
|
|
325
367
|
return [...this.#routes.keys()];
|
|
326
368
|
}
|
|
327
369
|
}
|
|
328
370
|
exports.BareServer = BareServer;
|
|
371
|
+
exports.BareHttp = BareServer;
|
|
329
372
|
function checkRouteSetUp(routeSetUp, key) {
|
|
330
373
|
if (typeof routeSetUp.route !== 'string') {
|
|
331
374
|
throw new TypeError(`A route path for the method ${key} is not a a string`);
|
|
@@ -358,5 +401,3 @@ function checkParams(params) {
|
|
|
358
401
|
}
|
|
359
402
|
return params;
|
|
360
403
|
}
|
|
361
|
-
const BareHttp = BareServer;
|
|
362
|
-
exports.BareHttp = BareHttp;
|
package/lib/websocket.js
CHANGED
|
@@ -9,7 +9,7 @@ const callsites_1 = __importDefault(require("callsites"));
|
|
|
9
9
|
const hyperid_1 = __importDefault(require("hyperid"));
|
|
10
10
|
const logger_1 = require("./logger");
|
|
11
11
|
const utils_1 = require("./utils");
|
|
12
|
-
const generateId = hyperid_1.default();
|
|
12
|
+
const generateId = (0, hyperid_1.default)();
|
|
13
13
|
class WebSocketServer {
|
|
14
14
|
opts;
|
|
15
15
|
_internal;
|
|
@@ -42,21 +42,23 @@ class WebSocketServer {
|
|
|
42
42
|
if (response instanceof Promise) {
|
|
43
43
|
response
|
|
44
44
|
.then((answer) => this.doUpgrade(answer, request, socket, head))
|
|
45
|
-
.catch((e) => this.rejectUpgrade(socket, e?.message, e));
|
|
45
|
+
.catch((e) => this.rejectUpgrade(request, socket, e?.message, e));
|
|
46
46
|
}
|
|
47
47
|
else {
|
|
48
48
|
this.doUpgrade(response, request, socket, head);
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
catch (e) {
|
|
52
|
-
|
|
52
|
+
if (e instanceof Error) {
|
|
53
|
+
this.rejectUpgrade(request, socket, e?.message, e);
|
|
54
|
+
}
|
|
53
55
|
}
|
|
54
56
|
});
|
|
55
57
|
this.customUpgradeDone = true;
|
|
56
58
|
}
|
|
57
59
|
doUpgrade(answer, request, socket, head) {
|
|
58
60
|
if (!answer.access)
|
|
59
|
-
this.rejectUpgrade(socket, answer.message);
|
|
61
|
+
this.rejectUpgrade(request, socket, answer.message);
|
|
60
62
|
else {
|
|
61
63
|
this._internal.handleUpgrade(request, socket, head, (ws) => {
|
|
62
64
|
const userClient = {
|
|
@@ -68,15 +70,15 @@ class WebSocketServer {
|
|
|
68
70
|
});
|
|
69
71
|
}
|
|
70
72
|
}
|
|
71
|
-
rejectUpgrade(socket, message = 'Not Authorized', data) {
|
|
72
|
-
logger_1.logMe.warn(message || `Upgrade rejected for the client from ${socket.remoteAddress}`, data);
|
|
73
|
+
rejectUpgrade(request, socket, message = 'Not Authorized', data) {
|
|
74
|
+
logger_1.logMe.warn(message || `Upgrade rejected for the client from ${request.socket.remoteAddress}`, data);
|
|
73
75
|
socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n'); // TODO: enhance to be able to personalize this
|
|
74
76
|
socket.destroy();
|
|
75
77
|
}
|
|
76
78
|
attachTypesHandling() {
|
|
77
79
|
this._internal.on('connection', (ws, _, client) => {
|
|
78
80
|
ws.onmessage = (event) => {
|
|
79
|
-
const decode = utils_1.JSONParse(event.data);
|
|
81
|
+
const decode = (0, utils_1.JSONParse)(event.data);
|
|
80
82
|
if (decode === null) {
|
|
81
83
|
logger_1.logMe.error('Incorrect data received from the client', {
|
|
82
84
|
data: event.data,
|
|
@@ -105,7 +107,7 @@ class WebSocketServer {
|
|
|
105
107
|
.then((resolvedResponse) => {
|
|
106
108
|
if (!resolvedResponse)
|
|
107
109
|
return;
|
|
108
|
-
this.send({ ws, client }, utils_1.JSONStringify({ type: `${decode.type}_RESPONSE`, ...resolvedResponse }));
|
|
110
|
+
this.send({ ws, client }, (0, utils_1.JSONStringify)({ type: `${decode.type}_RESPONSE`, ...resolvedResponse }));
|
|
109
111
|
})
|
|
110
112
|
.catch((e) => logger_1.logMe.error(`Error working out a handler for type ${decode.type}`, {
|
|
111
113
|
error: e,
|
|
@@ -116,7 +118,7 @@ class WebSocketServer {
|
|
|
116
118
|
else {
|
|
117
119
|
if (!response)
|
|
118
120
|
return;
|
|
119
|
-
this.send({ ws, client }, utils_1.JSONStringify({ type: `${decode.type}_RESPONSE`, ...response }));
|
|
121
|
+
this.send({ ws, client }, (0, utils_1.JSONStringify)({ type: `${decode.type}_RESPONSE`, ...response }));
|
|
120
122
|
}
|
|
121
123
|
}
|
|
122
124
|
catch (e) {
|
|
@@ -147,7 +149,7 @@ class WebSocketServer {
|
|
|
147
149
|
if (typeof receiver.handler !== 'function') {
|
|
148
150
|
throw new Error(`Can't declare a handler with type ${typeof receiver.handler}, should be a function with following signature: WsMessageHandler<T,?>`);
|
|
149
151
|
}
|
|
150
|
-
const place = callsites_1.default()[2];
|
|
152
|
+
const place = (0, callsites_1.default)()[2];
|
|
151
153
|
const loc = `${place.getFileName()}:${place.getLineNumber()}:${place.getColumnNumber()}`;
|
|
152
154
|
this.#types.set(receiver.type, { loc, handler: receiver.handler });
|
|
153
155
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "barehttp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Lightweight and fast Node.js web server",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"directories": {
|
|
@@ -11,11 +11,11 @@
|
|
|
11
11
|
"lib"
|
|
12
12
|
],
|
|
13
13
|
"scripts": {
|
|
14
|
-
"build": "rm -rf ./lib && tsc",
|
|
14
|
+
"build": "rm -rf ./lib && tsc -p tsconfig.build.json",
|
|
15
15
|
"build:dev": "rm -rf ./dev-lib && tsc -p tsconfig.dev.json",
|
|
16
|
-
"test": "jest --
|
|
16
|
+
"test": "jest --coverage",
|
|
17
17
|
"lint": "eslint ./src --fix",
|
|
18
|
-
"release": "semantic-release"
|
|
18
|
+
"release": "semantic-release -e ./.releaserc.json"
|
|
19
19
|
},
|
|
20
20
|
"keywords": [
|
|
21
21
|
"nodejs",
|
|
@@ -33,37 +33,43 @@
|
|
|
33
33
|
},
|
|
34
34
|
"license": "MIT",
|
|
35
35
|
"dependencies": {
|
|
36
|
+
"ajv": "^8.11.0",
|
|
36
37
|
"callsites": "^3.1.0",
|
|
37
|
-
"cookie": "^0.
|
|
38
|
-
"cookie-signature": "^1.
|
|
39
|
-
"find-my-way": "^
|
|
40
|
-
"hyperid": "^2.1
|
|
41
|
-
"
|
|
42
|
-
"pino
|
|
43
|
-
"
|
|
38
|
+
"cookie": "^0.5.0",
|
|
39
|
+
"cookie-signature": "^1.2.0",
|
|
40
|
+
"find-my-way": "^5.6.0",
|
|
41
|
+
"hyperid": "^2.3.1",
|
|
42
|
+
"lodash": "^4.17.21",
|
|
43
|
+
"pino": "^7.11.0",
|
|
44
|
+
"pino-pretty": "^7.6.1",
|
|
45
|
+
"ts-morph": "^14.0.0",
|
|
46
|
+
"ws": "^8.6.0"
|
|
44
47
|
},
|
|
45
48
|
"devDependencies": {
|
|
46
|
-
"@
|
|
47
|
-
"@
|
|
49
|
+
"@semantic-release/git": "^10.0.1",
|
|
50
|
+
"@semantic-release/github": "^8.0.2",
|
|
51
|
+
"@ts-morph/bootstrap": "^0.12.2",
|
|
52
|
+
"@types/cookie": "^0.4.1",
|
|
48
53
|
"@types/cookie-signature": "^1.0.3",
|
|
49
|
-
"@types/jest": "^
|
|
50
|
-
"@types/node": "^
|
|
51
|
-
"@types/pino": "^
|
|
52
|
-
"@types/ws": "^
|
|
53
|
-
"@typescript-eslint/eslint-plugin": "^
|
|
54
|
-
"@typescript-eslint/parser": "^
|
|
54
|
+
"@types/jest": "^27.0.3",
|
|
55
|
+
"@types/node": "^14.17.0",
|
|
56
|
+
"@types/pino": "^7.0.5",
|
|
57
|
+
"@types/ws": "^8.2.1",
|
|
58
|
+
"@typescript-eslint/eslint-plugin": "^5.5.0",
|
|
59
|
+
"@typescript-eslint/parser": "^5.5.0",
|
|
55
60
|
"axios": "^0.21.1",
|
|
56
|
-
"eslint": "^
|
|
57
|
-
"eslint-plugin-import": "^2.
|
|
58
|
-
"eslint-plugin-jest": "^
|
|
61
|
+
"eslint": "^8.3.0",
|
|
62
|
+
"eslint-plugin-import": "^2.25.3",
|
|
63
|
+
"eslint-plugin-jest": "^25.3.0",
|
|
59
64
|
"express": "^4.17.1",
|
|
60
|
-
"fastify": "^3.
|
|
61
|
-
"jest": "^
|
|
62
|
-
"
|
|
65
|
+
"fastify": "^3.24.1",
|
|
66
|
+
"jest": "^27.4.3",
|
|
67
|
+
"prettier": "^2.5.1",
|
|
68
|
+
"semantic-release": "^18.0.1",
|
|
63
69
|
"supertest": "^6.1.3",
|
|
64
|
-
"ts-jest": "^
|
|
70
|
+
"ts-jest": "^27.0.7",
|
|
65
71
|
"ts-node-dev": "^1.1.6",
|
|
66
|
-
"typescript": "^4.
|
|
72
|
+
"typescript": "^4.6.4"
|
|
67
73
|
},
|
|
68
74
|
"optionalDependencies": {
|
|
69
75
|
"bufferutil": "^4.0.3",
|
package/lib/report.d.ts
DELETED
package/lib/report.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.generateReport = void 0;
|
|
4
|
-
function generateReport(routes) {
|
|
5
|
-
const lines = [];
|
|
6
|
-
lines.push('<!DOCTYPE html><html><head><title>Routes usage</title><meta charset="utf-8"></head><body><table style="border: 2px;border-style: ridge;border-radius: 5px;padding: 10px;"><tr><th>Route</th><th>Hits</th><th>Successes</th><th>Fails</th></tr>');
|
|
7
|
-
const sorted = [...routes].sort(([a], [b]) => {
|
|
8
|
-
if (a > b)
|
|
9
|
-
return 1;
|
|
10
|
-
else if (b > a)
|
|
11
|
-
return -1;
|
|
12
|
-
return 0;
|
|
13
|
-
});
|
|
14
|
-
sorted.forEach(([route, stats]) => {
|
|
15
|
-
lines.push(`<tr><td>${route}</td><td>${stats.hits}</td><td>${stats.success}</td><td>${stats.fails}</td></tr>`);
|
|
16
|
-
});
|
|
17
|
-
lines.push('</table></body></html>');
|
|
18
|
-
return lines.join('');
|
|
19
|
-
}
|
|
20
|
-
exports.generateReport = generateReport;
|