@radatek/microserver 3.0.2 → 3.0.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/microserver.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * MicroServer
3
- * @version 3.0.2
3
+ * @version 3.0.3
4
4
  * @package @radatek/microserver
5
5
  * @copyright Darius Kisonas 2022
6
6
  * @license MIT
@@ -32,9 +32,6 @@ export declare class WebSocketError extends Error {
32
32
  statusCode: number;
33
33
  constructor(text?: string, code?: number);
34
34
  }
35
- type DeferPromise<T = void> = Promise<T> & {
36
- resolve: (res?: T | Error) => void;
37
- };
38
35
  /** Middleware */
39
36
  export interface Middleware {
40
37
  (req: ServerRequest, res: ServerResponse, next: Function): any;
@@ -50,7 +47,7 @@ export declare abstract class Plugin {
50
47
  constructor();
51
48
  }
52
49
  interface PluginClass {
53
- new (options?: any, server?: MicroServer): Plugin;
50
+ new (options: any, server: MicroServer): Plugin;
54
51
  }
55
52
  export type ServerRequestBody<T = any> = T extends Model<infer U extends ModelSchema> ? ModelDocument<U> : Record<string, any>;
56
53
  /** Extended http.IncomingMessage */
@@ -89,12 +86,12 @@ export declare class ServerRequest<T = any> extends http.IncomingMessage {
89
86
  rawBody: Buffer[];
90
87
  /** Request raw body size */
91
88
  rawBodySize: number;
92
- _body?: ServerRequestBody<T>;
93
- _isReady: DeferPromise | undefined;
94
89
  private constructor();
95
- static extend(req: http.IncomingMessage, server: MicroServer): ServerRequest;
90
+ static extend(req: http.IncomingMessage, res: http.ServerResponse, server: MicroServer): ServerRequest;
96
91
  get isReady(): boolean;
97
92
  waitReady(): Promise<void>;
93
+ setReady(err?: Error): void;
94
+ setBody(body: ServerRequestBody<T>): void;
98
95
  /** Update request url */
99
96
  updateUrl(url: string): this;
100
97
  /** Rewrite request url */
@@ -351,7 +348,6 @@ export declare class WebSocketPlugin extends Plugin {
351
348
  constructor(options?: any, server?: MicroServer);
352
349
  addUpgradeHandler(srv: http.Server): void;
353
350
  upgradeHandler(server: MicroServer, req: ServerRequest, socket: net.Socket, head: any): void;
354
- static create(req: ServerRequest, options?: WebSocketOptions): WebSocket;
355
351
  }
356
352
  /** Trust proxy plugin, adds `req.ip` and `req.localip` */
357
353
  export declare class TrustProxyPlugin extends Plugin {
package/microserver.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * MicroServer
3
- * @version 3.0.2
3
+ * @version 3.0.3
4
4
  * @package @radatek/microserver
5
5
  * @copyright Darius Kisonas 2022
6
6
  * @license MIT
@@ -118,11 +118,11 @@ export class ServerRequest extends http.IncomingMessage {
118
118
  rawBodySize;
119
119
  _body;
120
120
  _isReady;
121
- constructor(server) {
121
+ constructor(res, server) {
122
122
  super(new net.Socket());
123
- ServerRequest.extend(this, server);
123
+ ServerRequest.extend(this, res, server);
124
124
  }
125
- static extend(req, server) {
125
+ static extend(req, res, server) {
126
126
  const reqNew = Object.setPrototypeOf(req, ServerRequest.prototype);
127
127
  let ip = req.socket.remoteAddress || '::1';
128
128
  if (ip.startsWith('::ffff:'))
@@ -141,6 +141,15 @@ export class ServerRequest extends http.IncomingMessage {
141
141
  rawBody: [],
142
142
  rawBodySize: 0
143
143
  });
144
+ if (req.readable && !req.complete) {
145
+ reqNew._isReady = deferPromise((err) => {
146
+ reqNew._isReady = undefined;
147
+ if (err && res && !res.headersSent) {
148
+ res.statusCode = 'statusCode' in err ? err.statusCode : 400;
149
+ res.end(http.STATUS_CODES[res.statusCode] || 'Error');
150
+ }
151
+ });
152
+ }
144
153
  reqNew.updateUrl(req.url || '/');
145
154
  return reqNew;
146
155
  }
@@ -154,6 +163,12 @@ export class ServerRequest extends http.IncomingMessage {
154
163
  if (res && res instanceof Error)
155
164
  throw res;
156
165
  }
166
+ setReady(err) {
167
+ this._isReady?.resolve(err);
168
+ }
169
+ setBody(body) {
170
+ this._body = body;
171
+ }
157
172
  /** Update request url */
158
173
  updateUrl(url) {
159
174
  this.url = url;
@@ -193,7 +208,8 @@ export class ServerResponse extends http.ServerResponse {
193
208
  isJson;
194
209
  headersOnly;
195
210
  constructor(server) {
196
- super(ServerRequest.extend(new http.IncomingMessage(new net.Socket()), server));
211
+ super(new http.IncomingMessage(new net.Socket()));
212
+ ServerRequest.extend(this.req, this, server);
197
213
  ServerResponse.extend(this);
198
214
  }
199
215
  static extend(res) {
@@ -572,17 +588,8 @@ export class MicroServer extends EventEmitter {
572
588
  }
573
589
  /** Default server handler */
574
590
  handler(req, res) {
575
- ServerRequest.extend(req, this);
591
+ ServerRequest.extend(req, res, this);
576
592
  ServerResponse.extend(res);
577
- if (req.readable) {
578
- req._isReady = deferPromise((err) => {
579
- req._isReady = undefined;
580
- if (err) {
581
- if (!res.headersSent)
582
- res.error('statusCode' in err ? err.statusCode : 400);
583
- }
584
- });
585
- }
586
593
  this._router.walk(this._stack, req, res, () => res.error(404));
587
594
  //this.handlerRouter(req, res, () => this.handlerLast(req, res))
588
595
  }
@@ -991,20 +998,9 @@ export class BodyPlugin extends Plugin {
991
998
  handler(req, res, next) {
992
999
  if (req.complete || req.method === 'GET') {
993
1000
  if (!req.body)
994
- req._body = {};
1001
+ req.setBody({});
995
1002
  return next();
996
1003
  }
997
- req._isReady = deferPromise((err) => {
998
- req._isReady = undefined;
999
- if (err) {
1000
- if (!req.complete)
1001
- req.pause();
1002
- if (!res.headersSent)
1003
- res.error('statusCode' in err ? err.statusCode : 400);
1004
- }
1005
- else if (req.complete)
1006
- res.removeHeader('Connection');
1007
- });
1008
1004
  const contentType = req.headers['content-type'] || '';
1009
1005
  if (contentType.startsWith('multipart/form-data')) {
1010
1006
  req.pause();
@@ -1012,44 +1008,35 @@ export class BodyPlugin extends Plugin {
1012
1008
  return next();
1013
1009
  }
1014
1010
  if (parseInt(req.headers['content-length'] || '-1') > this._maxBodySize) {
1015
- return req._isReady?.resolve(new ResponseError("too big", 413));
1011
+ return req.setReady(new ResponseError("too big", 413));
1016
1012
  }
1017
1013
  req.once('error', () => { })
1018
1014
  .on('data', chunk => {
1019
1015
  req.rawBodySize += chunk.length;
1020
1016
  if (req.rawBodySize >= this._maxBodySize)
1021
- req._isReady?.resolve(new ResponseError("too big", 413));
1017
+ req.setReady(new ResponseError("too big", 413));
1022
1018
  else
1023
1019
  req.rawBody.push(chunk);
1024
1020
  })
1025
1021
  .once('end', () => {
1026
- req._isReady?.resolve();
1027
- Object.defineProperty(req, 'body', {
1028
- get: () => {
1029
- if (!req._body) {
1030
- let charset = contentType.match(/charset=(\S+)/)?.[1];
1031
- if (charset !== 'utf8' && charset !== 'latin1' && charset !== 'ascii')
1032
- charset = 'utf8';
1033
- const bodyString = Buffer.concat(req.rawBody).toString(charset);
1034
- if (contentType.startsWith('application/x-www-form-urlencoded')) {
1035
- req._body = querystring.parse(bodyString);
1036
- }
1037
- else if (bodyString.startsWith('{') || bodyString.startsWith('[')) {
1038
- try {
1039
- req._body = JSON.parse(bodyString);
1040
- }
1041
- catch {
1042
- return res.jsonError(405);
1043
- }
1044
- }
1045
- else
1046
- req._body = {};
1047
- }
1048
- return req._body;
1049
- },
1050
- configurable: true,
1051
- enumerable: true
1052
- });
1022
+ let charset = contentType.match(/charset=(\S+)/)?.[1];
1023
+ if (charset !== 'utf8' && charset !== 'latin1' && charset !== 'ascii')
1024
+ charset = 'utf8';
1025
+ const bodyString = Buffer.concat(req.rawBody).toString(charset);
1026
+ if (contentType.startsWith('application/x-www-form-urlencoded')) {
1027
+ req.setBody(querystring.parse(bodyString));
1028
+ }
1029
+ else if (bodyString.startsWith('{') || bodyString.startsWith('[')) {
1030
+ try {
1031
+ req.setBody(JSON.parse(bodyString));
1032
+ }
1033
+ catch {
1034
+ return res.jsonError(405);
1035
+ }
1036
+ }
1037
+ else
1038
+ req.setBody({});
1039
+ req.setReady();
1053
1040
  return next();
1054
1041
  });
1055
1042
  }
@@ -1070,19 +1057,6 @@ export class UploadPlugin extends Plugin {
1070
1057
  const contentType = req.headers['content-type'] || '';
1071
1058
  if (!contentType.startsWith('multipart/form-data'))
1072
1059
  return next();
1073
- if (!req._isReady) {
1074
- req._isReady = deferPromise((err) => {
1075
- req._isReady = undefined;
1076
- if (err) {
1077
- req.pause();
1078
- if (!res.headersSent)
1079
- res.setHeader('Connection', 'close');
1080
- res.error('statusCode' in err ? err.statusCode : 400);
1081
- }
1082
- else
1083
- res.removeHeader('Connection');
1084
- });
1085
- }
1086
1060
  req.pause();
1087
1061
  res.setHeader('Connection', 'close');
1088
1062
  if (!this._uploadDir)
@@ -1152,7 +1126,7 @@ export class UploadPlugin extends Plugin {
1152
1126
  fileStream = undefined;
1153
1127
  if (buffer[nextBoundaryIndexEnd] === 45) {
1154
1128
  res.removeHeader('Connection');
1155
- req._isReady?.resolve();
1129
+ req.setReady();
1156
1130
  }
1157
1131
  buffer = buffer.subarray(nextBoundaryIndex);
1158
1132
  }
@@ -1170,7 +1144,7 @@ export class UploadPlugin extends Plugin {
1170
1144
  };
1171
1145
  const _removeTempFiles = () => {
1172
1146
  if (!req.isReady)
1173
- req._isReady?.resolve(new Error('Upload error'));
1147
+ req.setReady(new Error('Upload error'));
1174
1148
  if (fileStream) {
1175
1149
  fileStream.close();
1176
1150
  fileStream = undefined;
@@ -1181,12 +1155,12 @@ export class UploadPlugin extends Plugin {
1181
1155
  delete f.filePath;
1182
1156
  });
1183
1157
  files.splice(0);
1184
- req._isReady?.resolve();
1158
+ req.setReady();
1185
1159
  };
1186
1160
  next();
1187
- req.once('error', () => req._isReady?.resolve(new Error('Upload error')))
1161
+ req.once('error', () => req.setReady(new Error('Upload error')))
1188
1162
  .on('data', chunk => chunkParse(chunk))
1189
- .once('end', () => req._isReady?.resolve(new Error('Upload error')));
1163
+ .once('end', () => req.setReady(new Error('Upload error')));
1190
1164
  res.once('finish', () => _removeTempFiles());
1191
1165
  res.once('error', () => _removeTempFiles());
1192
1166
  res.once('close', () => _removeTempFiles());
@@ -1230,9 +1204,12 @@ export class WebSocket extends EventEmitter {
1230
1204
  this._options.deflate = true;
1231
1205
  }
1232
1206
  this.ready = true;
1233
- this._upgrade(key, headers);
1207
+ this._upgrade(key, headers, () => {
1208
+ req.setReady();
1209
+ this.emit('open');
1210
+ });
1234
1211
  }
1235
- _upgrade(key, headers = []) {
1212
+ _upgrade(key, headers = [], cb) {
1236
1213
  const digest = crypto.createHash('sha1')
1237
1214
  .update(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')
1238
1215
  .digest('base64');
@@ -1245,7 +1222,7 @@ export class WebSocket extends EventEmitter {
1245
1222
  '',
1246
1223
  ''
1247
1224
  ];
1248
- this._socket.write(headers.join('\r\n'));
1225
+ this._socket.write(headers.join('\r\n'), cb);
1249
1226
  this._socket.on('error', this._errorHandler.bind(this));
1250
1227
  this._socket.on('data', this._dataHandler.bind(this));
1251
1228
  this._socket.on('close', () => this.emit('close'));
@@ -1511,7 +1488,6 @@ export class WebSocketPlugin extends Plugin {
1511
1488
  srv.on('upgrade', this._handler);
1512
1489
  }
1513
1490
  upgradeHandler(server, req, socket, head) {
1514
- ServerRequest.extend(req, server);
1515
1491
  const host = req.headers.host || '';
1516
1492
  const vhostPlugin = server.getPlugin('vhost');
1517
1493
  const vserver = vhostPlugin?.vhosts?.[host] || server;
@@ -1524,7 +1500,7 @@ export class WebSocketPlugin extends Plugin {
1524
1500
  statusCode: 200,
1525
1501
  socket,
1526
1502
  server,
1527
- write(data) {
1503
+ end(data) {
1528
1504
  if (res.headersSent)
1529
1505
  throw new Error('Headers already sent');
1530
1506
  let code = res.statusCode || 403;
@@ -1538,7 +1514,7 @@ export class WebSocketPlugin extends Plugin {
1538
1514
  const headers = [
1539
1515
  `HTTP/1.1 ${code} ${http.STATUS_CODES[code]}`,
1540
1516
  'Connection: close',
1541
- 'Content-Type: text/html',
1517
+ 'Content-Type: text/plain',
1542
1518
  `Content-Length: ${Buffer.byteLength(data)}`,
1543
1519
  '',
1544
1520
  data
@@ -1547,22 +1523,25 @@ export class WebSocketPlugin extends Plugin {
1547
1523
  },
1548
1524
  error(code) {
1549
1525
  res.statusCode = code || 403;
1550
- res.write();
1551
- },
1552
- end(data) {
1553
- res.write(data);
1526
+ res.end();
1554
1527
  },
1555
1528
  send(data) {
1556
- res.write(data);
1529
+ res.end(data);
1557
1530
  },
1558
1531
  getHeader() { },
1559
1532
  setHeader() { }
1560
1533
  };
1534
+ ServerRequest.extend(req, res, server);
1535
+ let _ws;
1536
+ Object.defineProperty(req, 'websocket', {
1537
+ get: () => {
1538
+ if (!_ws)
1539
+ _ws = new WebSocket(req, server.config.websocket);
1540
+ return _ws;
1541
+ },
1542
+ enumerable: true
1543
+ });
1561
1544
  vserver.handler(req, res);
1562
- //vserver.handlerRouter(req, res as unknown as ServerResponse, () => res.error(404))
1563
- }
1564
- static create(req, options) {
1565
- return new WebSocket(req, options);
1566
1545
  }
1567
1546
  }
1568
1547
  // #endregion WebSocket
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@radatek/microserver",
3
- "version": "3.0.2",
3
+ "version": "3.0.3",
4
4
  "description": "HTTP MicroServer",
5
5
  "author": "Darius Kisonas",
6
6
  "license": "MIT",