2015-02-10 19:41:24 +00:00
"use strict" ;
2015-05-15 10:39:42 +00:00
var errors = require ( "http-error" ) ;
2015-05-06 13:18:58 +00:00
2015-02-10 19:41:24 +00:00
plugin . consumes = [
"connect" ,
2015-09-22 14:46:28 +00:00
"connect.static" ,
2015-02-10 19:41:24 +00:00
"connect.render" ,
"connect.render.ejs"
] ;
plugin . provides = [
"connect.error"
] ;
module . exports = plugin ;
2015-09-22 14:46:28 +00:00
var errorPages = {
404 : 1 ,
401 : 1 ,
500 : 1 ,
503 : 1
} ;
2015-02-10 19:41:24 +00:00
var statusCodes = {
400 : "Bad Request" ,
401 : "Unauthorized" ,
402 : "Payment Required" ,
403 : "Forbidden" ,
404 : "Not Found" ,
405 : "Method Not Allowed" ,
406 : "Not Acceptable" ,
407 : "Proxy Authentication Required" ,
408 : "Request Timeout" ,
409 : "Conflict" ,
410 : "Gone" ,
411 : "Length Required" ,
412 : "Precondition Failed" ,
413 : "Request Entity Too Large" ,
414 : "Request-URI Too Long" ,
415 : "Unsupported Media Type" ,
416 : "Requested Range Not Satisfiable" ,
417 : "Expectation Failed" ,
418 : "I'm a Teapot" , // (RFC 2324) http://tools.ietf.org/html/rfc2324
420 : "Enhance Your Calm" , // Returned by the Twitter Search and Trends API when the client is being rate limited
422 : "Unprocessable Entity" , // (WebDAV) (RFC 4918)
423 : "Locked" , // (WebDAV) (RFC 4918)
424 : "Failed Dependency" , // (WebDAV) (RFC 4918)
425 : "Unordered Collection" , // (RFC 3648)
426 : "Upgrade Required" , // (RFC 2817)
428 : "Precondition Required" ,
429 : "Too Many Requests" , // Used for rate limiting
431 : "Request Header Fields Too Large" ,
444 : "No Response" , // An nginx HTTP server extension. The server returns no information to the client and closes the connection (useful as a deterrent for malware).
449 : "Retry With" , // A Microsoft extension. The request should be retried after performing the appropriate action.
450 : "Blocked By Windows Parental Controls" ,
499 : "Client Closed Request" ,
500 : "Internal Server Error" ,
501 : "Not Implemented" ,
502 : "Bad Gateway" ,
503 : "Service Unavailable" ,
504 : "Gateway Timeout" ,
505 : "HTTP Version Not Supported" ,
506 : "Variant Also Negotiates" ,
507 : "Insufficient Storage" ,
508 : "Loop Detected" ,
509 : "Bandwidth Limit Exceeded" ,
510 : "Not Extended" ,
511 : "Network Authentication Required"
} ;
2015-09-18 12:14:46 +00:00
var NICE _USER _ERROR _MSG = "Something went wrong. Please retry in a few minutes and contact support if it continues to occur" ;
2015-09-22 14:46:28 +00:00
2015-02-10 19:41:24 +00:00
function plugin ( options , imports , register ) {
var connect = imports . connect ;
var showStackTrace = false ;
var frontdoor = require ( "frontdoor" ) ;
var statics = imports [ "connect.static" ] ;
2015-09-22 14:46:28 +00:00
function isDev ( mode ) {
return /^(onlinedev|devel)$/ . test ( mode ) ;
}
2015-02-10 19:41:24 +00:00
// serve index.html
statics . addStatics ( [ {
path : _ _dirname + "/www" ,
mount : "/error_handler"
} ] ) ;
// make sure res.json is available
connect . useStart ( frontdoor . middleware . jsonWriter ( ) ) ;
2015-09-22 14:46:28 +00:00
function sanitizeErrorCode ( code ) {
code = parseInt ( code , 10 ) ;
if ( isNaN ( code ) ) return 500 ;
if ( code < 400 ) return 500 ;
2015-09-22 15:04:54 +00:00
2015-09-22 14:46:28 +00:00
return code ;
}
2015-02-10 19:41:24 +00:00
connect . useError ( function ( err , req , res , next ) {
2015-05-06 13:18:58 +00:00
if ( typeof err == "string" )
2015-05-15 10:39:42 +00:00
err = new errors . InternalServerError ( err ) ;
2015-02-10 19:41:24 +00:00
2015-09-22 14:46:28 +00:00
var statusCode = sanitizeErrorCode ( err . code || err . status || res . statusCode ) ;
var stack ;
2015-02-10 19:41:24 +00:00
2015-09-22 14:46:28 +00:00
if ( isDev ( options . mode ) )
stack = err . stack || err . message || err . toString ( ) ;
2015-09-23 09:33:49 +00:00
2015-02-10 19:41:24 +00:00
var accept = req . headers . accept || '' ;
2015-09-22 14:46:28 +00:00
if ( /json/ . test ( accept ) ) {
2015-02-10 19:41:24 +00:00
var error = {
2015-09-22 15:04:54 +00:00
code : statusCode ,
2015-02-10 19:41:24 +00:00
hostname : options . hostname ,
2015-09-22 14:46:28 +00:00
scope : options . scope ,
2015-10-05 14:40:27 +00:00
stack : stack
2015-02-10 19:41:24 +00:00
} ;
2015-10-05 14:40:27 +00:00
var allowedErrorKeys = [
2015-10-05 14:43:31 +00:00
"message" , "projectState" , "premium" , "retryIn" , "progress" ,
2015-12-21 14:28:47 +00:00
"oldHost" , "blocked" , "className" , "errors"
2015-10-05 14:40:27 +00:00
] ;
allowedErrorKeys . forEach ( function ( key ) {
if ( err . hasOwnProperty ( key ) )
error [ key ] = err [ key ] ;
} ) ;
2015-02-10 19:41:24 +00:00
2015-09-18 10:59:54 +00:00
try {
JSON . stringify ( error ) ;
2015-09-22 14:46:28 +00:00
}
catch ( e ) {
2015-09-18 10:59:54 +00:00
console . error ( "Cannot send error as JSON: " , error ) ;
2015-09-18 12:14:46 +00:00
error . message = NICE _USER _ERROR _MSG ;
2015-09-18 11:02:50 +00:00
error . scope = null ;
2015-09-18 10:59:54 +00:00
}
2015-09-22 14:46:28 +00:00
return res . json ( {
error : error
} , null , statusCode ) ;
2015-02-10 19:41:24 +00:00
}
2015-09-22 14:46:28 +00:00
var path = errorPages [ statusCode ] ? _ _dirname + "/views/error-" + statusCode + ".html.ejs" : _ _dirname + "/views/error.html.ejs" ;
res . setHeader ( 'Content-Type' , 'text/html; charset=utf-8' ) ;
2015-09-22 15:04:54 +00:00
2015-09-22 14:46:28 +00:00
res . render ( path , {
title : statusCodes [ statusCode ] || NICE _USER _ERROR _MSG ,
scope : options . scope || "" ,
showStackTrace : showStackTrace ,
2015-09-23 09:33:49 +00:00
stack : stack ,
2015-09-22 14:46:28 +00:00
statusCode : statusCode ,
error : err . toString ( )
} , next ) ;
2015-02-10 19:41:24 +00:00
} ) ;
register ( null , {
"connect.error" : { }
} ) ;
}