rc@73
|
1 /*!
|
rc@73
|
2 * errorhandler
|
rc@73
|
3 * Copyright(c) 2010 Sencha Inc.
|
rc@73
|
4 * Copyright(c) 2011 TJ Holowaychuk
|
rc@73
|
5 * MIT Licensed
|
rc@73
|
6 */
|
rc@73
|
7
|
rc@73
|
8 /**
|
rc@73
|
9 * Module dependencies.
|
rc@73
|
10 */
|
rc@73
|
11
|
rc@73
|
12 var accepts = require('accepts')
|
rc@73
|
13 var escapeHtml = require('escape-html');
|
rc@73
|
14 var fs = require('fs');
|
rc@73
|
15
|
rc@73
|
16 /**
|
rc@73
|
17 * Error handler:
|
rc@73
|
18 *
|
rc@73
|
19 * Development error handler, providing stack traces
|
rc@73
|
20 * and error message responses for requests accepting text, html,
|
rc@73
|
21 * or json.
|
rc@73
|
22 *
|
rc@73
|
23 * Text:
|
rc@73
|
24 *
|
rc@73
|
25 * By default, and when _text/plain_ is accepted a simple stack trace
|
rc@73
|
26 * or error message will be returned.
|
rc@73
|
27 *
|
rc@73
|
28 * JSON:
|
rc@73
|
29 *
|
rc@73
|
30 * When _application/json_ is accepted, connect will respond with
|
rc@73
|
31 * an object in the form of `{ "error": error }`.
|
rc@73
|
32 *
|
rc@73
|
33 * HTML:
|
rc@73
|
34 *
|
rc@73
|
35 * When accepted connect will output a nice html stack trace.
|
rc@73
|
36 *
|
rc@73
|
37 * @return {Function}
|
rc@73
|
38 * @api public
|
rc@73
|
39 */
|
rc@73
|
40
|
rc@73
|
41 exports = module.exports = function errorHandler(){
|
rc@73
|
42 // get environment
|
rc@73
|
43 var env = process.env.NODE_ENV || 'development'
|
rc@73
|
44
|
rc@73
|
45 return function errorHandler(err, req, res, next){
|
rc@73
|
46 // respect err.status
|
rc@73
|
47 if (err.status) {
|
rc@73
|
48 res.statusCode = err.status
|
rc@73
|
49 }
|
rc@73
|
50
|
rc@73
|
51 // default status code to 500
|
rc@73
|
52 if (res.statusCode < 400) {
|
rc@73
|
53 res.statusCode = 500
|
rc@73
|
54 }
|
rc@73
|
55
|
rc@73
|
56 // write error to console
|
rc@73
|
57 if (env !== 'test') {
|
rc@73
|
58 console.error(err.stack || String(err))
|
rc@73
|
59 }
|
rc@73
|
60
|
rc@73
|
61 // cannot actually respond
|
rc@73
|
62 if (res._header) {
|
rc@73
|
63 return req.socket.destroy()
|
rc@73
|
64 }
|
rc@73
|
65
|
rc@73
|
66 // negotiate
|
rc@73
|
67 var accept = accepts(req)
|
rc@73
|
68 var type = accept.types('html', 'json', 'text')
|
rc@73
|
69
|
rc@73
|
70 // Security header for content sniffing
|
rc@73
|
71 res.setHeader('X-Content-Type-Options', 'nosniff')
|
rc@73
|
72
|
rc@73
|
73 // html
|
rc@73
|
74 if (type === 'html') {
|
rc@73
|
75 fs.readFile(__dirname + '/public/style.css', 'utf8', function(e, style){
|
rc@73
|
76 if (e) return next(e);
|
rc@73
|
77 fs.readFile(__dirname + '/public/error.html', 'utf8', function(e, html){
|
rc@73
|
78 if (e) return next(e);
|
rc@73
|
79 var stack = (err.stack || '')
|
rc@73
|
80 .split('\n').slice(1)
|
rc@73
|
81 .map(function(v){ return '<li>' + escapeHtml(v).replace(/ /g, ' ') + '</li>'; }).join('');
|
rc@73
|
82 html = html
|
rc@73
|
83 .replace('{style}', style)
|
rc@73
|
84 .replace('{stack}', stack)
|
rc@73
|
85 .replace('{title}', escapeHtml(exports.title))
|
rc@73
|
86 .replace('{statusCode}', res.statusCode)
|
rc@73
|
87 .replace(/\{error\}/g, escapeHtml(String(err)).replace(/ /g, ' ').replace(/\n/g, '<br>'));
|
rc@73
|
88 res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
rc@73
|
89 res.end(html);
|
rc@73
|
90 });
|
rc@73
|
91 });
|
rc@73
|
92 // json
|
rc@73
|
93 } else if (type === 'json') {
|
rc@73
|
94 var error = { message: err.message, stack: err.stack };
|
rc@73
|
95 for (var prop in err) error[prop] = err[prop];
|
rc@73
|
96 var json = JSON.stringify({ error: error });
|
rc@73
|
97 res.setHeader('Content-Type', 'application/json');
|
rc@73
|
98 res.end(json);
|
rc@73
|
99 // plain text
|
rc@73
|
100 } else {
|
rc@73
|
101 res.setHeader('Content-Type', 'text/plain');
|
rc@73
|
102 res.end(err.stack || String(err));
|
rc@73
|
103 }
|
rc@73
|
104 };
|
rc@73
|
105 };
|
rc@73
|
106
|
rc@73
|
107 /**
|
rc@73
|
108 * Template title, framework authors may override this value.
|
rc@73
|
109 */
|
rc@73
|
110
|
rc@73
|
111 exports.title = 'Connect';
|