ultimate-express 1.2.10 → 1.2.12

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ultimate-express",
3
- "version": "1.2.10",
3
+ "version": "1.2.12",
4
4
  "description": "The Ultimate Express. Fastest http server with full Express compatibility, based on uWebSockets.",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
package/src/request.js CHANGED
@@ -34,6 +34,7 @@ module.exports = class Request extends Readable {
34
34
  #cachedHeaders = null;
35
35
  #cachedDistinctHeaders = null;
36
36
  #rawHeadersEntries = [];
37
+ #cachedParsedIp = null;
37
38
  constructor(req, res, app) {
38
39
  super();
39
40
  this._res = res;
@@ -58,13 +59,13 @@ module.exports = class Request extends Readable {
58
59
  }
59
60
  this.method = req.getMethod().toUpperCase();
60
61
  this.params = {};
61
- this.rawIp = Buffer.from(this._res.getRemoteAddressAsText()).toString();
62
62
 
63
63
  this._gotParams = new Set();
64
64
  this._stack = [];
65
65
  this._paramStack = [];
66
66
  this.bufferedData = Buffer.allocUnsafe(0);
67
67
  this.receivedData = false;
68
+ this.rawIp = this._res.getRemoteAddress();
68
69
 
69
70
  const additionalMethods = this.app.get('body methods');
70
71
  // skip reading body for non-POST requests
@@ -156,7 +157,7 @@ module.exports = class Request extends Readable {
156
157
  get ip() {
157
158
  const trust = this.app.get('trust proxy fn');
158
159
  if(!trust) {
159
- return this.rawIp;
160
+ return this.parsedIp;
160
161
  }
161
162
  return proxyaddr(this, trust);
162
163
  }
@@ -217,9 +218,31 @@ module.exports = class Request extends Readable {
217
218
  return this.headers['x-requested-with'] === 'XMLHttpRequest';
218
219
  }
219
220
 
221
+ get parsedIp() {
222
+ if(this.#cachedParsedIp) {
223
+ return this.#cachedParsedIp;
224
+ }
225
+ let ip = '';
226
+ if(this.rawIp.byteLength === 4) {
227
+ // ipv4
228
+ ip = this.rawIp.join('.');
229
+ } else {
230
+ // ipv6
231
+ const dv = new DataView(this.rawIp);
232
+ for(let i = 0; i < 8; i++) {
233
+ ip += dv.getUint16(i * 2).toString(16).padStart(4, '0');
234
+ if(i < 7) {
235
+ ip += ':';
236
+ }
237
+ }
238
+ }
239
+ this.#cachedParsedIp = ip;
240
+ return ip;
241
+ }
242
+
220
243
  get connection() {
221
244
  return {
222
- remoteAddress: this.rawIp,
245
+ remoteAddress: this.parsedIp,
223
246
  localPort: this.app.port,
224
247
  remotePort: this.app.port,
225
248
  encrypted: this.app.ssl,
package/src/response.js CHANGED
@@ -28,7 +28,9 @@ const fs = require("fs");
28
28
  const Path = require("path");
29
29
  const statuses = require("statuses");
30
30
  const { sign } = require("cookie-signature");
31
- const { EventEmitter } = require("tseep");
31
+ // events is faster at init, tseep is faster at sending events
32
+ // since we create a ton of objects and dont send a ton of events, its better to use events here
33
+ const { EventEmitter } = require("events");
32
34
  const http = require("http");
33
35
  const ms = require('ms');
34
36
  const etag = require("etag");
@@ -67,8 +69,12 @@ module.exports = class Response extends Writable {
67
69
  this.chunkedTransfer = true;
68
70
  this.totalSize = 0;
69
71
  this.headers = {
72
+ 'connection': 'keep-alive',
70
73
  'keep-alive': 'timeout=10'
71
74
  };
75
+ if(this.app.get('x-powered-by')) {
76
+ this.headers['x-powered-by'] = 'UltimateExpress';
77
+ }
72
78
  // support for node internal
73
79
  this[kOutHeaders] = new Proxy(this.headers, {
74
80
  set: (obj, prop, value) => {
@@ -80,9 +86,6 @@ module.exports = class Response extends Writable {
80
86
  }
81
87
  });
82
88
  this.body = undefined;
83
- if(this.app.get('x-powered-by')) {
84
- this.set('x-powered-by', 'uExpress');
85
- }
86
89
  this.on('error', (err) => {
87
90
  if(this.aborted) {
88
91
  return;
@@ -194,7 +197,7 @@ module.exports = class Response extends Writable {
194
197
  if(!this.headersSent) {
195
198
  const etagFn = this.app.get('etag fn');
196
199
  if(data && !this.headers['etag'] && etagFn && !this.req.noEtag) {
197
- this.set('etag', etagFn(data));
200
+ this.headers['etag'] = etagFn(data);
198
201
  }
199
202
  const fresh = this.req.fresh;
200
203
  this._res.writeStatus(fresh ? '304' : this.statusCode.toString());
@@ -361,15 +364,15 @@ module.exports = class Response extends Writable {
361
364
  }
362
365
 
363
366
  // headers
364
- if(!this.get('Content-Type')) {
367
+ if(!this.headers['content-type']) {
365
368
  const m = mime.lookup(fullpath);
366
369
  if(m) this.type(m);
367
370
  }
368
371
  if(options.cacheControl) {
369
- this.set('Cache-Control', `public, max-age=${options.maxAge / 1000}` + (options.immutable ? ', immutable' : ''));
372
+ this.headers['cache-control'] = `public, max-age=${options.maxAge / 1000}` + (options.immutable ? ', immutable' : '');
370
373
  }
371
374
  if(options.lastModified) {
372
- this.set('Last-Modified', stat.mtime.toUTCString());
375
+ this.headers['last-modified'] = stat.mtime.toUTCString();
373
376
  }
374
377
  if(options.headers) {
375
378
  for(const header in options.headers) {
@@ -382,7 +385,7 @@ module.exports = class Response extends Writable {
382
385
 
383
386
  // etag
384
387
  if(options.etag && !this.headers['etag'] && etagFn) {
385
- this.set('etag', etagFn(stat));
388
+ this.headers['etag'] = etagFn(stat);
386
389
  }
387
390
  if(!options.etag) {
388
391
  this.req.noEtag = true;
@@ -397,7 +400,7 @@ module.exports = class Response extends Writable {
397
400
  // range requests
398
401
  let offset = 0, len = stat.size, ranged = false;
399
402
  if(options.acceptRanges) {
400
- this.set('accept-ranges', 'bytes');
403
+ this.headers['accept-ranges'] = 'bytes';
401
404
  if(this.req.headers.range) {
402
405
  let ranges = this.req.range(stat.size, {
403
406
  combine: true
@@ -410,12 +413,12 @@ module.exports = class Response extends Writable {
410
413
 
411
414
  if(ranges === -1) {
412
415
  this.status(416);
413
- this.set('Content-Range', `bytes */${stat.size}`);
416
+ this.headers['content-range'] = `bytes */${stat.size}`;
414
417
  return done(new Error('Range Not Satisfiable'));
415
418
  }
416
419
  if(ranges !== -2 && ranges.length === 1) {
417
420
  this.status(206);
418
- this.set('Content-Range', `bytes ${ranges[0].start}-${ranges[0].end}/${stat.size}`);
421
+ this.headers['content-range'] = `bytes ${ranges[0].start}-${ranges[0].end}/${stat.size}`;
419
422
  offset = ranges[0].start;
420
423
  len = ranges[0].end - ranges[0].start + 1;
421
424
  ranged = true;
@@ -501,7 +504,7 @@ module.exports = class Response extends Writable {
501
504
  if(field === 'set-cookie' && Array.isArray(value)) {
502
505
  value = value.join('; ');
503
506
  } else if(field === 'content-type') {
504
- if(value.startsWith('text/') || value === 'application/json' || value === 'application/javascript') {
507
+ if(!value.includes('charset=') && (value.startsWith('text/') || value === 'application/json' || value === 'application/javascript')) {
505
508
  value += '; charset=utf-8';
506
509
  }
507
510
  }
@@ -589,7 +592,7 @@ module.exports = class Response extends Writable {
589
592
  return this.cookie(name, '', opts);
590
593
  }
591
594
  attachment(filename) {
592
- this.set('Content-Disposition', `attachment; filename="${filename}"`);
595
+ this.headers['Content-Disposition'] = `attachment; filename="${filename}"`;
593
596
  this.type(filename.split('.').pop());
594
597
  return this;
595
598
  }
@@ -611,8 +614,8 @@ module.exports = class Response extends Writable {
611
614
  return this;
612
615
  }
613
616
  json(body) {
614
- if(!this.get('Content-Type')) {
615
- this.set('Content-Type', 'application/json; charset=utf-8');
617
+ if(!this.headers['content-type']) {
618
+ this.headers['content-type'] = 'application/json; charset=utf-8';
616
619
  }
617
620
  const escape = this.app.get('json escape');
618
621
  const replacer = this.app.get('json replacer');
@@ -623,9 +626,9 @@ module.exports = class Response extends Writable {
623
626
  let callback = this.req.query[this.app.get('jsonp callback name')];
624
627
  let body = stringify(object, this.app.get('json replacer'), this.app.get('json spaces'), this.app.get('json escape'));
625
628
 
626
- if(!this.get('Content-Type')) {
627
- this.set('Content-Type', 'application/javascript; charset=utf-8');
628
- this.set('X-Content-Type-Options', 'nosniff');
629
+ if(!this.headers['content-type']) {
630
+ this.headers['content-type'] = 'application/javascript; charset=utf-8';
631
+ this.headers['X-Content-Type-Options'] = 'nosniff';
629
632
  }
630
633
 
631
634
  if(Array.isArray(callback)) {
@@ -633,8 +636,6 @@ module.exports = class Response extends Writable {
633
636
  }
634
637
 
635
638
  if(typeof callback === 'string' && callback.length !== 0) {
636
- this.set('Content-Type', 'application/javascript; charset=utf-8');
637
- this.set('X-Content-Type-Options', 'nosniff');
638
639
  callback = callback.replace(/[^\[\]\w$.]/g, '');
639
640
 
640
641
  if(body === undefined) {
@@ -651,7 +652,7 @@ module.exports = class Response extends Writable {
651
652
  return this.send(body);
652
653
  }
653
654
  links(links) {
654
- this.set('Link', Object.entries(links).map(([rel, url]) => `<${url}>; rel="${rel}"`).join(', '));
655
+ this.headers['link'] = Object.entries(links).map(([rel, url]) => `<${url}>; rel="${rel}"`).join(', ');
655
656
  return this;
656
657
  }
657
658
  location(path) {
@@ -660,7 +661,7 @@ module.exports = class Response extends Writable {
660
661
  if(!path) path = this.req.get('Referer');
661
662
  if(!path) path = '/';
662
663
  }
663
- return this.set('Location', encodeUrl(path));
664
+ return this.headers['location'] = encodeUrl(path);
664
665
  }
665
666
  redirect(status, url) {
666
667
  if(typeof status !== 'number' && !url) {
@@ -669,7 +670,7 @@ module.exports = class Response extends Writable {
669
670
  }
670
671
  this.location(url);
671
672
  this.status(status);
672
- this.set('Content-Type', 'text/plain; charset=utf-8');
673
+ this.headers['content-type'] = 'text/plain; charset=utf-8';
673
674
  return this.send(`${statuses.message[status] ?? status}. Redirecting to ${url}`);
674
675
  }
675
676
 
@@ -680,7 +681,7 @@ module.exports = class Response extends Writable {
680
681
  if(ct.startsWith('text/') || ct === 'application/json' || ct === 'application/javascript') {
681
682
  ct += '; charset=UTF-8';
682
683
  }
683
- return this.set('Content-Type', ct);
684
+ return this.set('content-type', ct);
684
685
  }
685
686
  contentType(type) {
686
687
  return this.type(type);
package/src/router.js CHANGED
@@ -326,7 +326,6 @@ module.exports = class Router extends EventEmitter {
326
326
  }
327
327
 
328
328
  #preprocessRequest(req, res, route) {
329
- return new Promise(async resolve => {
330
329
  req.route = route;
331
330
  if(route.optimizedParams) {
332
331
  req.params = req.optimizedParams;
@@ -350,34 +349,37 @@ module.exports = class Router extends EventEmitter {
350
349
  }
351
350
  }
352
351
 
353
- for(let param in req.params) {
354
- if(this.#paramCallbacks.has(param) && !req._gotParams.has(param)) {
355
- req._gotParams.add(param);
356
- const pcs = this.#paramCallbacks.get(param);
357
- for(let i = 0; i < pcs.length; i++) {
358
- const fn = pcs[i];
359
- await new Promise(resolveRoute => {
360
- const next = (thingamabob) => {
361
- if(thingamabob) {
362
- if(thingamabob === 'route') {
363
- return resolve('route');
364
- } else {
365
- this.#handleError(thingamabob, req, res);
366
- return resolve(false);
367
- }
368
- }
369
- return resolveRoute();
370
- };
371
- req.next = next;
372
- fn(req, res, next, req.params[param], param);
373
- });
352
+ if(this.#paramCallbacks.size > 0) {
353
+ return new Promise(async resolve => {
354
+ for(let param in req.params) {
355
+ if(this.#paramCallbacks.has(param) && !req._gotParams.has(param)) {
356
+ req._gotParams.add(param);
357
+ const pcs = this.#paramCallbacks.get(param);
358
+ for(let i = 0; i < pcs.length; i++) {
359
+ const fn = pcs[i];
360
+ await new Promise(resolveRoute => {
361
+ const next = (thingamabob) => {
362
+ if(thingamabob) {
363
+ if(thingamabob === 'route') {
364
+ return resolve('route');
365
+ } else {
366
+ this.#handleError(thingamabob, req, res);
367
+ return resolve(false);
368
+ }
369
+ }
370
+ return resolveRoute();
371
+ };
372
+ req.next = next;
373
+ fn(req, res, next, req.params[param], param);
374
+ });
375
+ }
376
+ }
374
377
  }
375
- }
378
+ });
376
379
  }
377
-
378
- resolve(true);
379
- });
380
+ return true;
380
381
  }
382
+
381
383
  param(name, fn) {
382
384
  if(typeof name === 'function') {
383
385
  deprecated('app.param(callback)', 'app.param(name, callback)', true);
@@ -413,7 +415,7 @@ module.exports = class Router extends EventEmitter {
413
415
  return resolve(this._routeRequest(req, res, 0, this._routes, false, skipUntil));
414
416
  }
415
417
  let callbackindex = 0;
416
- const continueRoute = await this.#preprocessRequest(req, res, route);
418
+ let continueRoute = await this.#preprocessRequest(req, res, route);
417
419
  if(route.use) {
418
420
  req._stack.push(route.path);
419
421
  req._opPath =