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 | */
|
26 | var 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 |
|
37 | OutputHandler.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 | */
|
144 | module.exports = OutputHandler;
|