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 +2 -1
- package/package.json +13 -9
- package/src/request.js +8 -2
- package/src/response.js +44 -92
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
|
-
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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.
|
|
82
|
-
"express-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
145
|
-
|
|
148
|
+
|
|
149
|
+
if (this.chunkedTransfer) {
|
|
146
150
|
this._res.write(chunk);
|
|
147
|
-
|
|
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
|
-
|
|
156
|
+
if (done) {
|
|
157
|
+
super.end();
|
|
154
158
|
this.finished = true;
|
|
155
|
-
|
|
156
|
-
|
|
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
|
-
|
|
159
|
-
|
|
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
|
-
|
|
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
|
+
}
|