Skip to content

Commit

Permalink
Send error responses in the requested format
Browse files Browse the repository at this point in the history
Change-Id: I1efdf840dc28275cfa664fad6b82c0ba15c2c793
  • Loading branch information
arlolra committed Nov 4, 2016
1 parent 518f9bf commit 8357b71
Show file tree
Hide file tree
Showing 2 changed files with 130 additions and 101 deletions.
146 changes: 88 additions & 58 deletions lib/api/apiUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,98 +26,130 @@ var apiUtils = module.exports = { };
*
* @method
* @param {Response} res The response object from our routing function.
* @param {MWParserEnvironment} env
* @param {String} path
* @param {Number} [httpStatus]
*/
apiUtils.relativeRedirect = function(res, env, path, httpStatus) {
apiUtils.relativeRedirect = function(res, path, httpStatus) {
if (res.headersSent) { return; }
var args = [path];
if (typeof httpStatus === 'number') {
args.unshift(httpStatus);
}
if (env.responseSent) {
return;
} else {
res.redirect.apply(res, args);
}
res.redirect.apply(res, args);
};

/**
* Set header, but only if response hasn't been sent.
*
* @method
* @param {Response} res The response object from our routing function.
* @param {MWParserEnvironment} env
* @param {String} name
* @param {String|String[]} value
* @param {String} field
* @param {String} value
*/
apiUtils.setHeader = function(res, env, name, value) {
if (env.responseSent) {
return;
} else {
res.setHeader(name, value);
}
apiUtils.setHeader = function(res, field, value) {
if (res.headersSent) { return; }
res.set(field, value);
};

/**
* Send response, but only if response hasn't been sent.
* Send an html response, but only if response hasn't been sent.
*
* @method
* @param {Response} res The response object from our routing function.
* @param {MWParserEnvironment} env
* @param {String} body
* @param {Number} [status] HTTP status code
* @param {String} [contentType] A more specific type to use.
* @param {Boolean} [omitEscape] Be explicit about omitting escaping.
*/
apiUtils.sendResponse = function(res, env, body, status, omitEscape) {
if (env.responseSent) {
return;
} else {
env.responseSent = true;
if (status) {
res.status(status);
}
body = String(body);
if (!omitEscape) {
body = Util.entityEncodeAll(body);
}
res.send(body);
// Flush log buffer for linter
env.log('end/response');
apiUtils.htmlResponse = function(res, body, status, contentType, omitEscape) {
if (res.headersSent) { return; }
if (typeof status === 'number') {
res.status(status);
}
contentType = contentType || 'text/html; charset=utf-8';
console.assert(/^text\/html;/.test(contentType));
apiUtils.setHeader(res, 'content-type', contentType);
// Explicit cast, since express varies response encoding by argument type
// though that's probably offset by setting the header above
body = String(body);
if (!omitEscape) {
body = Util.entityEncodeAll(body);
}
res.send(body); // Default string encoding for send is text/html
};

/**
* Send a plaintext response, but only if response hasn't been sent.
*
* @method
* @param {Response} res The response object from our routing function.
* @param {String} text
* @param {Number} [status] HTTP status code
* @param {String} [contentType] A more specific type to use.
*/
apiUtils.plainResponse = function(res, text, status, contentType) {
if (res.headersSent) { return; }
if (typeof status === 'number') {
res.status(status);
}
contentType = contentType || 'text/plain; charset=utf-8';
console.assert(/^text\/plain;/.test(contentType));
apiUtils.setHeader(res, 'content-type', contentType);
// Explicit cast, since express varies response encoding by argument type
// though that's probably offset by setting the header above
res.send(String(text));
};

/**
* Send a JSON response, but only if response hasn't been sent.
*
* @method
* @param {Response} res The response object from our routing function.
* @param {Object} json
* @param {Number} [status] HTTP status code
* @param {String} [contentType] A more specific type to use.
*/
apiUtils.jsonResponse = function(res, json, status, contentType) {
if (res.headersSent) { return; }
if (typeof status === 'number') {
res.status(status);
}
contentType = contentType || 'application/json; charset=utf-8';
console.assert(/^application\/json;/.test(contentType));
apiUtils.setHeader(res, 'content-type', contentType);
res.json(json);
};

/**
* Render response, but only if response hasn't been sent.
*
* @method
* @param {Response} res The response object from our routing function.
* @param {MWParserEnvironment} env
* @param {String} view
* @param {Object} locals
*/
apiUtils.renderResponse = function(res, env, view, locals) {
if (env.responseSent) {
return;
} else {
env.responseSent = true;
res.render(view, locals);
}
apiUtils.renderResponse = function(res, view, locals) {
if (res.headersSent) { return; }
res.render(view, locals);
};

/**
* Send JSON response, but only if response hasn't been sent.
* Error response
*
* @method
* @param {Response} res The response object from our routing function.
* @param {MWParserEnvironment} env
* @param {Object} json
* @param {String} text
* @param {Number} [status]
*/
apiUtils.jsonResponse = function(res, env, json) {
if (env.responseSent) {
return;
} else {
env.responseSent = true;
res.json(json);
apiUtils.errorResponse = function(res, text, status) {
if (typeof status !== 'number') {
status = 500;
}
var enc = res.locals.errorEnc;
if (enc === 'json') {
text = { error: text };
}
apiUtils[enc + 'Response'](res, text, status);
};

/**
Expand All @@ -128,7 +160,6 @@ apiUtils.jsonResponse = function(res, env, json) {
* @param {MWParserEnvironment} env
* @param {Error} err
*/

apiUtils.timeoutResp = function(env, err) {
if (err instanceof Promise.TimeoutError) {
err = new Error('Request timed out.');
Expand All @@ -144,7 +175,7 @@ apiUtils.logTime = function(env, res, str) {
};

apiUtils.rtResponse = function(env, req, res, data) {
apiUtils.renderResponse(res, env, 'roundtrip', data);
apiUtils.renderResponse(res, 'roundtrip', data);
apiUtils.logTime(env, res, 'parsing');
};

Expand Down Expand Up @@ -427,8 +458,8 @@ apiUtils.redirectToOldid = function(req, res) {
metrics.increment('redirectToOldid.' + format.toLowerCase());
}
// Don't cache requests with no oldid
apiUtils.setHeader(res, env, 'Cache-Control', 'private,no-cache,s-maxage=0');
apiUtils.relativeRedirect(res, env, path);
apiUtils.setHeader(res, 'Cache-Control', 'private,no-cache,s-maxage=0');
apiUtils.relativeRedirect(res, path);
};

/**
Expand Down Expand Up @@ -511,10 +542,9 @@ apiUtils.wt2htmlRes = function(env, res, html, pb) {
body: pb.mw,
};
}
apiUtils.setHeader(res, env, 'content-type', apiUtils.pagebundleContentType(env));
apiUtils.jsonResponse(res, env, response);
apiUtils.jsonResponse(res, response, undefined, apiUtils.pagebundleContentType(env));
} else {
apiUtils.setHeader(res, env, 'content-type', apiUtils.htmlContentType(env));
apiUtils.sendResponse(res, env, html, undefined, true);
apiUtils.htmlResponse(res, html, undefined, apiUtils.htmlContentType(env), true);
}
env.log('end/response'); // Flush log buffer for linter
};
Loading

0 comments on commit 8357b71

Please sign in to comment.