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
113 changes: 79 additions & 34 deletions lib/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -228,16 +228,21 @@ req.range = function range(size, options) {
*/

defineGetter(req, 'query', function query(){
if (this._query !== undefined) {
return this._query;
}

var queryparse = this.app.get('query parser fn');

if (!queryparse) {
// parsing is disabled
return Object.create(null);
this._query = Object.create(null);
} else {
var querystring = parse(this).query;
this._query = queryparse(querystring);
}

var querystring = parse(this).query;

return queryparse(querystring);
return this._query;
});

/**
Expand Down Expand Up @@ -295,23 +300,29 @@ req.is = function is(types) {
*/

defineGetter(req, 'protocol', function protocol(){
if (this._protocol !== undefined) {
return this._protocol;
}

var proto = this.socket.encrypted
? 'https'
: 'http';
var trust = this.app.get('trust proxy fn');

if (!trust(this.socket.remoteAddress, 0)) {
return proto;
}
this._protocol = proto;
} else {
// Note: X-Forwarded-Proto is normally only ever a
// single value, but this is to be safe.
var header = this.get('X-Forwarded-Proto') || proto
var index = header.indexOf(',')

// Note: X-Forwarded-Proto is normally only ever a
// single value, but this is to be safe.
var header = this.get('X-Forwarded-Proto') || proto
var index = header.indexOf(',')
this._protocol = index !== -1
? header.substring(0, index).trim()
: header.trim();
}

return index !== -1
? header.substring(0, index).trim()
: header.trim()
return this._protocol;
});

/**
Expand All @@ -338,8 +349,12 @@ defineGetter(req, 'secure', function secure(){
*/

defineGetter(req, 'ip', function ip(){
if (this._ip !== undefined) {
return this._ip;
}
var trust = this.app.get('trust proxy fn');
return proxyaddr(this, trust);
this._ip = proxyaddr(this, trust);
return this._ip;
});

/**
Expand All @@ -355,14 +370,18 @@ defineGetter(req, 'ip', function ip(){
*/

defineGetter(req, 'ips', function ips() {
if (this._ips !== undefined) {
return this._ips;
}
var trust = this.app.get('trust proxy fn');
var addrs = proxyaddr.all(this, trust);

// reverse the order (to farthest -> closest)
// and remove socket address
addrs.reverse().pop()

return addrs
this._ips = addrs;
return this._ips;
});

/**
Expand All @@ -381,16 +400,23 @@ defineGetter(req, 'ips', function ips() {
*/

defineGetter(req, 'subdomains', function subdomains() {
if (this._subdomains !== undefined) {
return this._subdomains;
}
var hostname = this.hostname;

if (!hostname) return [];
if (!hostname) {
this._subdomains = [];
} else {
var offset = this.app.get('subdomain offset');
var subdomains = !isIP(hostname)
? hostname.split('.').reverse()
: [hostname];

var offset = this.app.get('subdomain offset');
var subdomains = !isIP(hostname)
? hostname.split('.').reverse()
: [hostname];
this._subdomains = subdomains.slice(offset);
}

return subdomains.slice(offset);
return this._subdomains;
});

/**
Expand All @@ -401,7 +427,11 @@ defineGetter(req, 'subdomains', function subdomains() {
*/

defineGetter(req, 'path', function path() {
return parse(this).pathname;
if (this._path !== undefined) {
return this._path;
}
this._path = parse(this).pathname;
return this._path;
});

/**
Expand All @@ -416,6 +446,9 @@ defineGetter(req, 'path', function path() {
*/

defineGetter(req, 'host', function host(){
if (this._host !== undefined) {
return this._host;
}
var trust = this.app.get('trust proxy fn');
var val = this.get('X-Forwarded-Host');

Expand All @@ -427,7 +460,8 @@ defineGetter(req, 'host', function host(){
val = val.substring(0, val.indexOf(',')).trimEnd()
}

return val || undefined;
this._host = val || undefined;
return this._host;
});

/**
Expand All @@ -442,19 +476,26 @@ defineGetter(req, 'host', function host(){
*/

defineGetter(req, 'hostname', function hostname(){
if (this._hostname !== undefined) {
return this._hostname;
}
var host = this.host;

if (!host) return;

// IPv6 literal support
var offset = host[0] === '['
? host.indexOf(']') + 1
: 0;
var index = host.indexOf(':', offset);
if (!host) {
this._hostname = undefined;
} else {
// IPv6 literal support
var offset = host[0] === '['
? host.indexOf(']') + 1
: 0;
var index = host.indexOf(':', offset);

this._hostname = index !== -1
? host.substring(0, index)
: host;
}

return index !== -1
? host.substring(0, index)
: host;
return this._hostname;
});

/**
Expand Down Expand Up @@ -506,8 +547,12 @@ defineGetter(req, 'stale', function stale(){
*/

defineGetter(req, 'xhr', function xhr(){
if (this._xhr !== undefined) {
return this._xhr;
}
var val = this.get('X-Requested-With') || '';
return val.toLowerCase() === 'xmlhttprequest';
this._xhr = val.toLowerCase() === 'xmlhttprequest';
return this._xhr;
});

/**
Expand Down
37 changes: 27 additions & 10 deletions lib/response.js
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,20 @@ res.send = function send(body) {
return this.json(chunk);
}
break;
case 'bigint':
chunk = chunk.toString();
encoding = 'utf8';
const ct = this.get('Content-Type');
if (typeof ct === 'string') {
this.set('Content-Type', setCharset(ct, 'utf-8'));
} else {
this.type('txt');
}
break;
case 'symbol':
throw new TypeError('symbol cannot be sent in a response');
case 'function':
throw new TypeError('function cannot be sent in a response');
}

// determine if ETag should be generated
Expand All @@ -169,8 +183,11 @@ res.send = function send(body) {
if (Buffer.isBuffer(chunk)) {
// get length of Buffer
len = chunk.length
} else if (!generateETag && chunk.length < 1000) {
// just calculate length when no ETag + small chunk
} else if (!generateETag) {
// just calculate length when no ETag
len = Buffer.byteLength(chunk, encoding)
} else if (chunk.length < 1000) {
// just calculate length when ETag + small chunk
len = Buffer.byteLength(chunk, encoding)
} else {
// convert chunk to Buffer and calculate
Expand Down Expand Up @@ -760,7 +777,9 @@ res.cookie = function (name, value, options) {
}

if (opts.maxAge != null) {
var maxAge = opts.maxAge - 0
var maxAge = typeof opts.maxAge === 'bigint'
? Number(opts.maxAge)
: opts.maxAge - 0

if (!isNaN(maxAge)) {
opts.expires = new Date(Date.now() + maxAge)
Expand All @@ -780,12 +799,9 @@ res.cookie = function (name, value, options) {
/**
* Set the location header to `url`.
*
* The given `url` can also be "back", which redirects
* to the _Referrer_ or _Referer_ headers or "/".
*
* Examples:
*
* res.location('/foo/bar').;
* res.location('/foo/bar');
* res.location('http://example.com');
* res.location('../login');
*
Expand Down Expand Up @@ -839,15 +855,16 @@ res.redirect = function redirect(url) {
address = this.location(address).get('Location');

// Support text/{plain,html} by default
var statusMessage = statuses.message[status] || 'Redirecting';
this.format({
text: function(){
body = statuses.message[status] + '. Redirecting to ' + address
body = statusMessage + '. Redirecting to ' + address
},

html: function(){
var u = escapeHtml(address);
body = '<!DOCTYPE html><head><title>' + statuses.message[status] + '</title></head>'
+ '<body><p>' + statuses.message[status] + '. Redirecting to ' + u + '</p></body>'
body = '<!DOCTYPE html><head><title>' + statusMessage + '</title></head>'
+ '<body><p>' + statusMessage + '. Redirecting to ' + u + '</p></body>'
},

default: function(){
Expand Down
29 changes: 29 additions & 0 deletions test/app.request.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,5 +139,34 @@ describe('app', function(){
.get('/sub/foo')
.expect(200, 'tobi', cb)
})

describe('getter caching', function () {
it('should cache lazy getters per request', function (done) {
var app = express()

app.use(function (req, res) {
var q1 = req.query
var q2 = req.query
var ips1 = req.ips
var ips2 = req.ips
var path1 = req.path
var path2 = req.path
var sub1 = req.subdomains
var sub2 = req.subdomains

var assert = require('node:assert')
assert.strictEqual(q1, q2)
assert.strictEqual(ips1, ips2)
assert.strictEqual(path1, path2)
assert.strictEqual(sub1, sub2)

res.send('cached')
})

request(app)
.get('/foo?bar=1')
.expect(200, 'cached', done)
})
})
})
})
15 changes: 15 additions & 0 deletions test/res.cookie.js
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,21 @@ describe('res', function(){
.get('/')
.expect(500, /option maxAge is invalid/, done)
})

it('should support bigint maxAge', function (done) {
var app = express()

app.use(function (req, res) {
res.cookie('name', 'tobi', { maxAge: 1000n })
res.end()
})

request(app)
.get('/')
.expect(200)
.expect('Set-Cookie', /Max-Age=1/)
.end(done)
})
})

describe('priority', function () {
Expand Down
17 changes: 17 additions & 0 deletions test/res.redirect.js
Original file line number Diff line number Diff line change
Expand Up @@ -211,4 +211,21 @@ describe('res', function(){
.end(done)
})
})

describe('when status is custom or non-standard', function(){
it('should use "Redirecting" fallback in response body', function(done){
var app = express();

app.use(function(req, res){
res.redirect(399, 'http://google.com');
});

request(app)
.get('/')
.set('Accept', 'text/plain')
.expect(399)
.expect('Location', 'http://google.com')
.expect('Redirecting. Redirecting to http://google.com', done);
})
})
})
Loading