Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,11 @@ function createETagGenerator (options) {

function parseExtendedQueryString(str) {
return qs.parse(str, {
allowPrototypes: true
allowPrototypes: true,
// fix(#7147): qs defaults arrayLimit to 20, causing arrays with more than
// 20 items to be returned as a plain object with numeric string keys instead
// of an array. Setting Infinity removes that cap while the existing
// parameterLimit (default: 1000) already bounds total query string size.
arrayLimit: Infinity
});
}
60 changes: 60 additions & 0 deletions test/req.query.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,63 @@ function createApp(setting) {

return app;
}

// regression test for issue #7147
// req.query was returning a plain object (not an array) when a repeated
// query param had more than 20 values — caused by qs defaulting arrayLimit to 20
describe('regression: issue #7147 — arrays with more than 20 values', function () {
it('should parse 21 repeated query params as an array, not an object', function (done) {
var app = createApp('extended');

// Build ?ids=0&ids=1&...&ids=20 (21 values)
var query = Array.from({ length: 21 }, function (_, i) { return 'ids=' + i; }).join('&');

request(app)
.get('/?' + query)
.expect(200)
.end(function (err, res) {
if (err) return done(err);
var parsed = JSON.parse(res.text);
assert.ok(Array.isArray(parsed.ids),
'req.query.ids should be an Array, got: ' + JSON.stringify(parsed.ids));
assert.strictEqual(parsed.ids.length, 21);
done();
});
});

it('should parse 100 repeated query params as an array', function (done) {
var app = createApp('extended');

var query = Array.from({ length: 100 }, function (_, i) { return 'ids=' + i; }).join('&');

request(app)
.get('/?' + query)
.expect(200)
.end(function (err, res) {
if (err) return done(err);
var parsed = JSON.parse(res.text);
assert.ok(Array.isArray(parsed.ids),
'req.query.ids should be an Array, got: ' + typeof parsed.ids);
assert.strictEqual(parsed.ids.length, 100);
done();
});
});

it('should still parse 20 or fewer repeated params as an array', function (done) {
var app = createApp('extended');

var query = Array.from({ length: 20 }, function (_, i) { return 'ids=' + i; }).join('&');

request(app)
.get('/?' + query)
.expect(200)
.end(function (err, res) {
if (err) return done(err);
var parsed = JSON.parse(res.text);
assert.ok(Array.isArray(parsed.ids),
'req.query.ids should be an Array for 20 items');
assert.strictEqual(parsed.ids.length, 20);
done();
});
});
});
69 changes: 69 additions & 0 deletions test/res.type.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,74 @@ describe('res', function(){
.get('/')
.expect('Content-Type', 'application/vnd.amazon.ebook', done);
})

describe('edge cases', function(){
it('should handle empty string gracefully', function(done){
var app = express();

app.use(function(req, res){
res.type('').end('test');
});

request(app)
.get('/')
.expect('Content-Type', 'application/octet-stream')
.end(done);
})

it('should handle file extension with dots', function(done){
var app = express();

app.use(function(req, res){
res.type('.json').end('{"test": true}');
});

request(app)
.get('/')
.expect('Content-Type', 'application/json; charset=utf-8')
.end(done);
})

it('should handle multiple file extensions', function(done){
var app = express();

app.use(function(req, res){
res.type('file.tar.gz').end('compressed');
});

request(app)
.get('/')
.expect('Content-Type', 'application/gzip')
.end(done);
})

it('should handle uppercase extensions', function(done){
var app = express();

app.use(function(req, res){
res.type('FILE.JSON').end('{"test": true}');
});

request(app)
.get('/')
.expect('Content-Type', 'application/json; charset=utf-8')
.end(done);
})

it('should handle extension with special characters', function(done){
var app = express();

app.use(function(req, res){
res.type('file@test.json').end('{"test": true}');
});

request(app)
.get('/')
.expect('Content-Type', 'application/json; charset=utf-8')
.end(done);
})
})
})
})