ultimate-express 1.3.17 → 1.3.19
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 +3 -2
- package/src/middlewares.js +6 -5
- package/src/request.js +10 -9
- package/src/response.js +14 -5
- package/src/router.js +9 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ultimate-express",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.19",
|
|
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": {
|
|
@@ -72,7 +72,8 @@
|
|
|
72
72
|
"ejs": "^3.1.10",
|
|
73
73
|
"errorhandler": "^1.5.1",
|
|
74
74
|
"exit-hook": "^2.2.1",
|
|
75
|
-
"express": "
|
|
75
|
+
"express-4": "npm:express@4",
|
|
76
|
+
"express-5": "npm:express@5",
|
|
76
77
|
"express-art-template": "^1.0.1",
|
|
77
78
|
"express-async-errors": "^3.1.1",
|
|
78
79
|
"express-dot-engine": "^1.0.8",
|
package/src/middlewares.js
CHANGED
|
@@ -180,11 +180,6 @@ function createBodyParser(defaultType, beforeReturn) {
|
|
|
180
180
|
return next();
|
|
181
181
|
}
|
|
182
182
|
|
|
183
|
-
// skip reading too large body
|
|
184
|
-
if(length && +length > options.limit) {
|
|
185
|
-
return next(new Error('Request entity too large'));
|
|
186
|
-
}
|
|
187
|
-
|
|
188
183
|
if(options.simpleType) {
|
|
189
184
|
const semicolonIndex = type.indexOf(';');
|
|
190
185
|
const clearType = semicolonIndex !== -1 ? type.substring(0, semicolonIndex) : type;
|
|
@@ -203,6 +198,12 @@ function createBodyParser(defaultType, beforeReturn) {
|
|
|
203
198
|
}
|
|
204
199
|
}
|
|
205
200
|
|
|
201
|
+
// skip reading too large body
|
|
202
|
+
if(length && +length > options.limit) {
|
|
203
|
+
return next(new Error('Request entity too large'));
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
|
|
206
207
|
// skip reading body for non-POST requests
|
|
207
208
|
// this makes it +10k req/sec faster
|
|
208
209
|
if( additionalMethods === undefined ) additionalMethods = req.app.get('body methods') ?? null;
|
package/src/request.js
CHANGED
|
@@ -72,6 +72,7 @@ module.exports = class Request extends Readable {
|
|
|
72
72
|
this._stack = [];
|
|
73
73
|
this._paramStack = [];
|
|
74
74
|
this.receivedData = false;
|
|
75
|
+
this.doneReadingData = false;
|
|
75
76
|
// reading ip is very slow in UWS, so its better to not do it unless truly needed
|
|
76
77
|
if(this.app.needsIpAfterResponse || this.key < 100) {
|
|
77
78
|
// if app needs ip after response, read it now because after response its not accessible
|
|
@@ -92,30 +93,30 @@ module.exports = class Request extends Readable {
|
|
|
92
93
|
this._res.onData((ab, isLast) => {
|
|
93
94
|
// make stream actually readable
|
|
94
95
|
this.receivedData = true;
|
|
96
|
+
if(isLast) {
|
|
97
|
+
this.doneReadingData = true;
|
|
98
|
+
}
|
|
95
99
|
// instead of pushing data immediately, buffer it
|
|
96
100
|
// because writable streams cant handle the amount of data uWS gives (usually 512kb+)
|
|
97
101
|
const chunk = Buffer.from(ab);
|
|
98
102
|
this.bufferedData = Buffer.concat([this.bufferedData, chunk]);
|
|
99
|
-
|
|
100
|
-
// once its done start pushing data
|
|
101
|
-
this._read();
|
|
102
|
-
}
|
|
103
|
+
this._read();
|
|
103
104
|
});
|
|
104
105
|
} else {
|
|
105
106
|
this.receivedData = true;
|
|
106
107
|
}
|
|
107
108
|
}
|
|
108
109
|
|
|
109
|
-
|
|
110
|
+
_read() {
|
|
110
111
|
if(!this.receivedData || !this.bufferedData) {
|
|
111
112
|
return;
|
|
112
113
|
}
|
|
113
114
|
if(this.bufferedData.length > 0) {
|
|
114
|
-
// push
|
|
115
|
-
const chunk = this.bufferedData.subarray(0, 1024 *
|
|
116
|
-
this.bufferedData = this.bufferedData.subarray(1024 *
|
|
115
|
+
// push 128kb chunks
|
|
116
|
+
const chunk = this.bufferedData.subarray(0, 1024 * 128);
|
|
117
|
+
this.bufferedData = this.bufferedData.subarray(1024 * 128);
|
|
117
118
|
this.push(chunk);
|
|
118
|
-
} else {
|
|
119
|
+
} else if(this.doneReadingData) {
|
|
119
120
|
this.push(null);
|
|
120
121
|
}
|
|
121
122
|
}
|
package/src/response.js
CHANGED
|
@@ -78,6 +78,7 @@ module.exports = class Response extends Writable {
|
|
|
78
78
|
this.finished = false;
|
|
79
79
|
this.aborted = false;
|
|
80
80
|
this.statusCode = 200;
|
|
81
|
+
this.statusText = undefined;
|
|
81
82
|
this.chunkedTransfer = true;
|
|
82
83
|
this.totalSize = 0;
|
|
83
84
|
this.headers = {
|
|
@@ -132,7 +133,8 @@ module.exports = class Response extends Writable {
|
|
|
132
133
|
this._res.cork(() => {
|
|
133
134
|
if(!this.headersSent) {
|
|
134
135
|
this.writeHead(this.statusCode);
|
|
135
|
-
this.
|
|
136
|
+
const statusMessage = this.statusText ?? statuses.message[this.statusCode] ?? '';
|
|
137
|
+
this._res.writeStatus(`${this.statusCode} ${statusMessage}`.trim());
|
|
136
138
|
this.writeHeaders(typeof chunk === 'string');
|
|
137
139
|
}
|
|
138
140
|
if(!Buffer.isBuffer(chunk) && !(chunk instanceof ArrayBuffer)) {
|
|
@@ -181,6 +183,9 @@ module.exports = class Response extends Writable {
|
|
|
181
183
|
}
|
|
182
184
|
writeHead(statusCode, statusMessage, headers) {
|
|
183
185
|
this.statusCode = statusCode;
|
|
186
|
+
if(typeof statusMessage === 'string') {
|
|
187
|
+
this.statusText = statusMessage;
|
|
188
|
+
}
|
|
184
189
|
if(!headers) {
|
|
185
190
|
if(!statusMessage) return;
|
|
186
191
|
headers = statusMessage;
|
|
@@ -235,7 +240,8 @@ module.exports = class Response extends Writable {
|
|
|
235
240
|
this.headers['etag'] = etagFn(data);
|
|
236
241
|
}
|
|
237
242
|
const fresh = this.req.fresh;
|
|
238
|
-
this.
|
|
243
|
+
const statusMessage = this.statusText ?? statuses.message[this.statusCode] ?? '';
|
|
244
|
+
this._res.writeStatus(fresh ? '304 Not Modified' : `${this.statusCode} ${statusMessage}`.trim());
|
|
239
245
|
this.writeHeaders(true);
|
|
240
246
|
if(fresh) {
|
|
241
247
|
this._res.end();
|
|
@@ -556,7 +562,9 @@ module.exports = class Response extends Writable {
|
|
|
556
562
|
get(field) {
|
|
557
563
|
return this.headers[field.toLowerCase()];
|
|
558
564
|
}
|
|
559
|
-
getHeader
|
|
565
|
+
getHeader(field) {
|
|
566
|
+
return this.get(field);
|
|
567
|
+
}
|
|
560
568
|
removeHeader(field) {
|
|
561
569
|
delete this.headers[field.toLowerCase()];
|
|
562
570
|
return this;
|
|
@@ -568,7 +576,7 @@ module.exports = class Response extends Writable {
|
|
|
568
576
|
const newVal = [];
|
|
569
577
|
if(Array.isArray(old)) {
|
|
570
578
|
newVal.push(...old);
|
|
571
|
-
} else
|
|
579
|
+
} else {
|
|
572
580
|
newVal.push(old);
|
|
573
581
|
}
|
|
574
582
|
if(Array.isArray(value)) {
|
|
@@ -740,7 +748,8 @@ function pipeStreamOverResponse(res, readStream, totalSize, callback) {
|
|
|
740
748
|
res._res.cork(() => {
|
|
741
749
|
if(!res.headersSent) {
|
|
742
750
|
res.writeHead(res.statusCode);
|
|
743
|
-
res.
|
|
751
|
+
const statusMessage = res.statusText ?? statuses.message[res.statusCode] ?? '';
|
|
752
|
+
res._res.writeStatus(`${res.statusCode} ${statusMessage}`.trim());
|
|
744
753
|
res.writeHeaders(true);
|
|
745
754
|
}
|
|
746
755
|
const ab = chunk.buffer.slice(chunk.byteOffset, chunk.byteOffset + chunk.byteLength);
|
package/src/router.js
CHANGED
|
@@ -19,6 +19,7 @@ const Response = require("./response.js");
|
|
|
19
19
|
const Request = require("./request.js");
|
|
20
20
|
const { EventEmitter } = require("tseep");
|
|
21
21
|
const compileDeclarative = require("./declarative.js");
|
|
22
|
+
const statuses = require("statuses");
|
|
22
23
|
|
|
23
24
|
let resCodes = {}, resDecMethods = ['set', 'setHeader', 'header', 'send', 'end', 'append', 'status'];
|
|
24
25
|
for(let method of resDecMethods) {
|
|
@@ -108,6 +109,10 @@ module.exports = class Router extends EventEmitter {
|
|
|
108
109
|
let path = req._opPath;
|
|
109
110
|
let pattern = route.pattern;
|
|
110
111
|
|
|
112
|
+
if(req.endsWithSlash && path.endsWith('/') && !this.get('strict routing')) {
|
|
113
|
+
path = path.slice(0, -1);
|
|
114
|
+
}
|
|
115
|
+
|
|
111
116
|
if (typeof pattern === 'string') {
|
|
112
117
|
if(pattern === '/*') {
|
|
113
118
|
return true;
|
|
@@ -351,6 +356,9 @@ module.exports = class Router extends EventEmitter {
|
|
|
351
356
|
}
|
|
352
357
|
|
|
353
358
|
_extractParams(pattern, path) {
|
|
359
|
+
if(path.endsWith('/')) {
|
|
360
|
+
path = path.slice(0, -1);
|
|
361
|
+
}
|
|
354
362
|
let match = pattern.exec(path);
|
|
355
363
|
const obj = match?.groups ?? new NullObject();
|
|
356
364
|
for(let i = 1; i < match.length; i++) {
|
|
@@ -598,7 +606,7 @@ module.exports = class Router extends EventEmitter {
|
|
|
598
606
|
|
|
599
607
|
_sendErrorPage(request, response, err, checkEnv = false) {
|
|
600
608
|
if(checkEnv && this.get('env') === 'production') {
|
|
601
|
-
err = 'Internal Server Error';
|
|
609
|
+
err = response.statusCode >= 400 ? (statuses.message[response.statusCode] ?? 'Internal Server Error') : 'Internal Server Error';
|
|
602
610
|
}
|
|
603
611
|
request.noEtag = true;
|
|
604
612
|
response.setHeader('Content-Type', 'text/html; charset=utf-8');
|