Skip to content
Open
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
145 changes: 68 additions & 77 deletions lib/response.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,107 +123,98 @@ res.links = function(links) {
*/

res.send = function send(body) {
var chunk = body;
var encoding;
var req = this.req;
var type;
const req = this.req;
const app = this.app;

// settings
var app = this.app;
let chunk = body;
let encoding;
let type;

switch (typeof chunk) {
// string defaulting to html
case 'string':
if (!this.get('Content-Type')) {
this.type('html');
}
break;
case 'boolean':
case 'number':
case 'object':
if (chunk === null) {
chunk = '';
} else if (ArrayBuffer.isView(chunk)) {
if (!this.get('Content-Type')) {
this.type('bin');
}
} else {
return this.json(chunk);
}
break;
if (typeof chunk === 'string') {
if (!this.get('Content-Type')) this.type('html');
encoding = 'utf8';
Comment on lines +134 to +135

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using curly braces would make it more consistent and readable.

} else if (typeof chunk === 'boolean' || typeof chunk === 'number' || chunk === null) {
chunk = chunk === null ? '' : chunk;
Comment on lines +136 to +137

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would make sense to split the null case from the boolean | number case to avoid using a ternary.

} else if (typeof chunk === 'object') {
if (ArrayBuffer.isView(chunk)) {
if (!this.get('Content-Type')) this.type('bin');
} else {
return this.json(chunk);
}
}

// write strings in utf-8
if (typeof chunk === 'string') {
encoding = 'utf8';
type = this.get('Content-Type');

// reflect this in content-type
if (typeof type === 'string') {
this.set('Content-Type', setCharset(type, 'utf-8'));
}
}

// determine if ETag should be generated
var etagFn = app.get('etag fn')
var generateETag = !this.get('ETag') && typeof etagFn === 'function'

// populate Content-Length
var len
if (chunk !== undefined) {
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
len = Buffer.byteLength(chunk, encoding)
} else {
// convert chunk to Buffer and calculate
chunk = Buffer.from(chunk, encoding)
encoding = undefined;
len = chunk.length
}

this.set('Content-Length', len);
}
chunk = prepareChunk(this, chunk, encoding);

// populate ETag
var etag;
if (generateETag && len !== undefined) {
if ((etag = etagFn(chunk, encoding))) {
this.set('ETag', etag);
}
}
setETagHeader(this, chunk, encoding);

// freshness
if (req.fresh) this.status(304);

// strip irrelevant headers
if (204 === this.statusCode || 304 === this.statusCode) {
this.removeHeader('Content-Type');
this.removeHeader('Content-Length');
this.removeHeader('Transfer-Encoding');
chunk = '';
}

// alter headers for 205
if (this.statusCode === 205) {
this.set('Content-Length', '0')
this.removeHeader('Transfer-Encoding')
chunk = ''
}
stripBodyIfNotAllowed(this);

if (req.method === 'HEAD') {
// skip body for HEAD
this.end();
} else {
// respond
this.end(chunk, encoding);
}

return this;
};

function prepareChunk(res, chunk, encoding) {
const app = res.app;
const etagFn = app.get('etag fn');
const shouldGenerateETag = !res.get('ETag') && typeof etagFn === 'function';

if (chunk === undefined) return chunk;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could even return earlier.


let len;

if (Buffer.isBuffer(chunk)) {
len = chunk.length;
} else if (!shouldGenerateETag && chunk.length < 1000) {
len = Buffer.byteLength(chunk, encoding);
} else {
chunk = Buffer.from(chunk, encoding);
encoding = undefined;
len = chunk.length;
}

res.set('Content-Length', len);
return chunk;
}

function setETagHeader(res, chunk, encoding) {
const etagFn = res.app.get('etag fn');
if (!res.get('ETag') && typeof etagFn === 'function' && chunk != null) {
const etag = etagFn(chunk, encoding);
if (etag) res.set('ETag', etag);
}
}

function stripBodyIfNotAllowed(res) {
const status = res.statusCode;

if (status === 204 || status === 304) {
res.removeHeader('Content-Type');
res.removeHeader('Content-Length');
res.removeHeader('Transfer-Encoding');
return res.end('');
}

if (status === 205) {
res.set('Content-Length', '0');
res.removeHeader('Transfer-Encoding');
return res.end('');
}
}


/**
* Send JSON response.
*
Expand Down