@radatek/microserver 3.0.1 → 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.0
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 {
@@ -392,6 +388,8 @@ export interface StaticFilesOptions {
392
388
  etag?: boolean;
393
389
  /** Max file age in seconds */
394
390
  maxAge?: number;
391
+ /** static errors file for status code, '*' - default */
392
+ errors?: Record<string, string>;
395
393
  }
396
394
  export interface ServeFileOptions {
397
395
  /** path */
@@ -445,6 +443,7 @@ export declare class StaticFilesPlugin extends Plugin {
445
443
  /** Max file age in seconds (default: 31536000) */
446
444
  maxAge?: number;
447
445
  prefix: string;
446
+ errors?: Record<string, string>;
448
447
  constructor(options?: StaticFilesOptions | string, server?: MicroServer);
449
448
  /** Default static files handler */
450
449
  handler(req: ServerRequest, res: ServerResponse, next: Function): any;
package/microserver.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * MicroServer
3
- * @version 3.0.0
3
+ * @version 3.0.3
4
4
  * @package @radatek/microserver
5
5
  * @copyright Darius Kisonas 2022
6
6
  * @license MIT
@@ -65,13 +65,10 @@ export class WebSocketError extends Error {
65
65
  }
66
66
  function deferPromise(cb) {
67
67
  let _resolve;
68
- const p = new Promise((resolve, reject) => {
68
+ const p = new Promise((resolve) => {
69
69
  _resolve = (res) => {
70
70
  cb?.(res);
71
- if (res instanceof Error)
72
- reject(res);
73
- else
74
- resolve(res);
71
+ resolve(res);
75
72
  };
76
73
  });
77
74
  p.resolve = _resolve;
@@ -121,11 +118,11 @@ export class ServerRequest extends http.IncomingMessage {
121
118
  rawBodySize;
122
119
  _body;
123
120
  _isReady;
124
- constructor(server) {
121
+ constructor(res, server) {
125
122
  super(new net.Socket());
126
- ServerRequest.extend(this, server);
123
+ ServerRequest.extend(this, res, server);
127
124
  }
128
- static extend(req, server) {
125
+ static extend(req, res, server) {
129
126
  const reqNew = Object.setPrototypeOf(req, ServerRequest.prototype);
130
127
  let ip = req.socket.remoteAddress || '::1';
131
128
  if (ip.startsWith('::ffff:'))
@@ -144,6 +141,15 @@ export class ServerRequest extends http.IncomingMessage {
144
141
  rawBody: [],
145
142
  rawBodySize: 0
146
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
+ }
147
153
  reqNew.updateUrl(req.url || '/');
148
154
  return reqNew;
149
155
  }
@@ -153,7 +159,15 @@ export class ServerRequest extends http.IncomingMessage {
153
159
  async waitReady() {
154
160
  if (this._isReady === undefined)
155
161
  return;
156
- await this._isReady;
162
+ const res = await this._isReady;
163
+ if (res && res instanceof Error)
164
+ throw res;
165
+ }
166
+ setReady(err) {
167
+ this._isReady?.resolve(err);
168
+ }
169
+ setBody(body) {
170
+ this._body = body;
157
171
  }
158
172
  /** Update request url */
159
173
  updateUrl(url) {
@@ -194,7 +208,8 @@ export class ServerResponse extends http.ServerResponse {
194
208
  isJson;
195
209
  headersOnly;
196
210
  constructor(server) {
197
- 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);
198
213
  ServerResponse.extend(this);
199
214
  }
200
215
  static extend(res) {
@@ -260,30 +275,31 @@ export class ServerResponse extends http.ServerResponse {
260
275
  send(data = '') {
261
276
  if (this.headersSent)
262
277
  return;
263
- if (!this.req.complete) {
264
- this.req.pause();
265
- this.setHeader('Connection', 'close');
266
- }
267
- if (data instanceof Readable)
268
- return (data.pipe(this, { end: true }), void 0);
269
- if (!this.getHeader('Content-Type') && !(data instanceof Buffer)) {
278
+ if (typeof data === 'object' || this.isJson) {
270
279
  if (data instanceof Error)
271
280
  return this.error(data);
272
- if (this.isJson || typeof data === 'object') {
273
- data = JSON.stringify(typeof data === 'string' ? { message: data } : data);
274
- this.setHeader('Content-Type', 'application/json');
275
- }
276
- else {
277
- data = data.toString();
278
- if (data[0] === '{' || data[1] === '[')
279
- this.setHeader('Content-Type', 'application/json');
280
- else if (data[0] === '<' && (data.startsWith('<!DOCTYPE') || data.startsWith('<html')))
281
- this.setHeader('Content-Type', 'text/html');
281
+ if (data instanceof Readable)
282
+ return (data.pipe(this, { end: true }), void 0);
283
+ if (data instanceof Buffer) {
284
+ this.setHeader('Content-Length', data.byteLength);
285
+ if (this.headersOnly)
286
+ this.end();
282
287
  else
283
- this.setHeader('Content-Type', 'text/plain');
288
+ this.end(data);
289
+ return;
284
290
  }
291
+ data = JSON.stringify(typeof data === 'string' ? { message: data } : data);
292
+ this.setHeader('Content-Type', 'application/json');
285
293
  }
286
294
  data = data.toString();
295
+ if (!this.getHeader('Content-Type')) {
296
+ if (data[0] === '{' || data[1] === '[')
297
+ this.setHeader('Content-Type', 'application/json');
298
+ else if (data[0] === '<' && (data.startsWith('<!DOCTYPE') || data.startsWith('<html')))
299
+ this.setHeader('Content-Type', 'text/html');
300
+ else
301
+ this.setHeader('Content-Type', 'text/plain');
302
+ }
287
303
  this.setHeader('Content-Length', Buffer.byteLength(data, 'utf8'));
288
304
  if (this.headersOnly)
289
305
  this.end();
@@ -342,6 +358,7 @@ export class ServerResponse extends http.ServerResponse {
342
358
  export class MicroServer extends EventEmitter {
343
359
  config;
344
360
  auth;
361
+ _plugins = {};
345
362
  _stack = [];
346
363
  _router = new RouterPlugin();
347
364
  _worker = new Worker();
@@ -571,17 +588,8 @@ export class MicroServer extends EventEmitter {
571
588
  }
572
589
  /** Default server handler */
573
590
  handler(req, res) {
574
- ServerRequest.extend(req, this);
591
+ ServerRequest.extend(req, res, this);
575
592
  ServerResponse.extend(res);
576
- if (req.readable) {
577
- req._isReady = deferPromise((err) => {
578
- req._isReady = undefined;
579
- if (err) {
580
- if (!res.headersSent)
581
- res.error('statusCode' in err ? err.statusCode : 400);
582
- }
583
- });
584
- }
585
593
  this._router.walk(this._stack, req, res, () => res.error(404));
586
594
  //this.handlerRouter(req, res, () => this.handlerLast(req, res))
587
595
  }
@@ -596,6 +604,7 @@ export class MicroServer extends EventEmitter {
596
604
  /** Clear routes and middlewares */
597
605
  clear() {
598
606
  this._stack = [];
607
+ this._plugins = {};
599
608
  this._router.clear();
600
609
  this._plugin(this._router);
601
610
  return this;
@@ -689,6 +698,7 @@ export class MicroServer extends EventEmitter {
689
698
  await this.use(routes);
690
699
  }
691
700
  if (plugin.handler && plugin.name) {
701
+ this._plugins[plugin.name] = plugin;
692
702
  this.emit('plugin', plugin.name);
693
703
  this.emit('plugin:' + plugin.name);
694
704
  this._worker.endJob('plugin:' + plugin.name);
@@ -702,8 +712,7 @@ export class MicroServer extends EventEmitter {
702
712
  this._stack.splice(idx + 1, 0, middleware);
703
713
  }
704
714
  getPlugin(id) {
705
- let p = this._stack.find(m => m.plugin?.name === id);
706
- return p?.plugin;
715
+ return this._plugins[id];
707
716
  }
708
717
  async waitPlugin(id) {
709
718
  const p = this.getPlugin(id);
@@ -989,20 +998,9 @@ export class BodyPlugin extends Plugin {
989
998
  handler(req, res, next) {
990
999
  if (req.complete || req.method === 'GET') {
991
1000
  if (!req.body)
992
- req._body = {};
1001
+ req.setBody({});
993
1002
  return next();
994
1003
  }
995
- req._isReady = deferPromise((err) => {
996
- req._isReady = undefined;
997
- if (err) {
998
- if (!req.complete)
999
- req.pause();
1000
- if (!res.headersSent)
1001
- res.error('statusCode' in err ? err.statusCode : 400);
1002
- }
1003
- else if (req.complete)
1004
- res.removeHeader('Connection');
1005
- });
1006
1004
  const contentType = req.headers['content-type'] || '';
1007
1005
  if (contentType.startsWith('multipart/form-data')) {
1008
1006
  req.pause();
@@ -1010,44 +1008,35 @@ export class BodyPlugin extends Plugin {
1010
1008
  return next();
1011
1009
  }
1012
1010
  if (parseInt(req.headers['content-length'] || '-1') > this._maxBodySize) {
1013
- return req._isReady?.resolve(new ResponseError("too big", 413));
1011
+ return req.setReady(new ResponseError("too big", 413));
1014
1012
  }
1015
1013
  req.once('error', () => { })
1016
1014
  .on('data', chunk => {
1017
1015
  req.rawBodySize += chunk.length;
1018
1016
  if (req.rawBodySize >= this._maxBodySize)
1019
- req._isReady?.resolve(new ResponseError("too big", 413));
1017
+ req.setReady(new ResponseError("too big", 413));
1020
1018
  else
1021
1019
  req.rawBody.push(chunk);
1022
1020
  })
1023
1021
  .once('end', () => {
1024
- req._isReady?.resolve();
1025
- Object.defineProperty(req, 'body', {
1026
- get: () => {
1027
- if (!req._body) {
1028
- let charset = contentType.match(/charset=(\S+)/)?.[1];
1029
- if (charset !== 'utf8' && charset !== 'latin1' && charset !== 'ascii')
1030
- charset = 'utf8';
1031
- const bodyString = Buffer.concat(req.rawBody).toString(charset);
1032
- if (contentType.startsWith('application/x-www-form-urlencoded')) {
1033
- req._body = querystring.parse(bodyString);
1034
- }
1035
- else if (bodyString.startsWith('{') || bodyString.startsWith('[')) {
1036
- try {
1037
- req._body = JSON.parse(bodyString);
1038
- }
1039
- catch {
1040
- return res.jsonError(405);
1041
- }
1042
- }
1043
- else
1044
- req._body = {};
1045
- }
1046
- return req._body;
1047
- },
1048
- configurable: true,
1049
- enumerable: true
1050
- });
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();
1051
1040
  return next();
1052
1041
  });
1053
1042
  }
@@ -1068,19 +1057,6 @@ export class UploadPlugin extends Plugin {
1068
1057
  const contentType = req.headers['content-type'] || '';
1069
1058
  if (!contentType.startsWith('multipart/form-data'))
1070
1059
  return next();
1071
- if (!req._isReady) {
1072
- req._isReady = deferPromise((err) => {
1073
- req._isReady = undefined;
1074
- if (err) {
1075
- req.pause();
1076
- if (!res.headersSent)
1077
- res.setHeader('Connection', 'close');
1078
- res.error('statusCode' in err ? err.statusCode : 400);
1079
- }
1080
- else
1081
- res.removeHeader('Connection');
1082
- });
1083
- }
1084
1060
  req.pause();
1085
1061
  res.setHeader('Connection', 'close');
1086
1062
  if (!this._uploadDir)
@@ -1148,8 +1124,10 @@ export class UploadPlugin extends Plugin {
1148
1124
  fileStream.end();
1149
1125
  lastFile.size += nextBoundaryIndex - 2;
1150
1126
  fileStream = undefined;
1151
- if (buffer[nextBoundaryIndexEnd] === 45)
1152
- req._isReady?.resolve();
1127
+ if (buffer[nextBoundaryIndexEnd] === 45) {
1128
+ res.removeHeader('Connection');
1129
+ req.setReady();
1130
+ }
1153
1131
  buffer = buffer.subarray(nextBoundaryIndex);
1154
1132
  }
1155
1133
  else {
@@ -1166,7 +1144,7 @@ export class UploadPlugin extends Plugin {
1166
1144
  };
1167
1145
  const _removeTempFiles = () => {
1168
1146
  if (!req.isReady)
1169
- req._isReady?.resolve(new Error('Upload error'));
1147
+ req.setReady(new Error('Upload error'));
1170
1148
  if (fileStream) {
1171
1149
  fileStream.close();
1172
1150
  fileStream = undefined;
@@ -1177,12 +1155,12 @@ export class UploadPlugin extends Plugin {
1177
1155
  delete f.filePath;
1178
1156
  });
1179
1157
  files.splice(0);
1180
- req._isReady?.resolve();
1158
+ req.setReady();
1181
1159
  };
1182
1160
  next();
1183
- req.once('error', () => req._isReady?.resolve(new Error('Upload error')))
1161
+ req.once('error', () => req.setReady(new Error('Upload error')))
1184
1162
  .on('data', chunk => chunkParse(chunk))
1185
- .once('end', () => req._isReady?.resolve(new Error('Upload error')));
1163
+ .once('end', () => req.setReady(new Error('Upload error')));
1186
1164
  res.once('finish', () => _removeTempFiles());
1187
1165
  res.once('error', () => _removeTempFiles());
1188
1166
  res.once('close', () => _removeTempFiles());
@@ -1226,9 +1204,12 @@ export class WebSocket extends EventEmitter {
1226
1204
  this._options.deflate = true;
1227
1205
  }
1228
1206
  this.ready = true;
1229
- this._upgrade(key, headers);
1207
+ this._upgrade(key, headers, () => {
1208
+ req.setReady();
1209
+ this.emit('open');
1210
+ });
1230
1211
  }
1231
- _upgrade(key, headers = []) {
1212
+ _upgrade(key, headers = [], cb) {
1232
1213
  const digest = crypto.createHash('sha1')
1233
1214
  .update(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')
1234
1215
  .digest('base64');
@@ -1241,7 +1222,7 @@ export class WebSocket extends EventEmitter {
1241
1222
  '',
1242
1223
  ''
1243
1224
  ];
1244
- this._socket.write(headers.join('\r\n'));
1225
+ this._socket.write(headers.join('\r\n'), cb);
1245
1226
  this._socket.on('error', this._errorHandler.bind(this));
1246
1227
  this._socket.on('data', this._dataHandler.bind(this));
1247
1228
  this._socket.on('close', () => this.emit('close'));
@@ -1507,7 +1488,6 @@ export class WebSocketPlugin extends Plugin {
1507
1488
  srv.on('upgrade', this._handler);
1508
1489
  }
1509
1490
  upgradeHandler(server, req, socket, head) {
1510
- ServerRequest.extend(req, server);
1511
1491
  const host = req.headers.host || '';
1512
1492
  const vhostPlugin = server.getPlugin('vhost');
1513
1493
  const vserver = vhostPlugin?.vhosts?.[host] || server;
@@ -1520,7 +1500,7 @@ export class WebSocketPlugin extends Plugin {
1520
1500
  statusCode: 200,
1521
1501
  socket,
1522
1502
  server,
1523
- write(data) {
1503
+ end(data) {
1524
1504
  if (res.headersSent)
1525
1505
  throw new Error('Headers already sent');
1526
1506
  let code = res.statusCode || 403;
@@ -1534,7 +1514,7 @@ export class WebSocketPlugin extends Plugin {
1534
1514
  const headers = [
1535
1515
  `HTTP/1.1 ${code} ${http.STATUS_CODES[code]}`,
1536
1516
  'Connection: close',
1537
- 'Content-Type: text/html',
1517
+ 'Content-Type: text/plain',
1538
1518
  `Content-Length: ${Buffer.byteLength(data)}`,
1539
1519
  '',
1540
1520
  data
@@ -1543,22 +1523,25 @@ export class WebSocketPlugin extends Plugin {
1543
1523
  },
1544
1524
  error(code) {
1545
1525
  res.statusCode = code || 403;
1546
- res.write();
1547
- },
1548
- end(data) {
1549
- res.write(data);
1526
+ res.end();
1550
1527
  },
1551
1528
  send(data) {
1552
- res.write(data);
1529
+ res.end(data);
1553
1530
  },
1554
1531
  getHeader() { },
1555
1532
  setHeader() { }
1556
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
+ });
1557
1544
  vserver.handler(req, res);
1558
- //vserver.handlerRouter(req, res as unknown as ServerResponse, () => res.error(404))
1559
- }
1560
- static create(req, options) {
1561
- return new WebSocket(req, options);
1562
1545
  }
1563
1546
  }
1564
1547
  // #endregion WebSocket
@@ -1685,6 +1668,7 @@ export class StaticFilesPlugin extends Plugin {
1685
1668
  /** Max file age in seconds (default: 31536000) */
1686
1669
  maxAge;
1687
1670
  prefix;
1671
+ errors;
1688
1672
  constructor(options, server) {
1689
1673
  super();
1690
1674
  if (!options)
@@ -1695,23 +1679,37 @@ export class StaticFilesPlugin extends Plugin {
1695
1679
  if (server && !server.getPlugin('static'))
1696
1680
  this.name = 'static';
1697
1681
  this.mimeTypes = options.mimeTypes ? { ...StaticFilesPlugin.mimeTypes, ...options.mimeTypes } : Object.freeze(StaticFilesPlugin.mimeTypes);
1698
- this.root = path.resolve((options.root || options?.path || 'public').replace(/^\//, '')) + path.sep;
1682
+ this.root = (options.root && path.isAbsolute(options.root) ? options.root : path.resolve(options.root || options?.path || 'public')).replace(/[\/\\]$/, '') + path.sep;
1699
1683
  this.ignore = (options.ignore || []).map((p) => path.normalize(path.join(this.root, p)) + path.sep);
1700
1684
  this.index = options.index || 'index.html';
1701
1685
  this.handlers = options.handlers;
1702
1686
  this.lastModified = options.lastModified !== false;
1703
1687
  this.etag = options.etag !== false;
1704
1688
  this.maxAge = options.maxAge;
1689
+ this.errors = options.errors;
1705
1690
  this.prefix = ('/' + (options.path?.replace(/^[.\/]*/, '') || '').replace(/\/$/, '')).replace(/\/$/, '');
1706
- }
1707
- /** Default static files handler */
1708
- handler(req, res, next) {
1709
- res.file = (path) => {
1710
- this.serveFile(req, res, typeof path === 'object' ? path : {
1691
+ const defSend = ServerResponse.prototype.send;
1692
+ ServerResponse.prototype.send = function (data) {
1693
+ const plugin = this.req.server.getPlugin('static');
1694
+ if (this.statusCode < 400 || this.isJson || typeof data !== 'string' || !plugin?.errors || this.getHeader('Content-Type'))
1695
+ return defSend.call(this, data);
1696
+ const errFile = plugin.errors[this.statusCode] || plugin.errors['*'];
1697
+ if (errFile)
1698
+ plugin.serveFile(this.req, this, { path: errFile, mimeType: 'text/html' });
1699
+ return defSend.call(this, data);
1700
+ };
1701
+ ServerResponse.prototype.file = function (path) {
1702
+ const plugin = this.req.server.getPlugin('static');
1703
+ if (!plugin)
1704
+ throw new Error('Server error');
1705
+ plugin.serveFile(this.req, this, typeof path === 'object' ? path : {
1711
1706
  path,
1712
1707
  mimeType: StaticFilesPlugin.mimeTypes[extname(path)] || 'application/octet-stream'
1713
1708
  });
1714
1709
  };
1710
+ }
1711
+ /** Default static files handler */
1712
+ handler(req, res, next) {
1715
1713
  if (req.method !== 'GET')
1716
1714
  return next();
1717
1715
  if (!('path' in req.params)) { // global handler
@@ -1756,10 +1754,12 @@ export class StaticFilesPlugin extends Plugin {
1756
1754
  serveFile(req, res, options) {
1757
1755
  const filePath = path.isAbsolute(options.path) ? options.path : path.join(options.root || this.root, options.path);
1758
1756
  const statRes = (err, stats) => {
1759
- if (err)
1760
- return res.error(err);
1761
- if (!stats.isFile())
1762
- return res.error(404);
1757
+ if (err || !stats.isFile()) {
1758
+ if (res.statusCode < 400)
1759
+ return res.error(404);
1760
+ res.end(res.statusCode + ' ' + http.STATUS_CODES[res.statusCode]);
1761
+ return;
1762
+ }
1763
1763
  if (!res.getHeader('Content-Type')) {
1764
1764
  if (options.mimeType)
1765
1765
  res.setHeader('Content-Type', options.mimeType);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@radatek/microserver",
3
- "version": "3.0.1",
3
+ "version": "3.0.3",
4
4
  "description": "HTTP MicroServer",
5
5
  "author": "Darius Kisonas",
6
6
  "license": "MIT",