ultimate-express 1.2.15 → 1.2.17
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 +8 -8
- package/package.json +2 -1
- package/src/middlewares.js +20 -6
- package/src/request.js +3 -3
- package/src/response.js +31 -19
package/README.md
CHANGED
|
@@ -28,13 +28,13 @@ Tested using [wrk](https://github.com/wg/wrk) (`-d 60 -t 1 -c 200`). Etag was di
|
|
|
28
28
|
|
|
29
29
|
| Test | Express req/sec | µExpress req/sec | Express throughput | µExpress throughput | µExpress speedup |
|
|
30
30
|
| --------------------------------------------- | --------------- | ---------------- | ------------------ | ------------------- | ---------------- |
|
|
31
|
-
| routing/simple-routes (/) |
|
|
32
|
-
| routing/lot-of-routes (/999) | 4.
|
|
33
|
-
| routing/some-middlewares (/90) | 10.
|
|
34
|
-
| routers/nested-routers (/abccc/nested/ddd) | 10.
|
|
35
|
-
| middlewares/express-static (/static/index.js) |
|
|
36
|
-
| engines/ejs (/test) | 5.
|
|
37
|
-
| middlewares/body-urlencoded (/abc) | 8.
|
|
31
|
+
| routing/simple-routes (/) | 11.16k | 75.14k | 2.08 MB/sec | 14.46 MB/sec | **6.73X** |
|
|
32
|
+
| routing/lot-of-routes (/999) | 4.63k | 54.57k | 0.84 MB/sec | 10.03 MB/sec | **11.78X** |
|
|
33
|
+
| routing/some-middlewares (/90) | 10.12k | 61.92k | 1.79 MB/sec | 11.32 MB/sec | **6.12X** |
|
|
34
|
+
| routers/nested-routers (/abccc/nested/ddd) | 10.18k | 51.15k | 1.82 MB/sec | 9.40 MB/sec | **5.02X** |
|
|
35
|
+
| middlewares/express-static (/static/index.js) | 6.58k | 32.45k | 10.15 MB/sec | 49.43 MB/sec | **4.87X** |
|
|
36
|
+
| engines/ejs (/test) | 5.50k | 40.82k | 2.45 MB/sec | 18.38 MB/sec | **7.42X** |
|
|
37
|
+
| middlewares/body-urlencoded (/abc) | 8.07k | 50.52k | 1.68 MB/sec | 10.78 MB/sec | **6.26X** |
|
|
38
38
|
|
|
39
39
|
### Performance against other frameworks
|
|
40
40
|
|
|
@@ -282,7 +282,7 @@ In general, basically all features and options are supported. Use [Express 4.x d
|
|
|
282
282
|
|
|
283
283
|
## Tested middlewares
|
|
284
284
|
|
|
285
|
-
|
|
285
|
+
Almost all middlewares that are compatible with Express are compatible with µExpress. Here's list of middlewares that we test for compatibility:
|
|
286
286
|
|
|
287
287
|
- ✅ [body-parser](https://npmjs.com/package/body-parser) (use `express.text()` etc instead for better performance)
|
|
288
288
|
- ✅ [cookie-parser](https://npmjs.com/package/cookie-parser)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ultimate-express",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.17",
|
|
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": {
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
"homepage": "https://github.com/dimdenGD/ultimate-express#readme",
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"accepts": "^1.3.8",
|
|
42
|
+
"bytes": "^3.1.2",
|
|
42
43
|
"cookie": "^0.6.0",
|
|
43
44
|
"cookie-signature": "^1.2.1",
|
|
44
45
|
"encodeurl": "^2.0.0",
|
package/src/middlewares.js
CHANGED
|
@@ -155,13 +155,15 @@ function createBodyParser(defaultType, beforeReturn) {
|
|
|
155
155
|
return (req, res, next) => {
|
|
156
156
|
const type = req.headers['content-type'];
|
|
157
157
|
|
|
158
|
-
// skip reading body
|
|
159
|
-
if(
|
|
158
|
+
// skip reading body twice
|
|
159
|
+
if(req.bodyRead) {
|
|
160
160
|
return next();
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
-
|
|
164
|
-
|
|
163
|
+
req.body = {};
|
|
164
|
+
|
|
165
|
+
// skip reading body for non-json content type
|
|
166
|
+
if(!type) {
|
|
165
167
|
return next();
|
|
166
168
|
}
|
|
167
169
|
|
|
@@ -216,6 +218,8 @@ function createBodyParser(defaultType, beforeReturn) {
|
|
|
216
218
|
}
|
|
217
219
|
}
|
|
218
220
|
|
|
221
|
+
req.bodyRead = true;
|
|
222
|
+
|
|
219
223
|
function onData(buf) {
|
|
220
224
|
if(!Buffer.isBuffer(buf)) {
|
|
221
225
|
buf = Buffer.from(buf);
|
|
@@ -266,7 +270,12 @@ const json = createBodyParser('application/json', function(req, res, next, optio
|
|
|
266
270
|
return next(new Error('Invalid body'));
|
|
267
271
|
}
|
|
268
272
|
}
|
|
269
|
-
|
|
273
|
+
try {
|
|
274
|
+
req.body = JSON.parse(buf.toString(), options.reviver);
|
|
275
|
+
} catch(e) {
|
|
276
|
+
return next(e);
|
|
277
|
+
}
|
|
278
|
+
|
|
270
279
|
next();
|
|
271
280
|
});
|
|
272
281
|
|
|
@@ -290,7 +299,12 @@ const text = createBodyParser('text/plain', function(req, res, next, options, bu
|
|
|
290
299
|
if(encoding !== 'utf-8' && encoding !== 'utf-16le' && encoding !== 'latin1') {
|
|
291
300
|
return next(new Error('Unsupported charset'));
|
|
292
301
|
}
|
|
293
|
-
|
|
302
|
+
try {
|
|
303
|
+
req.body = buf.toString(encoding);
|
|
304
|
+
} catch(e) {
|
|
305
|
+
return next(e);
|
|
306
|
+
}
|
|
307
|
+
|
|
294
308
|
next();
|
|
295
309
|
});
|
|
296
310
|
|
package/src/request.js
CHANGED
|
@@ -63,13 +63,12 @@ module.exports = class Request extends Readable {
|
|
|
63
63
|
if(this.endsWithSlash && this.path !== '/' && !this.app.get('strict routing')) {
|
|
64
64
|
this._opPath = this._opPath.slice(0, -1);
|
|
65
65
|
}
|
|
66
|
-
this.method = req.
|
|
66
|
+
this.method = req.getCaseSensitiveMethod().toUpperCase();
|
|
67
67
|
this.params = {};
|
|
68
68
|
|
|
69
69
|
this._gotParams = new Set();
|
|
70
70
|
this._stack = [];
|
|
71
71
|
this._paramStack = [];
|
|
72
|
-
this.bufferedData = Buffer.allocUnsafe(0);
|
|
73
72
|
this.receivedData = false;
|
|
74
73
|
// reading ip is very slow in UWS, so its better to not do it unless truly needed
|
|
75
74
|
if(this.app.needsIpAfterResponse || this.key < 100) {
|
|
@@ -87,6 +86,7 @@ module.exports = class Request extends Readable {
|
|
|
87
86
|
this.method === 'PATCH' ||
|
|
88
87
|
(additionalMethods && additionalMethods.includes(this.method))
|
|
89
88
|
) {
|
|
89
|
+
this.bufferedData = Buffer.allocUnsafe(0);
|
|
90
90
|
this._res.onData((ab, isLast) => {
|
|
91
91
|
// make stream actually readable
|
|
92
92
|
this.receivedData = true;
|
|
@@ -105,7 +105,7 @@ module.exports = class Request extends Readable {
|
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
async _read() {
|
|
108
|
-
if(!this.receivedData) {
|
|
108
|
+
if(!this.receivedData || !this.bufferedData) {
|
|
109
109
|
return;
|
|
110
110
|
}
|
|
111
111
|
if(this.bufferedData.length > 0) {
|
package/src/response.js
CHANGED
|
@@ -43,30 +43,30 @@ class Socket extends EventEmitter {
|
|
|
43
43
|
constructor(response) {
|
|
44
44
|
super();
|
|
45
45
|
this.response = response;
|
|
46
|
-
this.writable = true;
|
|
47
46
|
|
|
48
47
|
this.on('error', (err) => {
|
|
49
48
|
this.emit('close');
|
|
50
49
|
});
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
}
|
|
51
|
+
get writable() {
|
|
52
|
+
return !this.response.finished;
|
|
54
53
|
}
|
|
55
54
|
}
|
|
56
55
|
|
|
57
56
|
module.exports = class Response extends Writable {
|
|
57
|
+
#socket = null;
|
|
58
58
|
constructor(res, req, app) {
|
|
59
59
|
super();
|
|
60
60
|
this._req = req;
|
|
61
61
|
this._res = res;
|
|
62
62
|
this.headersSent = false;
|
|
63
63
|
this.aborted = false;
|
|
64
|
-
this.socket = new Socket(this);
|
|
65
64
|
this.app = app;
|
|
66
65
|
this.locals = {};
|
|
67
66
|
this.aborted = false;
|
|
68
67
|
this.statusCode = 200;
|
|
69
68
|
this.chunkedTransfer = true;
|
|
69
|
+
this.finished = false;
|
|
70
70
|
this.totalSize = 0;
|
|
71
71
|
this.headers = {
|
|
72
72
|
'connection': 'keep-alive',
|
|
@@ -92,11 +92,20 @@ module.exports = class Response extends Writable {
|
|
|
92
92
|
}
|
|
93
93
|
this._res.cork(() => {
|
|
94
94
|
this._res.close();
|
|
95
|
-
this.
|
|
95
|
+
this.finished = true;
|
|
96
|
+
if(this.socketExists) this.socket.emit('close');
|
|
96
97
|
});
|
|
97
98
|
});
|
|
98
|
-
this.emit('socket', this.socket);
|
|
99
99
|
}
|
|
100
|
+
|
|
101
|
+
get socket() {
|
|
102
|
+
this.socketExists = true;
|
|
103
|
+
if(!this.#socket) {
|
|
104
|
+
this.#socket = new Socket(this);
|
|
105
|
+
}
|
|
106
|
+
return this.#socket;
|
|
107
|
+
}
|
|
108
|
+
|
|
100
109
|
_write(chunk, encoding, callback) {
|
|
101
110
|
if(this.aborted) {
|
|
102
111
|
const err = new Error('Request aborted');
|
|
@@ -135,7 +144,8 @@ module.exports = class Response extends Writable {
|
|
|
135
144
|
const [ok, done] = this._res.tryEnd(chunk, this.totalSize);
|
|
136
145
|
if(done) {
|
|
137
146
|
this.destroy();
|
|
138
|
-
this.
|
|
147
|
+
this.finished = true;
|
|
148
|
+
if(this.socketExists) this.socket.emit('close');
|
|
139
149
|
callback();
|
|
140
150
|
} else {
|
|
141
151
|
// still writing
|
|
@@ -148,7 +158,8 @@ module.exports = class Response extends Writable {
|
|
|
148
158
|
const [ok, done] = this._res.tryEnd(chunk.slice(offset - lastOffset), this.totalSize);
|
|
149
159
|
if(done) {
|
|
150
160
|
this.destroy();
|
|
151
|
-
this.
|
|
161
|
+
this.finished = true;
|
|
162
|
+
if(this.socketExists) this.socket.emit('close');
|
|
152
163
|
callback();
|
|
153
164
|
} else if(ok) {
|
|
154
165
|
callback();
|
|
@@ -213,7 +224,8 @@ module.exports = class Response extends Writable {
|
|
|
213
224
|
this.headersSent = true;
|
|
214
225
|
if(fresh) {
|
|
215
226
|
this._res.end();
|
|
216
|
-
this.
|
|
227
|
+
this.finished = true;
|
|
228
|
+
if(this.socketExists) this.socket.emit('close');
|
|
217
229
|
return;
|
|
218
230
|
}
|
|
219
231
|
}
|
|
@@ -230,7 +242,8 @@ module.exports = class Response extends Writable {
|
|
|
230
242
|
this._res.end(data);
|
|
231
243
|
}
|
|
232
244
|
}
|
|
233
|
-
this.
|
|
245
|
+
this.finished = true;
|
|
246
|
+
if(this.socketExists) this.socket.emit('close');
|
|
234
247
|
});
|
|
235
248
|
|
|
236
249
|
return this;
|
|
@@ -692,12 +705,8 @@ module.exports = class Response extends Writable {
|
|
|
692
705
|
return this;
|
|
693
706
|
}
|
|
694
707
|
|
|
695
|
-
get finished() {
|
|
696
|
-
return !this.socket.writable;
|
|
697
|
-
}
|
|
698
|
-
|
|
699
708
|
get writableFinished() {
|
|
700
|
-
return
|
|
709
|
+
return this.finished;
|
|
701
710
|
}
|
|
702
711
|
}
|
|
703
712
|
|
|
@@ -728,7 +737,8 @@ function pipeStreamOverResponse(res, readStream, totalSize, callback) {
|
|
|
728
737
|
|
|
729
738
|
if (done) {
|
|
730
739
|
readStream.destroy();
|
|
731
|
-
res.
|
|
740
|
+
res.finished = true;
|
|
741
|
+
if(res.socketExists) res.socket.emit('close');
|
|
732
742
|
if(callback) callback();
|
|
733
743
|
} else if (!ok) {
|
|
734
744
|
readStream.pause();
|
|
@@ -743,7 +753,8 @@ function pipeStreamOverResponse(res, readStream, totalSize, callback) {
|
|
|
743
753
|
const [ok, done] = res._res.tryEnd(res._res.ab.slice(offset - res._res.abOffset), totalSize);
|
|
744
754
|
if (done) {
|
|
745
755
|
readStream.destroy();
|
|
746
|
-
res.
|
|
756
|
+
res.finished = true;
|
|
757
|
+
if(res.socketExists) res.socket.emit('close');
|
|
747
758
|
if(callback) callback();
|
|
748
759
|
} else if (ok) {
|
|
749
760
|
readStream.resume();
|
|
@@ -757,7 +768,8 @@ function pipeStreamOverResponse(res, readStream, totalSize, callback) {
|
|
|
757
768
|
if(callback) callback(e);
|
|
758
769
|
if(!res.finished) {
|
|
759
770
|
res._res.close();
|
|
760
|
-
res.
|
|
771
|
+
res.finished = true;
|
|
772
|
+
if(res.socketExists) res.socket.emit('error', e);
|
|
761
773
|
}
|
|
762
774
|
});
|
|
763
775
|
}
|