ultimate-express 2.0.2 → 2.0.4
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 +1 -0
- package/package.json +1 -1
- package/src/application.js +6 -0
- package/src/request.js +5 -2
- package/src/response.js +28 -8
- package/src/router.js +20 -2
package/README.md
CHANGED
package/package.json
CHANGED
package/src/application.js
CHANGED
|
@@ -191,6 +191,12 @@ class Application extends Router {
|
|
|
191
191
|
if(request._error) {
|
|
192
192
|
return this._handleError(request._error, null, request, response);
|
|
193
193
|
}
|
|
194
|
+
if(request._isOptions && request._matchedMethods.size > 0) {
|
|
195
|
+
const allowedMethods = Array.from(request._matchedMethods).join(',');
|
|
196
|
+
response.setHeader('Allow', allowedMethods);
|
|
197
|
+
response.send(allowedMethods);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
194
200
|
response.status(404);
|
|
195
201
|
this._sendErrorPage(request, response, `Cannot ${request.method} ${request.path}`, false);
|
|
196
202
|
}
|
package/src/request.js
CHANGED
|
@@ -69,8 +69,11 @@ module.exports = class Request extends Readable {
|
|
|
69
69
|
this._opPath = this._opPath.slice(0, -1);
|
|
70
70
|
}
|
|
71
71
|
this.method = req.getCaseSensitiveMethod().toUpperCase();
|
|
72
|
+
this._isOptions = this.method === 'OPTIONS';
|
|
73
|
+
this._isHead = this.method === 'HEAD';
|
|
72
74
|
this.params = {};
|
|
73
|
-
|
|
75
|
+
|
|
76
|
+
this._matchedMethods = new Set();
|
|
74
77
|
this._gotParams = new Set();
|
|
75
78
|
this._stack = [];
|
|
76
79
|
this._paramStack = [];
|
|
@@ -252,7 +255,7 @@ module.exports = class Request extends Readable {
|
|
|
252
255
|
if(this.#cachedParsedIp !== null) {
|
|
253
256
|
return this.#cachedParsedIp;
|
|
254
257
|
}
|
|
255
|
-
const finished =
|
|
258
|
+
const finished = this.res.finished;
|
|
256
259
|
if(finished) {
|
|
257
260
|
// mark app as one that needs ip after response
|
|
258
261
|
this.app.needsIpAfterResponse = true;
|
package/src/response.js
CHANGED
|
@@ -69,6 +69,7 @@ class Socket extends EventEmitter {
|
|
|
69
69
|
|
|
70
70
|
module.exports = class Response extends Writable {
|
|
71
71
|
#socket = null;
|
|
72
|
+
#ended = false;
|
|
72
73
|
#pendingChunks = [];
|
|
73
74
|
#lastWriteChunkTime = 0;
|
|
74
75
|
#writeTimeout = null;
|
|
@@ -112,13 +113,16 @@ module.exports = class Response extends Writable {
|
|
|
112
113
|
this._res.cork(() => {
|
|
113
114
|
this._res.close();
|
|
114
115
|
this.finished = true;
|
|
115
|
-
|
|
116
|
+
this.#socket?.emit('close');
|
|
116
117
|
});
|
|
117
118
|
});
|
|
119
|
+
this.once('close', () => {
|
|
120
|
+
this.#ended = true
|
|
121
|
+
})
|
|
118
122
|
}
|
|
119
123
|
|
|
120
124
|
get socket() {
|
|
121
|
-
this
|
|
125
|
+
if(this.#ended) return null;
|
|
122
126
|
if(!this.#socket) {
|
|
123
127
|
this.#socket = new Socket(this);
|
|
124
128
|
}
|
|
@@ -188,7 +192,7 @@ module.exports = class Response extends Writable {
|
|
|
188
192
|
super.end();
|
|
189
193
|
this.finished = true;
|
|
190
194
|
this.writingChunk = false;
|
|
191
|
-
|
|
195
|
+
this.#socket?.emit('close');
|
|
192
196
|
callback(null);
|
|
193
197
|
} else if (!ok) {
|
|
194
198
|
this._res.ab = chunk;
|
|
@@ -198,7 +202,7 @@ module.exports = class Response extends Writable {
|
|
|
198
202
|
const [ok, done] = this._res.tryEnd(this._res.ab.slice(offset - this._res.abOffset), this.totalSize);
|
|
199
203
|
if (done) {
|
|
200
204
|
this.finished = true;
|
|
201
|
-
|
|
205
|
+
this.#socket?.emit('close');
|
|
202
206
|
}
|
|
203
207
|
if (ok) {
|
|
204
208
|
this.writingChunk = false;
|
|
@@ -258,10 +262,18 @@ module.exports = class Response extends Writable {
|
|
|
258
262
|
sendStatus(code) {
|
|
259
263
|
return this.status(code).send(statuses.message[+code] ?? code.toString());
|
|
260
264
|
}
|
|
261
|
-
end(data) {
|
|
265
|
+
end(data, cb) {
|
|
266
|
+
if(typeof data === 'function') {
|
|
267
|
+
cb = data;
|
|
268
|
+
data = undefined;
|
|
269
|
+
}
|
|
270
|
+
if(typeof cb !== 'function') {
|
|
271
|
+
cb = undefined; // silence the error?
|
|
272
|
+
}
|
|
273
|
+
|
|
262
274
|
if(this.writingChunk) {
|
|
263
275
|
this.once('drain', () => {
|
|
264
|
-
this.end(data);
|
|
276
|
+
this.end(data, cb);
|
|
265
277
|
});
|
|
266
278
|
return;
|
|
267
279
|
}
|
|
@@ -282,9 +294,13 @@ module.exports = class Response extends Writable {
|
|
|
282
294
|
if(fresh) {
|
|
283
295
|
this._res.end();
|
|
284
296
|
this.finished = true;
|
|
285
|
-
|
|
297
|
+
this.#socket?.emit('close');
|
|
286
298
|
this.emit('finish');
|
|
287
299
|
this.emit('close');
|
|
300
|
+
cb && queueMicrotask(() => {
|
|
301
|
+
this.#ended = true;
|
|
302
|
+
cb();
|
|
303
|
+
});
|
|
288
304
|
return;
|
|
289
305
|
}
|
|
290
306
|
}
|
|
@@ -309,9 +325,13 @@ module.exports = class Response extends Writable {
|
|
|
309
325
|
}
|
|
310
326
|
|
|
311
327
|
this.finished = true;
|
|
312
|
-
|
|
328
|
+
this.#socket?.emit('close');
|
|
313
329
|
this.emit('finish');
|
|
314
330
|
this.emit('close');
|
|
331
|
+
cb && queueMicrotask(() => {
|
|
332
|
+
this.#ended = true;
|
|
333
|
+
cb();
|
|
334
|
+
});
|
|
315
335
|
});
|
|
316
336
|
return this;
|
|
317
337
|
}
|
package/src/router.js
CHANGED
|
@@ -152,6 +152,9 @@ module.exports = class Router extends EventEmitter {
|
|
|
152
152
|
all: method === 'ALL' || method === 'USE',
|
|
153
153
|
gettable: method === 'GET' || method === 'HEAD',
|
|
154
154
|
};
|
|
155
|
+
if(typeof route.path === 'string' && (route.path.includes(':') || route.path.includes('*') || (route.path.includes('(') && route.path.includes(')'))) && route.pattern instanceof RegExp) {
|
|
156
|
+
route.complex = true;
|
|
157
|
+
}
|
|
155
158
|
routes.push(route);
|
|
156
159
|
// normal routes optimization
|
|
157
160
|
if(canBeOptimized(route.path) && route.pattern !== '/*' && !this.parent && this.get('case sensitive routing') && this.uwsApp) {
|
|
@@ -296,6 +299,12 @@ module.exports = class Router extends EventEmitter {
|
|
|
296
299
|
if(request._error) {
|
|
297
300
|
return this._handleError(request._error, null, request, response);
|
|
298
301
|
}
|
|
302
|
+
if(request._isOptions && request._matchedMethods.size > 0) {
|
|
303
|
+
const allowedMethods = Array.from(request._matchedMethods).join(',');
|
|
304
|
+
response.setHeader('Allow', allowedMethods);
|
|
305
|
+
response.send(allowedMethods);
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
299
308
|
response.status(404);
|
|
300
309
|
request.noEtag = true;
|
|
301
310
|
this._sendErrorPage(request, response, `Cannot ${request.method} ${request._originalPath}`, false);
|
|
@@ -369,7 +378,7 @@ module.exports = class Router extends EventEmitter {
|
|
|
369
378
|
req.route = route;
|
|
370
379
|
if(route.optimizedParams) {
|
|
371
380
|
req.params = {...req.optimizedParams};
|
|
372
|
-
} else if(
|
|
381
|
+
} else if(route.complex) {
|
|
373
382
|
let path = req._originalPath;
|
|
374
383
|
if(req._stack.length > 0) {
|
|
375
384
|
path = path.replace(this.getFullMountpath(req), '');
|
|
@@ -445,7 +454,7 @@ module.exports = class Router extends EventEmitter {
|
|
|
445
454
|
}
|
|
446
455
|
|
|
447
456
|
async _routeRequest(req, res, startIndex = 0, routes = this._routes, skipCheck = false, skipUntil) {
|
|
448
|
-
let routeIndex = skipCheck ? startIndex : findIndexStartingFrom(routes, r => (r.all || r.method === req.method || (r.gettable && req.
|
|
457
|
+
let routeIndex = skipCheck ? startIndex : findIndexStartingFrom(routes, r => (r.all || r.method === req.method || req._isOptions || (r.gettable && req._isHead)) && this._pathMatches(r, req), startIndex);
|
|
449
458
|
const route = routes[routeIndex];
|
|
450
459
|
if(!route) {
|
|
451
460
|
if(!skipCheck) {
|
|
@@ -546,6 +555,15 @@ module.exports = class Router extends EventEmitter {
|
|
|
546
555
|
}
|
|
547
556
|
|
|
548
557
|
try {
|
|
558
|
+
// handling OPTIONS method
|
|
559
|
+
if(req._isOptions && !route.all && route.method !== 'OPTIONS') {
|
|
560
|
+
req._matchedMethods.add(route.method);
|
|
561
|
+
if(route.gettable) {
|
|
562
|
+
req._matchedMethods.add('HEAD');
|
|
563
|
+
}
|
|
564
|
+
return next();
|
|
565
|
+
}
|
|
566
|
+
|
|
549
567
|
// skipping routes we already went through via optimized path
|
|
550
568
|
if(!skipCheck && skipUntil && skipUntil.routeKey >= route.routeKey) {
|
|
551
569
|
return next();
|