Skip to content

Commit 708ac4c

Browse files
committedApr 14, 2022
Fix handling very large stacks of sync middleware
closes #4891
1 parent 92c5ce5 commit 708ac4c

File tree

5 files changed

+56
-0
lines changed

5 files changed

+56
-0
lines changed
 

‎History.md

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ unreleased
55
* Allow `options` without `filename` in `res.download`
66
* Deprecate string and non-integer arguments to `res.status`
77
* Fix behavior of `null`/`undefined` as `maxAge` in `res.cookie`
8+
* Fix handling very large stacks of sync middleware
89
* Ignore `Object.prototype` values in settings through `app.set`/`app.get`
910
* Invoke `default` with same arguments as types in `res.format`
1011
* Support proper 205 responses using `res.send`

‎lib/router/index.js

+8
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ proto.handle = function handle(req, res, out) {
142142
var protohost = getProtohost(req.url) || ''
143143
var removed = '';
144144
var slashAdded = false;
145+
var sync = 0
145146
var paramcalled = {};
146147

147148
// store options for OPTIONS request
@@ -203,6 +204,11 @@ proto.handle = function handle(req, res, out) {
203204
return;
204205
}
205206

207+
// max sync stack
208+
if (++sync > 100) {
209+
return setImmediate(next, err)
210+
}
211+
206212
// get pathname of request
207213
var path = getPathname(req);
208214

@@ -321,6 +327,8 @@ proto.handle = function handle(req, res, out) {
321327
} else {
322328
layer.handle_request(req, res, next);
323329
}
330+
331+
sync = 0
324332
}
325333
};
326334

‎lib/router/route.js

+9
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ Route.prototype._options = function _options() {
9898
Route.prototype.dispatch = function dispatch(req, res, done) {
9999
var idx = 0;
100100
var stack = this.stack;
101+
var sync = 0
102+
101103
if (stack.length === 0) {
102104
return done();
103105
}
@@ -127,6 +129,11 @@ Route.prototype.dispatch = function dispatch(req, res, done) {
127129
return done(err);
128130
}
129131

132+
// max sync stack
133+
if (++sync > 100) {
134+
return setImmediate(next, err)
135+
}
136+
130137
if (layer.method && layer.method !== method) {
131138
return next(err);
132139
}
@@ -136,6 +143,8 @@ Route.prototype.dispatch = function dispatch(req, res, done) {
136143
} else {
137144
layer.handle_request(req, res, next);
138145
}
146+
147+
sync = 0
139148
}
140149
};
141150

‎test/Route.js

+22
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,28 @@ describe('Route', function(){
1313
route.dispatch(req, {}, done)
1414
})
1515

16+
it('should not stack overflow with a large sync stack', function (done) {
17+
this.timeout(5000) // long-running test
18+
19+
var req = { method: 'GET', url: '/' }
20+
var route = new Route('/foo')
21+
22+
for (var i = 0; i < 6000; i++) {
23+
route.all(function (req, res, next) { next() })
24+
}
25+
26+
route.get(function (req, res, next) {
27+
req.called = true
28+
next()
29+
})
30+
31+
route.dispatch(req, {}, function (err) {
32+
if (err) return done(err)
33+
assert.ok(req.called)
34+
done()
35+
})
36+
})
37+
1638
describe('.all', function(){
1739
it('should add handler', function(done){
1840
var req = { method: 'GET', url: '/' };

‎test/Router.js

+16
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,22 @@ describe('Router', function(){
7676
router.handle({ url: '/', method: 'GET' }, { end: done });
7777
});
7878

79+
it('should not stack overflow with a large sync stack', function (done) {
80+
this.timeout(5000) // long-running test
81+
82+
var router = new Router()
83+
84+
for (var i = 0; i < 6000; i++) {
85+
router.use(function (req, res, next) { next() })
86+
}
87+
88+
router.use(function (req, res) {
89+
res.end()
90+
})
91+
92+
router.handle({ url: '/', method: 'GET' }, { end: done })
93+
})
94+
7995
describe('.handle', function(){
8096
it('should dispatch', function(done){
8197
var router = new Router();

0 commit comments

Comments
 (0)
Please sign in to comment.