ultimate-express 1.4.4 → 1.4.5

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 CHANGED
@@ -308,7 +308,7 @@ Almost all middlewares that are compatible with Express are compatible with µEx
308
308
  - ✅ [body-parser](https://npmjs.com/package/body-parser) (use `express.text()` etc instead for better performance)
309
309
  - ✅ [cookie-parser](https://npmjs.com/package/cookie-parser)
310
310
  - ✅ [cookie-session](https://npmjs.com/package/cookie-session)
311
- - [compression](https://npmjs.com/package/compression)
311
+ - 🚧 [compression](https://npmjs.com/package/compression) - in some cases may send uncompressed files
312
312
  - ✅ [serve-static](https://npmjs.com/package/serve-static) (use `express.static()` instead for better performance)
313
313
  - ✅ [serve-index](https://npmjs.com/package/serve-index)
314
314
  - ✅ [cors](https://npmjs.com/package/cors)
@@ -322,6 +322,7 @@ Almost all middlewares that are compatible with Express are compatible with µEx
322
322
  - ✅ [express-subdomain](https://npmjs.com/package/express-subdomain)
323
323
  - ✅ [vhost](https://npmjs.com/package/vhost)
324
324
  - ✅ [tsoa](https://github.com/lukeautry/tsoa)
325
+ - ✅ [express-mongo-sanitize](https://www.npmjs.com/package/express-mongo-sanitize)
325
326
 
326
327
  Middlewares and modules that are confirmed to not work:
327
328
 
package/package.json CHANGED
@@ -1,11 +1,14 @@
1
1
  {
2
2
  "name": "ultimate-express",
3
- "version": "1.4.4",
3
+ "version": "1.4.5",
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": {
7
7
  "test": "node tests/index.js",
8
- "dev": "node --inspect=9229 demo/index.js"
8
+ "dev": "node --inspect=9229 demo/index.js",
9
+ "cover": "npm run cover:unit && npm run cover:report",
10
+ "cover:unit": "nyc --silent npm run test",
11
+ "cover:report": "nyc report --reporter=html"
9
12
  },
10
13
  "engines": {
11
14
  "node": ">=18"
@@ -42,7 +45,7 @@
42
45
  "dependencies": {
43
46
  "@types/express": "^4.0.0",
44
47
  "accepts": "^1.3.8",
45
- "acorn": "^8.14.0",
48
+ "acorn": "^8.14.1",
46
49
  "bytes": "^3.1.2",
47
50
  "cookie": "^1.0.2",
48
51
  "cookie-signature": "^1.2.2",
@@ -54,7 +57,7 @@
54
57
  "mime-types": "^2.1.35",
55
58
  "ms": "^2.1.3",
56
59
  "proxy-addr": "^2.0.7",
57
- "qs": "^6.13.1",
60
+ "qs": "^6.14.0",
58
61
  "range-parser": "^1.2.1",
59
62
  "statuses": "^2.0.1",
60
63
  "tseep": "^1.3.1",
@@ -65,27 +68,28 @@
65
68
  "devDependencies": {
66
69
  "@codechecks/client": "^0.1.12",
67
70
  "body-parser": "^1.20.3",
68
- "compression": "^1.7.5",
71
+ "compression": "^1.8.0",
69
72
  "cookie-parser": "^1.4.6",
70
73
  "cookie-session": "^2.1.0",
71
74
  "cors": "^2.8.5",
72
75
  "ejs": "^3.1.10",
73
76
  "errorhandler": "^1.5.1",
74
77
  "exit-hook": "^2.2.1",
75
- "express-4": "npm:express@4",
76
- "express-5": "npm:express@5",
78
+ "express": "latest-4",
77
79
  "express-art-template": "^1.0.1",
78
80
  "express-async-errors": "^3.1.1",
79
81
  "express-dot-engine": "^1.0.8",
80
82
  "express-fileupload": "^1.5.1",
81
- "express-handlebars": "^8.0.1",
82
- "express-rate-limit": "^7.4.1",
83
+ "express-handlebars": "^8.0.2",
84
+ "express-mongo-sanitize": "^2.2.0",
85
+ "express-rate-limit": "^7.5.0",
83
86
  "express-session": "^1.18.0",
84
87
  "express-subdomain": "^1.0.6",
85
88
  "formdata-node": "^6.0.3",
86
89
  "method-override": "^3.0.0",
87
90
  "multer": "^1.4.5-lts.1",
88
91
  "mustache-express": "^1.3.2",
92
+ "nyc": "^17.1.0",
89
93
  "pako": "^2.1.0",
90
94
  "pkg-pr-new": "^0.0.29",
91
95
  "pug": "^3.0.3",
package/src/request.js CHANGED
@@ -201,6 +201,9 @@ module.exports = class Request extends Readable {
201
201
  return index !== -1 ? header.slice(0, index).trim() : header.trim();
202
202
  }
203
203
 
204
+ set query(query) {
205
+ return this.#cachedQuery = query;
206
+ }
204
207
  get query() {
205
208
  if(this.#cachedQuery) {
206
209
  return this.#cachedQuery;
@@ -251,7 +254,7 @@ module.exports = class Request extends Readable {
251
254
  let ip = '';
252
255
  if(this.rawIp.byteLength === 4) {
253
256
  // ipv4
254
- ip = this.rawIp.join('.');
257
+ ip = new Uint8Array(this.rawIp).join('.');
255
258
  } else if(this.rawIp.byteLength === 16) {
256
259
  // ipv6
257
260
  const dv = new DataView(this.rawIp);
@@ -262,7 +265,7 @@ module.exports = class Request extends Readable {
262
265
  }
263
266
  }
264
267
  } else {
265
- this.#cachedParsedIp = undefined; // unix sockets dont have ip
268
+ ip = undefined; // unix sockets dont have ip
266
269
  }
267
270
  this.#cachedParsedIp = ip;
268
271
  return ip;
@@ -348,6 +351,9 @@ module.exports = class Request extends Readable {
348
351
  return parseRange(size, range, options);
349
352
  }
350
353
 
354
+ set headers(headers) {
355
+ this.#cachedHeaders = headers;
356
+ }
351
357
  get headers() {
352
358
  // https://nodejs.org/api/http.html#messageheaders
353
359
  if(this.#cachedHeaders) {
package/src/response.js CHANGED
@@ -81,6 +81,7 @@ module.exports = class Response extends Writable {
81
81
  this.statusText = undefined;
82
82
  this.chunkedTransfer = true;
83
83
  this.totalSize = 0;
84
+ this.writingChunk = false;
84
85
  this.headers = {
85
86
  'connection': 'keep-alive',
86
87
  'keep-alive': 'timeout=10'
@@ -121,62 +122,62 @@ module.exports = class Response extends Writable {
121
122
  }
122
123
 
123
124
  _write(chunk, encoding, callback) {
124
- if(this.aborted) {
125
+ if (this.aborted) {
125
126
  const err = new Error('Request aborted');
126
127
  err.code = 'ECONNABORTED';
127
128
  return this.destroy(err);
128
129
  }
129
- if(this.finished) {
130
+ if (this.finished) {
130
131
  const err = new Error('Response already finished');
131
132
  return this.destroy(err);
132
133
  }
134
+
135
+ this.writingChunk = true;
133
136
  this._res.cork(() => {
134
- if(!this.headersSent) {
137
+ if (!this.headersSent) {
135
138
  this.writeHead(this.statusCode);
136
139
  const statusMessage = this.statusText ?? statuses.message[this.statusCode] ?? '';
137
140
  this._res.writeStatus(`${this.statusCode} ${statusMessage}`.trim());
138
141
  this.writeHeaders(typeof chunk === 'string');
139
142
  }
140
- if(!Buffer.isBuffer(chunk) && !(chunk instanceof ArrayBuffer)) {
143
+
144
+ if (!Buffer.isBuffer(chunk) && !(chunk instanceof ArrayBuffer)) {
141
145
  chunk = Buffer.from(chunk);
142
146
  chunk = chunk.buffer.slice(chunk.byteOffset, chunk.byteOffset + chunk.byteLength);
143
147
  }
144
- if(this.chunkedTransfer) {
145
- // chunked transfer encoding
148
+
149
+ if (this.chunkedTransfer) {
146
150
  this._res.write(chunk);
147
- callback();
151
+ this.writingChunk = false;
152
+ callback(null);
148
153
  } else {
149
- // fixed size transfer encoding
150
154
  const lastOffset = this._res.getWriteOffset();
151
155
  const [ok, done] = this._res.tryEnd(chunk, this.totalSize);
152
- if(done) {
153
- this.destroy();
156
+ if (done) {
157
+ super.end();
154
158
  this.finished = true;
155
- if(this.socketExists) this.socket.emit('close');
156
- callback();
159
+ this.writingChunk = false;
160
+ if (this.socketExists) this.socket.emit('close');
161
+ callback(null);
162
+ } else if (!ok) {
163
+ this._res.ab = chunk;
164
+ this._res.abOffset = lastOffset;
165
+ this._res.onWritable((offset) => {
166
+ if (this.finished) return true;
167
+ const [ok, done] = this._res.tryEnd(this._res.ab.slice(offset - this._res.abOffset), this.totalSize);
168
+ if (done) {
169
+ this.finished = true;
170
+ if (this.socketExists) this.socket.emit('close');
171
+ }
172
+ if (ok) {
173
+ this.writingChunk = false;
174
+ callback(null);
175
+ }
176
+ return ok;
177
+ });
157
178
  } else {
158
- // still writing
159
- if(!ok) {
160
- // wait until uWS is ready to accept more data
161
- this._res.onWritable((offset) => {
162
- if(this.finished) {
163
- return true;
164
- }
165
- const [ok, done] = this._res.tryEnd(chunk.slice(offset - lastOffset), this.totalSize);
166
- if(done) {
167
- this.destroy();
168
- this.finished = true;
169
- if(this.socketExists) this.socket.emit('close');
170
- callback();
171
- } else if(ok) {
172
- callback();
173
- }
174
-
175
- return ok;
176
- });
177
- } else {
178
- callback();
179
- }
179
+ this.writingChunk = false;
180
+ callback(null);
180
181
  }
181
182
  }
182
183
  });
@@ -229,6 +230,12 @@ module.exports = class Response extends Writable {
229
230
  return this.status(code).send(statuses.message[+code] ?? code.toString());
230
231
  }
231
232
  end(data) {
233
+ if(this.writingChunk) {
234
+ this.once('drain', () => {
235
+ this.end(data);
236
+ });
237
+ return;
238
+ }
232
239
  if(this.finished) {
233
240
  return;
234
241
  }
@@ -498,7 +505,8 @@ module.exports = class Response extends Writable {
498
505
  opts.end = Math.max(offset, offset + len - 1);
499
506
  }
500
507
  const file = fs.createReadStream(fullpath, opts);
501
- pipeStreamOverResponse(this, file, len, callback);
508
+ this.set('Content-Length', len);
509
+ file.pipe(this);
502
510
  }
503
511
  }
504
512
  download(path, filename, options, callback) {
@@ -739,60 +747,4 @@ module.exports = class Response extends Writable {
739
747
  get writableFinished() {
740
748
  return this.finished;
741
749
  }
742
- }
743
-
744
- function pipeStreamOverResponse(res, readStream, totalSize, callback) {
745
- readStream.on('data', (chunk) => {
746
- if(res.finished) {
747
- return readStream.destroy();
748
- }
749
- res._res.cork(() => {
750
- if(!res.headersSent) {
751
- res.writeHead(res.statusCode);
752
- const statusMessage = res.statusText ?? statuses.message[res.statusCode] ?? '';
753
- res._res.writeStatus(`${res.statusCode} ${statusMessage}`.trim());
754
- res.writeHeaders(true);
755
- }
756
- const ab = chunk.buffer.slice(chunk.byteOffset, chunk.byteOffset + chunk.byteLength);
757
-
758
- const lastOffset = res._res.getWriteOffset();
759
- const [ok, done] = res._res.tryEnd(ab, totalSize);
760
-
761
- if (done) {
762
- readStream.destroy();
763
- res.finished = true;
764
- if(res.socketExists) res.socket.emit('close');
765
- if(callback) callback();
766
- } else if (!ok) {
767
- readStream.pause();
768
-
769
- res._res.ab = ab;
770
- res._res.abOffset = lastOffset;
771
-
772
- res._res.onWritable((offset) => {
773
- if(res.finished) {
774
- return true;
775
- }
776
- const [ok, done] = res._res.tryEnd(res._res.ab.slice(offset - res._res.abOffset), totalSize);
777
- if (done) {
778
- readStream.destroy();
779
- res.finished = true;
780
- if(res.socketExists) res.socket.emit('close');
781
- if(callback) callback();
782
- } else if (ok) {
783
- readStream.resume();
784
- }
785
-
786
- return ok;
787
- });
788
- }
789
- });
790
- }).on('error', e => {
791
- if(callback) callback(e);
792
- if(!res.finished) {
793
- res._res.close();
794
- res.finished = true;
795
- if(res.socketExists) res.socket.emit('error', e);
796
- }
797
- });
798
- }
750
+ }