UNPKG

5.17 kBJavaScriptView Raw
1
2'use strict';
3
4/**
5 * Module dependencies.
6 */
7
8const util = require('util');
9const createError = require('http-errors');
10const httpAssert = require('http-assert');
11const delegate = require('delegates');
12const statuses = require('statuses');
13const Cookies = require('cookies');
14
15const COOKIES = Symbol('context#cookies');
16
17/**
18 * Context prototype.
19 */
20
21const proto = module.exports = {
22
23 /**
24 * util.inspect() implementation, which
25 * just returns the JSON output.
26 *
27 * @return {Object}
28 * @api public
29 */
30
31 inspect() {
32 if (this === proto) return this;
33 return this.toJSON();
34 },
35
36 /**
37 * Return JSON representation.
38 *
39 * Here we explicitly invoke .toJSON() on each
40 * object, as iteration will otherwise fail due
41 * to the getters and cause utilities such as
42 * clone() to fail.
43 *
44 * @return {Object}
45 * @api public
46 */
47
48 toJSON() {
49 return {
50 request: this.request.toJSON(),
51 response: this.response.toJSON(),
52 app: this.app.toJSON(),
53 originalUrl: this.originalUrl,
54 req: '<original node req>',
55 res: '<original node res>',
56 socket: '<original node socket>'
57 };
58 },
59
60 /**
61 * Similar to .throw(), adds assertion.
62 *
63 * this.assert(this.user, 401, 'Please login!');
64 *
65 * See: https://github.com/jshttp/http-assert
66 *
67 * @param {Mixed} test
68 * @param {Number} status
69 * @param {String} message
70 * @api public
71 */
72
73 assert: httpAssert,
74
75 /**
76 * Throw an error with `status` (default 500) and
77 * `msg`. Note that these are user-level
78 * errors, and the message may be exposed to the client.
79 *
80 * this.throw(403)
81 * this.throw(400, 'name required')
82 * this.throw('something exploded')
83 * this.throw(new Error('invalid'))
84 * this.throw(400, new Error('invalid'))
85 *
86 * See: https://github.com/jshttp/http-errors
87 *
88 * Note: `status` should only be passed as the first parameter.
89 *
90 * @param {String|Number|Error} err, msg or status
91 * @param {String|Number|Error} [err, msg or status]
92 * @param {Object} [props]
93 * @api public
94 */
95
96 throw(...args) {
97 throw createError(...args);
98 },
99
100 /**
101 * Default error handling.
102 *
103 * @param {Error} err
104 * @api private
105 */
106
107 onerror(err) {
108 // don't do anything if there is no error.
109 // this allows you to pass `this.onerror`
110 // to node-style callbacks.
111 if (null == err) return;
112
113 if (!(err instanceof Error)) err = new Error(util.format('non-error thrown: %j', err));
114
115 let headerSent = false;
116 if (this.headerSent || !this.writable) {
117 headerSent = err.headerSent = true;
118 }
119
120 // delegate
121 this.app.emit('error', err, this);
122
123 // nothing we can do here other
124 // than delegate to the app-level
125 // handler and log.
126 if (headerSent) {
127 return;
128 }
129
130 const { res } = this;
131
132 // first unset all headers
133 /* istanbul ignore else */
134 if (typeof res.getHeaderNames === 'function') {
135 res.getHeaderNames().forEach(name => res.removeHeader(name));
136 } else {
137 res._headers = {}; // Node < 7.7
138 }
139
140 // then set those specified
141 this.set(err.headers);
142
143 // force text/plain
144 this.type = 'text';
145
146 // ENOENT support
147 if ('ENOENT' == err.code) err.status = 404;
148
149 // default to 500
150 if ('number' != typeof err.status || !statuses[err.status]) err.status = 500;
151
152 // respond
153 const code = statuses[err.status];
154 const msg = err.expose ? err.message : code;
155 this.status = err.status;
156 this.length = Buffer.byteLength(msg);
157 res.end(msg);
158 },
159
160 get cookies() {
161 if (!this[COOKIES]) {
162 this[COOKIES] = new Cookies(this.req, this.res, {
163 keys: this.app.keys,
164 secure: this.request.secure
165 });
166 }
167 return this[COOKIES];
168 },
169
170 set cookies(_cookies) {
171 this[COOKIES] = _cookies;
172 }
173};
174
175/**
176 * Custom inspection implementation for newer Node.js versions.
177 *
178 * @return {Object}
179 * @api public
180 */
181
182/* istanbul ignore else */
183if (util.inspect.custom) {
184 module.exports[util.inspect.custom] = module.exports.inspect;
185}
186
187/**
188 * Response delegation.
189 */
190
191delegate(proto, 'response')
192 .method('attachment')
193 .method('redirect')
194 .method('remove')
195 .method('vary')
196 .method('has')
197 .method('set')
198 .method('append')
199 .method('flushHeaders')
200 .access('status')
201 .access('message')
202 .access('body')
203 .access('length')
204 .access('type')
205 .access('lastModified')
206 .access('etag')
207 .getter('headerSent')
208 .getter('writable');
209
210/**
211 * Request delegation.
212 */
213
214delegate(proto, 'request')
215 .method('acceptsLanguages')
216 .method('acceptsEncodings')
217 .method('acceptsCharsets')
218 .method('accepts')
219 .method('get')
220 .method('is')
221 .access('querystring')
222 .access('idempotent')
223 .access('socket')
224 .access('search')
225 .access('method')
226 .access('query')
227 .access('path')
228 .access('url')
229 .access('accept')
230 .getter('origin')
231 .getter('href')
232 .getter('subdomains')
233 .getter('protocol')
234 .getter('host')
235 .getter('hostname')
236 .getter('URL')
237 .getter('header')
238 .getter('headers')
239 .getter('secure')
240 .getter('stale')
241 .getter('fresh')
242 .getter('ips')
243 .getter('ip');