UNPKG

4.09 kBJavaScriptView Raw
1/*
2 * Copyright (c) 2011-2013, Yahoo! Inc. All rights reserved.
3 * Copyrights licensed under the New BSD License.
4 * See the accompanying LICENSE file for terms.
5 */
6
7
8/*jslint anon:true, sloppy:true, nomen:true*/
9
10
11/**
12 * This is an object used as the single pathway for data to leave a mojit
13 * action execution. It is used as a component of the ActionContext object,
14 * which uses it to call <em>done</em> and <em>flush</em> in order to complete.
15 *
16 * There are two versions of this object, one for the client, and one for the
17 * server. This is the server version, which is more complex than the client
18 * version.
19 *
20 * @class OutputHandler
21 * @param {Object} req The Request object.
22 * @param {Object} res The Response object.
23 * @param {Function} next The next function, which should be invokable.
24 * @constructor
25 */
26var NAME = 'OutputHandler.server',
27 libutil = require('util'),
28 OutputHandler = function(req, res, next) {
29 this.req = req;
30 this.res = res;
31 this.next = next;
32 this.headers = {};
33 this.page = {};
34 };
35
36
37OutputHandler.prototype = {
38
39 setLogger: function(logger) {
40 this.logger = logger;
41 },
42
43
44 flush: function(data, meta) {
45 this._readMeta(meta);
46 this._writeHeaders();
47 this.res.write(data);
48 },
49
50
51 done: function(data, meta) {
52 var name,
53 obj,
54 size,
55 memDebug = {};
56
57 this.logger.log('done', 'info', NAME);
58 this._readMeta(meta);
59 this._writeHeaders();
60 if (!data ||
61 (typeof data !== 'string' && Object.keys(data).length === 0)) {
62 data = '';
63 }
64 this.res.end(data);
65 this.logger.log('END', 'mojito', NAME);
66 },
67
68
69 error: function(err) {
70 err = err || new Error('Unknown error occurred');
71 if (!err.code) {
72 err.code = 500;
73 }
74
75 if (err.code === 404) {
76 // FUTURE: [Issue 96] default Mojito 404 page
77 this.logger.log(err, 'warn', NAME);
78 } else {
79 this.logger.log(err, 'error', NAME);
80 }
81
82 var out = '<html>' +
83 '<body><h1>Error: ' + err.code + '</h1>' +
84 // The following line that includes the error message has been
85 // removed because the Paranoids don't want this data to be
86 // revealed in production environments. Once the bug having
87 // to do with different development environments has been
88 // fixed, we will be able to conditionally display the error
89 // details.
90
91 // "<p>" + err.message + "</p>" +
92 '<p>Error details are not available.</p>' +
93
94 '</body>' +
95 '</html>';
96 // TODO: [Issue 96] If YUI._mojito.DEBUG, add stack.
97
98 this.done(out, {
99 http: {
100 code: err.code,
101 reasonPhrase: err.reasonPhrase,
102 headers: {
103 'content-type': 'text/html'
104 }
105 }
106 });
107 },
108
109
110 _readMeta: function(meta) {
111 if (!meta || !meta.http) { return; }
112
113 var header;
114 for (header in meta.http.headers) {
115 if (meta.http.headers.hasOwnProperty(header)) {
116 this.headers[header] = meta.http.headers[header];
117 }
118 }
119
120 this.statusCode = meta.http.code;
121 this.reasonPhrase = meta.http.reasonPhrase;
122 },
123
124
125 _writeHeaders: function() {
126 if (!this.headersSent) {
127
128 // passing a falsy reason phrase would break, because node uses every non-string arguments[1]
129 // as header object/array
130 if (this.reasonPhrase && typeof this.reasonPhrase === 'string') {
131 this.res.writeHead(this.statusCode || 200, this.reasonPhrase, this.headers);
132 } else {
133 this.res.writeHead(this.statusCode || 200, this.headers);
134 }
135
136 this.headersSent = true;
137 }
138 }
139};
140
141
142/**
143 */
144module.exports = OutputHandler;