ultimate-express 1.2.17 → 1.2.20
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 +4 -3
- package/package.json +1 -1
- package/src/application.js +28 -10
- package/src/response.js +38 -43
- package/src/router.js +11 -6
package/README.md
CHANGED
|
@@ -79,7 +79,7 @@ https.createServer({
|
|
|
79
79
|
|
|
80
80
|
You have to pass `uwsOptions` to the `express()` constructor:
|
|
81
81
|
```js
|
|
82
|
-
const express = require("
|
|
82
|
+
const express = require("ultimate-express");
|
|
83
83
|
|
|
84
84
|
const app = express({
|
|
85
85
|
uwsOptions: {
|
|
@@ -144,7 +144,8 @@ In general, basically all features and options are supported. Use [Express 4.x d
|
|
|
144
144
|
|
|
145
145
|
### Application
|
|
146
146
|
|
|
147
|
-
- ✅ app.listen()
|
|
147
|
+
- ✅ app.listen(port[, host][, callback])
|
|
148
|
+
- ✅ app.listen(unix_socket[, callback])
|
|
148
149
|
- ✅ app.METHOD() (app.get, app.post, etc.)
|
|
149
150
|
- ✅ app.route()
|
|
150
151
|
- ✅ app.all()
|
|
@@ -312,4 +313,4 @@ Any Express view engine should work. Here's list of engines we include in our te
|
|
|
312
313
|
- ✅ [express-dot-engine](https://npmjs.com/package/express-dot-engine)
|
|
313
314
|
- ✅ [express-art-template](https://npmjs.com/package/express-art-template)
|
|
314
315
|
- ✅ [express-handlebars](https://npmjs.com/package/express-handlebars)
|
|
315
|
-
- ✅ [swig](https://npmjs.com/package/swig)
|
|
316
|
+
- ✅ [swig](https://npmjs.com/package/swig)
|
package/package.json
CHANGED
package/src/application.js
CHANGED
|
@@ -183,34 +183,52 @@ class Application extends Router {
|
|
|
183
183
|
const matchedRoute = await this._routeRequest(request, response);
|
|
184
184
|
if(!matchedRoute && !response.headersSent && !response.aborted) {
|
|
185
185
|
response.status(404);
|
|
186
|
-
|
|
186
|
+
this._sendErrorPage(request, response, `Cannot ${request.method} ${request.path}`, false);
|
|
187
187
|
}
|
|
188
188
|
});
|
|
189
189
|
}
|
|
190
190
|
|
|
191
|
-
listen(port, callback) {
|
|
191
|
+
listen(port, host, callback) {
|
|
192
192
|
this.#createRequestHandler();
|
|
193
|
+
// support listen(callback)
|
|
193
194
|
if(!callback && typeof port === 'function') {
|
|
194
195
|
callback = port;
|
|
195
196
|
port = 0;
|
|
196
197
|
}
|
|
198
|
+
// support listen(port, callback)
|
|
199
|
+
if(typeof host === 'function') {
|
|
200
|
+
callback = host;
|
|
201
|
+
host = undefined;
|
|
202
|
+
}
|
|
203
|
+
const onListen = socket => {
|
|
204
|
+
if(!socket) {
|
|
205
|
+
let err = new Error('Failed to listen on port ' + port + '. No permission or address already in use.');
|
|
206
|
+
throw err;
|
|
207
|
+
}
|
|
208
|
+
this.port = uWS.us_socket_local_port(socket);
|
|
209
|
+
callback(this.port);
|
|
210
|
+
};
|
|
197
211
|
let fn = 'listen';
|
|
212
|
+
let args = [port];
|
|
198
213
|
if(typeof port !== 'number') {
|
|
199
214
|
if(!isNaN(Number(port))) {
|
|
200
215
|
port = Number(port);
|
|
216
|
+
args.push(onListen);
|
|
217
|
+
if(host) {
|
|
218
|
+
args.unshift(host);
|
|
219
|
+
}
|
|
201
220
|
} else {
|
|
202
221
|
fn = 'listen_unix';
|
|
222
|
+
args.unshift(onListen);
|
|
223
|
+
if(host) {
|
|
224
|
+
args.unshift(host);
|
|
225
|
+
}
|
|
203
226
|
}
|
|
227
|
+
} else {
|
|
228
|
+
args.push(onListen);
|
|
204
229
|
}
|
|
205
230
|
this.listenCalled = true;
|
|
206
|
-
this.uwsApp[fn](
|
|
207
|
-
if(!socket) {
|
|
208
|
-
let err = new Error('Failed to listen on port ' + port + '. No permission or address already in use.');
|
|
209
|
-
throw err;
|
|
210
|
-
}
|
|
211
|
-
this.port = uWS.us_socket_local_port(socket);
|
|
212
|
-
callback(this.port);
|
|
213
|
-
});
|
|
231
|
+
this.uwsApp[fn](...args);
|
|
214
232
|
}
|
|
215
233
|
|
|
216
234
|
address() {
|
package/src/response.js
CHANGED
|
@@ -116,19 +116,7 @@ module.exports = class Response extends Writable {
|
|
|
116
116
|
if(!this.headersSent) {
|
|
117
117
|
this.writeHead(this.statusCode);
|
|
118
118
|
this._res.writeStatus(this.statusCode.toString());
|
|
119
|
-
|
|
120
|
-
if(header === 'content-length') {
|
|
121
|
-
// if content-length is set, disable chunked transfer encoding, since size is known
|
|
122
|
-
this.chunkedTransfer = false;
|
|
123
|
-
this.totalSize = parseInt(this.headers[header]);
|
|
124
|
-
continue;
|
|
125
|
-
}
|
|
126
|
-
this._res.writeHeader(header, this.headers[header]);
|
|
127
|
-
}
|
|
128
|
-
if(!this.headers['content-type']) {
|
|
129
|
-
this._res.writeHeader('content-type', 'text/html' + (typeof chunk === 'string' ? `; charset=utf-8` : ''));
|
|
130
|
-
}
|
|
131
|
-
this.headersSent = true;
|
|
119
|
+
this.writeHeaders(typeof chunk === 'string');
|
|
132
120
|
}
|
|
133
121
|
if(!Buffer.isBuffer(chunk) && !(chunk instanceof ArrayBuffer)) {
|
|
134
122
|
chunk = Buffer.from(chunk);
|
|
@@ -184,6 +172,27 @@ module.exports = class Response extends Writable {
|
|
|
184
172
|
this.set(header, headers[header]);
|
|
185
173
|
}
|
|
186
174
|
}
|
|
175
|
+
writeHeaders(utf8) {
|
|
176
|
+
for(const header in this.headers) {
|
|
177
|
+
if(header === 'content-length') {
|
|
178
|
+
// if content-length is set, disable chunked transfer encoding, since size is known
|
|
179
|
+
this.chunkedTransfer = false;
|
|
180
|
+
this.totalSize = parseInt(this.headers[header]);
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
if(Array.isArray(this.headers[header])) {
|
|
184
|
+
for(let value of this.headers[header]) {
|
|
185
|
+
this._res.writeHeader(header, value);
|
|
186
|
+
}
|
|
187
|
+
} else {
|
|
188
|
+
this._res.writeHeader(header, this.headers[header]);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if(!this.headers['content-type']) {
|
|
192
|
+
this._res.writeHeader('content-type', 'text/html' + (utf8 ? `; charset=utf-8` : ''));
|
|
193
|
+
}
|
|
194
|
+
this.headersSent = true;
|
|
195
|
+
}
|
|
187
196
|
_implicitHeader() {
|
|
188
197
|
// compatibility function
|
|
189
198
|
// usually should send headers but this is useless for us
|
|
@@ -212,16 +221,7 @@ module.exports = class Response extends Writable {
|
|
|
212
221
|
}
|
|
213
222
|
const fresh = this.req.fresh;
|
|
214
223
|
this._res.writeStatus(fresh ? '304' : this.statusCode.toString());
|
|
215
|
-
|
|
216
|
-
if(header === 'content-length') {
|
|
217
|
-
continue;
|
|
218
|
-
}
|
|
219
|
-
this._res.writeHeader(header, this.headers[header]);
|
|
220
|
-
}
|
|
221
|
-
if(!this.headers['content-type']) {
|
|
222
|
-
this._res.writeHeader('content-type', 'text/html' + (typeof data === 'string' ? `; charset=utf-8` : ''));
|
|
223
|
-
}
|
|
224
|
-
this.headersSent = true;
|
|
224
|
+
this.writeHeaders(true);
|
|
225
225
|
if(fresh) {
|
|
226
226
|
this._res.end();
|
|
227
227
|
this.finished = true;
|
|
@@ -543,18 +543,22 @@ module.exports = class Response extends Writable {
|
|
|
543
543
|
}
|
|
544
544
|
append(field, value) {
|
|
545
545
|
field = field.toLowerCase();
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
546
|
+
const old = this.headers[field];
|
|
547
|
+
if(old) {
|
|
548
|
+
const newVal = [];
|
|
549
|
+
if(Array.isArray(old)) {
|
|
550
|
+
newVal.push(...old);
|
|
551
|
+
} else if(old) {
|
|
552
|
+
newVal.push(old);
|
|
551
553
|
}
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
this.headers[field] = value.join(', ');
|
|
554
|
+
if(Array.isArray(value)) {
|
|
555
|
+
newVal.push(...value);
|
|
556
|
+
} else {
|
|
557
|
+
newVal.push(value);
|
|
557
558
|
}
|
|
559
|
+
this.headers[field] = newVal;
|
|
560
|
+
} else {
|
|
561
|
+
this.headers[field] = value;
|
|
558
562
|
}
|
|
559
563
|
return this;
|
|
560
564
|
}
|
|
@@ -719,16 +723,7 @@ function pipeStreamOverResponse(res, readStream, totalSize, callback) {
|
|
|
719
723
|
if(!res.headersSent) {
|
|
720
724
|
res.writeHead(res.statusCode);
|
|
721
725
|
res._res.writeStatus(res.statusCode.toString());
|
|
722
|
-
|
|
723
|
-
if(header === 'content-length') {
|
|
724
|
-
continue;
|
|
725
|
-
}
|
|
726
|
-
res._res.writeHeader(header, res.headers[header]);
|
|
727
|
-
}
|
|
728
|
-
if(!res.headers['content-type']) {
|
|
729
|
-
res._res.writeHeader('content-type', 'text/html; charset=utf-8');
|
|
730
|
-
}
|
|
731
|
-
res.headersSent = true;
|
|
726
|
+
res.writeHeaders(true);
|
|
732
727
|
}
|
|
733
728
|
const ab = chunk.buffer.slice(chunk.byteOffset, chunk.byteOffset + chunk.byteLength);
|
|
734
729
|
|
package/src/router.js
CHANGED
|
@@ -276,7 +276,8 @@ module.exports = class Router extends EventEmitter {
|
|
|
276
276
|
const matchedRoute = await this._routeRequest(request, response, 0, optimizedPath, true, route);
|
|
277
277
|
if(!matchedRoute && !response.headersSent && !response.aborted) {
|
|
278
278
|
response.status(404);
|
|
279
|
-
|
|
279
|
+
request.noEtag = true;
|
|
280
|
+
this._sendErrorPage(request, response, `Cannot ${request.method} ${request.path}`, false);
|
|
280
281
|
}
|
|
281
282
|
};
|
|
282
283
|
route.optimizedPath = optimizedPath;
|
|
@@ -305,7 +306,7 @@ module.exports = class Router extends EventEmitter {
|
|
|
305
306
|
if(response.statusCode === 200) {
|
|
306
307
|
response.statusCode = 500;
|
|
307
308
|
}
|
|
308
|
-
|
|
309
|
+
this._sendErrorPage(request, response, err, true);
|
|
309
310
|
}
|
|
310
311
|
});
|
|
311
312
|
}
|
|
@@ -313,7 +314,7 @@ module.exports = class Router extends EventEmitter {
|
|
|
313
314
|
if(response.statusCode === 200) {
|
|
314
315
|
response.statusCode = 500;
|
|
315
316
|
}
|
|
316
|
-
|
|
317
|
+
this._sendErrorPage(request, response, err, true);
|
|
317
318
|
}
|
|
318
319
|
|
|
319
320
|
#extractParams(pattern, path) {
|
|
@@ -524,11 +525,15 @@ module.exports = class Router extends EventEmitter {
|
|
|
524
525
|
return fns;
|
|
525
526
|
}
|
|
526
527
|
|
|
527
|
-
|
|
528
|
+
_sendErrorPage(request, response, err, checkEnv = false) {
|
|
528
529
|
if(checkEnv && this.get('env') === 'production') {
|
|
529
530
|
err = 'Internal Server Error';
|
|
530
531
|
}
|
|
531
|
-
|
|
532
|
+
request.noEtag = true;
|
|
533
|
+
response.setHeader('Content-Type', 'text/html; charset=utf-8');
|
|
534
|
+
response.setHeader('X-Content-Type-Options', 'nosniff');
|
|
535
|
+
response.setHeader('Content-Security-Policy', "default-src 'none'");
|
|
536
|
+
response.send(`<!DOCTYPE html>\n` +
|
|
532
537
|
`<html lang="en">\n` +
|
|
533
538
|
`<head>\n` +
|
|
534
539
|
`<meta charset="utf-8">\n` +
|
|
@@ -537,6 +542,6 @@ module.exports = class Router extends EventEmitter {
|
|
|
537
542
|
`<body>\n` +
|
|
538
543
|
`<pre>${err?.stack ?? err}</pre>\n` +
|
|
539
544
|
`</body>\n` +
|
|
540
|
-
`</html>\n
|
|
545
|
+
`</html>\n`);
|
|
541
546
|
}
|
|
542
547
|
}
|