ultimate-express 1.1.0 → 1.1.2
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 +4 -1
- package/src/application.js +11 -7
- package/src/response.js +31 -18
package/package.json
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ultimate-express",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
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
8
|
},
|
|
9
|
+
"engines": {
|
|
10
|
+
"node": ">=16"
|
|
11
|
+
},
|
|
9
12
|
"files": [
|
|
10
13
|
"src",
|
|
11
14
|
"EXPRESS_LICENSE"
|
package/src/application.js
CHANGED
|
@@ -65,7 +65,7 @@ class Application extends Router {
|
|
|
65
65
|
for(const key in defaultSettings) {
|
|
66
66
|
if(typeof this.settings[key] === 'undefined') {
|
|
67
67
|
if(typeof defaultSettings[key] === 'function') {
|
|
68
|
-
this.settings[key] = defaultSettings[key]();
|
|
68
|
+
this.settings[key] = defaultSettings[key](this);
|
|
69
69
|
} else {
|
|
70
70
|
this.settings[key] = defaultSettings[key];
|
|
71
71
|
}
|
|
@@ -75,19 +75,23 @@ class Application extends Router {
|
|
|
75
75
|
this.set('views', path.resolve('views'));
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
createWorkerTask(resolve, reject) {
|
|
79
|
+
const key = taskKey++;
|
|
80
|
+
workerTasks[key] = { resolve, reject };
|
|
81
|
+
if(key > 1000000) {
|
|
82
|
+
taskKey = 0;
|
|
83
|
+
}
|
|
84
|
+
return key;
|
|
85
|
+
}
|
|
86
|
+
|
|
78
87
|
readFileWithWorker(path) {
|
|
79
88
|
return new Promise((resolve, reject) => {
|
|
80
89
|
const worker = this.workers[Math.floor(Math.random() * this.workers.length)];
|
|
81
|
-
const key =
|
|
90
|
+
const key = this.createWorkerTask(resolve, reject);
|
|
82
91
|
worker.postMessage({ key, type: 'readFile', path });
|
|
83
|
-
workerTasks[key] = { resolve, reject };
|
|
84
|
-
if(key > 1000000) {
|
|
85
|
-
taskKey = 0;
|
|
86
|
-
}
|
|
87
92
|
});
|
|
88
93
|
}
|
|
89
94
|
|
|
90
|
-
|
|
91
95
|
set(key, value) {
|
|
92
96
|
if(key === 'trust proxy') {
|
|
93
97
|
if(!value) {
|
package/src/response.js
CHANGED
|
@@ -96,7 +96,7 @@ module.exports = class Response extends Writable {
|
|
|
96
96
|
this._res.writeHeader(header, this.headers[header]);
|
|
97
97
|
}
|
|
98
98
|
if(!this.headers['content-type']) {
|
|
99
|
-
this._res.writeHeader('content-type', 'text/html');
|
|
99
|
+
this._res.writeHeader('content-type', 'text/html' + (typeof chunk === 'string' ? `; charset=utf-8` : ''));
|
|
100
100
|
}
|
|
101
101
|
this.headersSent = true;
|
|
102
102
|
}
|
|
@@ -139,7 +139,7 @@ module.exports = class Response extends Writable {
|
|
|
139
139
|
if(!this.headersSent) {
|
|
140
140
|
const etagFn = this.app.get('etag fn');
|
|
141
141
|
if(data && !this.headers['etag'] && etagFn && !this.req.noEtag) {
|
|
142
|
-
this.set('etag', etagFn(data
|
|
142
|
+
this.set('etag', etagFn(data));
|
|
143
143
|
}
|
|
144
144
|
if(this.req.fresh) {
|
|
145
145
|
if(!this.headersSent) {
|
|
@@ -157,13 +157,16 @@ module.exports = class Response extends Writable {
|
|
|
157
157
|
this._res.writeHeader(header, this.headers[header]);
|
|
158
158
|
}
|
|
159
159
|
if(!this.headers['content-type']) {
|
|
160
|
-
this._res.writeHeader('content-type', 'text/html');
|
|
160
|
+
this._res.writeHeader('content-type', 'text/html' + (typeof data === 'string' ? `; charset=utf-8` : ''));
|
|
161
161
|
}
|
|
162
162
|
this.headersSent = true;
|
|
163
163
|
}
|
|
164
164
|
if(!data && this.headers['content-length']) {
|
|
165
165
|
this._res.endWithoutBody(this.headers['content-length'].toString());
|
|
166
166
|
} else {
|
|
167
|
+
if(data instanceof Buffer) {
|
|
168
|
+
data = data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
|
|
169
|
+
}
|
|
167
170
|
if(this.req.method === 'HEAD') {
|
|
168
171
|
const length = Buffer.byteLength(data ?? '');
|
|
169
172
|
this._res.endWithoutBody(length.toString());
|
|
@@ -180,14 +183,10 @@ module.exports = class Response extends Writable {
|
|
|
180
183
|
if(this.headersSent) {
|
|
181
184
|
throw new Error('Can\'t write body: Response was already sent');
|
|
182
185
|
}
|
|
183
|
-
if(
|
|
184
|
-
body = body.buffer.slice(body.byteOffset, body.byteOffset + body.byteLength);
|
|
185
|
-
} else if(body === null || body === undefined) {
|
|
186
|
+
if(body === null || body === undefined) {
|
|
186
187
|
body = '';
|
|
187
|
-
} else if(typeof body === 'object') {
|
|
188
|
-
|
|
189
|
-
return this.json(body);
|
|
190
|
-
}
|
|
188
|
+
} else if(typeof body === 'object' && !Buffer.isBuffer(body)) {
|
|
189
|
+
return this.json(body);
|
|
191
190
|
} else if(typeof body === 'number') {
|
|
192
191
|
if(arguments[1]) {
|
|
193
192
|
deprecated('res.send(status, body)', 'res.status(status).send(body)');
|
|
@@ -199,6 +198,12 @@ module.exports = class Response extends Writable {
|
|
|
199
198
|
} else {
|
|
200
199
|
body = String(body);
|
|
201
200
|
}
|
|
201
|
+
if(typeof body === 'string') {
|
|
202
|
+
const contentType = this.headers['content-type'];
|
|
203
|
+
if(contentType && !contentType.includes(';')) {
|
|
204
|
+
this.headers['content-type'] += '; charset=utf-8';
|
|
205
|
+
}
|
|
206
|
+
}
|
|
202
207
|
this.writeHead(this.statusCode);
|
|
203
208
|
return this.end(body);
|
|
204
209
|
}
|
|
@@ -390,7 +395,7 @@ module.exports = class Response extends Writable {
|
|
|
390
395
|
if(this._res.aborted) {
|
|
391
396
|
return;
|
|
392
397
|
}
|
|
393
|
-
this.
|
|
398
|
+
this.end(data);
|
|
394
399
|
if(callback) callback();
|
|
395
400
|
}).catch((err) => {
|
|
396
401
|
if(callback) callback(err);
|
|
@@ -446,10 +451,15 @@ module.exports = class Response extends Writable {
|
|
|
446
451
|
this.set(header, field[header]);
|
|
447
452
|
}
|
|
448
453
|
} else {
|
|
454
|
+
field = field.toLowerCase();
|
|
449
455
|
if(field === 'set-cookie' && Array.isArray(value)) {
|
|
450
456
|
value = value.join('; ');
|
|
457
|
+
} else if(field === 'content-type') {
|
|
458
|
+
if(value.startsWith('text/') || value === 'application/json' || value === 'application/javascript') {
|
|
459
|
+
value += '; charset=utf-8';
|
|
460
|
+
}
|
|
451
461
|
}
|
|
452
|
-
this.headers[field
|
|
462
|
+
this.headers[field] = String(value);
|
|
453
463
|
}
|
|
454
464
|
return this;
|
|
455
465
|
}
|
|
@@ -557,7 +567,7 @@ module.exports = class Response extends Writable {
|
|
|
557
567
|
}
|
|
558
568
|
json(body) {
|
|
559
569
|
if(!this.get('Content-Type')) {
|
|
560
|
-
this.set('Content-Type', 'application/json');
|
|
570
|
+
this.set('Content-Type', 'application/json; charset=utf-8');
|
|
561
571
|
}
|
|
562
572
|
const escape = this.app.get('json escape');
|
|
563
573
|
const replacer = this.app.get('json replacer');
|
|
@@ -569,7 +579,7 @@ module.exports = class Response extends Writable {
|
|
|
569
579
|
let body = stringify(object, this.app.get('json replacer'), this.app.get('json spaces'), this.app.get('json escape'));
|
|
570
580
|
|
|
571
581
|
if(!this.get('Content-Type')) {
|
|
572
|
-
this.set('Content-Type', 'application/javascript');
|
|
582
|
+
this.set('Content-Type', 'application/javascript; charset=utf-8');
|
|
573
583
|
this.set('X-Content-Type-Options', 'nosniff');
|
|
574
584
|
}
|
|
575
585
|
|
|
@@ -578,7 +588,7 @@ module.exports = class Response extends Writable {
|
|
|
578
588
|
}
|
|
579
589
|
|
|
580
590
|
if(typeof callback === 'string' && callback.length !== 0) {
|
|
581
|
-
this.set('Content-Type', 'application/javascript');
|
|
591
|
+
this.set('Content-Type', 'application/javascript; charset=utf-8');
|
|
582
592
|
this.set('X-Content-Type-Options', 'nosniff');
|
|
583
593
|
callback = callback.replace(/[^\[\]\w$.]/g, '');
|
|
584
594
|
|
|
@@ -614,14 +624,17 @@ module.exports = class Response extends Writable {
|
|
|
614
624
|
}
|
|
615
625
|
this.location(url);
|
|
616
626
|
this.status(status);
|
|
617
|
-
this.set('Content-Type', 'text/plain');
|
|
627
|
+
this.set('Content-Type', 'text/plain; charset=utf-8');
|
|
618
628
|
return this.send(`${statuses.message[status] ?? status}. Redirecting to ${url}`);
|
|
619
629
|
}
|
|
620
630
|
|
|
621
631
|
type(type) {
|
|
622
|
-
|
|
632
|
+
let ct = type.indexOf('/') === -1
|
|
623
633
|
? (mime.contentType(type) || 'application/octet-stream')
|
|
624
634
|
: type;
|
|
635
|
+
if(ct.startsWith('text/') || ct === 'application/json' || ct === 'application/javascript') {
|
|
636
|
+
ct += '; charset=UTF-8';
|
|
637
|
+
}
|
|
625
638
|
return this.set('Content-Type', ct);
|
|
626
639
|
}
|
|
627
640
|
contentType(type) {
|
|
@@ -658,7 +671,7 @@ function pipeStreamOverResponse(res, readStream, totalSize, callback) {
|
|
|
658
671
|
res._res.writeHeader(header, res.headers[header]);
|
|
659
672
|
}
|
|
660
673
|
if(!res.headers['content-type']) {
|
|
661
|
-
res._res.writeHeader('content-type', 'text/html');
|
|
674
|
+
res._res.writeHeader('content-type', 'text/html; charset=utf-8');
|
|
662
675
|
}
|
|
663
676
|
res.headersSent = true;
|
|
664
677
|
}
|