nodester 0.1.5 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Readme.md +16 -55
- package/lib/application/index.js +174 -63
- package/lib/body/extract.js +15 -4
- package/lib/constants/Bounds.js +15 -0
- package/lib/constants/Clauses.js +13 -0
- package/lib/constants/ResponseFormats.js +2 -2
- package/lib/controllers/methods/index.js +7 -0
- package/lib/controllers/mixins/index.js +36 -36
- package/lib/database/connection.js +6 -0
- package/lib/database/migration.js +14 -4
- package/lib/facades/methods/index.js +10 -9
- package/lib/facades/mixins/index.js +67 -13
- package/lib/factories/responses/rest.js +25 -13
- package/lib/http/{request.js → request/index.js} +53 -75
- package/lib/http/request/utils.js +27 -0
- package/lib/http/response/headers.js +138 -0
- package/lib/http/response/index.js +248 -0
- package/lib/http/response/utils.js +38 -0
- package/lib/middlewares/SearchParams/index.js +25 -0
- package/lib/middlewares/cookies/index.js +44 -0
- package/lib/middlewares/etag/index.js +32 -15
- package/lib/middlewares/formidable/index.js +30 -25
- package/lib/middlewares/ql/sequelize/index.js +11 -2
- package/lib/middlewares/ql/sequelize/interpreter/QueryLexer.js +2 -1
- package/lib/middlewares/render/index.js +62 -0
- package/lib/models/associate.js +25 -1
- package/lib/models/define.js +19 -15
- package/lib/models/mixins.js +7 -0
- package/lib/query/traverse.js +96 -63
- package/lib/router/handlers.util.js +1 -0
- package/lib/router/index.js +193 -98
- package/lib/router/markers.js +7 -0
- package/lib/router/route.js +5 -0
- package/lib/router/routes.util.js +12 -14
- package/lib/router/utils.js +7 -0
- package/lib/stacks/MarkersStack.js +41 -3
- package/lib/stacks/MiddlewaresStack.js +200 -0
- package/lib/structures/Enum.js +46 -0
- package/lib/structures/Filter.js +156 -0
- package/lib/structures/Params.js +55 -0
- package/lib/tools/sql.tool.js +7 -0
- package/lib/utils/objects.util.js +31 -24
- package/lib/utils/sanitizations.util.js +10 -4
- package/lib/validators/arguments.js +68 -0
- package/lib/validators/dates.js +7 -0
- package/lib/validators/numbers.js +7 -0
- package/package.json +11 -8
- package/lib/database/utils.js +0 -19
- package/lib/enums/Enum.js +0 -16
- package/lib/filters/Filter.js +0 -109
- package/lib/http/response.js +0 -1074
- package/lib/http/utils.js +0 -254
- package/lib/params/Params.js +0 -37
- package/lib/policies/Role.js +0 -77
- package/lib/policies/RoleExtracting.js +0 -97
- package/lib/services/includes.service.js +0 -79
- package/lib/services/jwt.service.js +0 -147
- package/lib/stacks/MiddlewareStack.js +0 -159
|
@@ -2,17 +2,21 @@
|
|
|
2
2
|
* /nodester
|
|
3
3
|
* MIT Licensed
|
|
4
4
|
*/
|
|
5
|
+
|
|
5
6
|
'use strict';
|
|
6
7
|
|
|
8
|
+
const { IncomingMessage } = require('http');
|
|
9
|
+
|
|
7
10
|
const accepts = require('accepts');
|
|
8
11
|
const isIP = require('net').isIP;
|
|
9
12
|
const typeis = require('type-is');
|
|
10
|
-
const { IncomingMessage } = require('http');
|
|
11
13
|
const fresh = require('fresh');
|
|
12
14
|
const parseRange = require('range-parser');
|
|
13
15
|
const parse = require('parseurl');
|
|
14
16
|
const proxyaddr = require('proxy-addr');
|
|
15
17
|
|
|
18
|
+
const { defineGetter } = require('./utils');
|
|
19
|
+
|
|
16
20
|
|
|
17
21
|
const req = IncomingMessage.prototype;
|
|
18
22
|
|
|
@@ -38,8 +42,10 @@ module.exports = req;
|
|
|
38
42
|
* Aliased as `req.header()`.
|
|
39
43
|
*
|
|
40
44
|
* @param {String} name
|
|
45
|
+
*
|
|
41
46
|
* @return {String}
|
|
42
|
-
*
|
|
47
|
+
*
|
|
48
|
+
* @api public
|
|
43
49
|
*/
|
|
44
50
|
req.get =
|
|
45
51
|
req.header = function header(name) {
|
|
@@ -106,7 +112,8 @@ req.header = function header(name) {
|
|
|
106
112
|
*
|
|
107
113
|
* @param {String|Array} type(s)
|
|
108
114
|
* @return {String|Array|Boolean}
|
|
109
|
-
*
|
|
115
|
+
*
|
|
116
|
+
* @api public
|
|
110
117
|
*/
|
|
111
118
|
req.accepts = function() {
|
|
112
119
|
const accept = accepts(this);
|
|
@@ -119,7 +126,8 @@ req.accepts = function() {
|
|
|
119
126
|
*
|
|
120
127
|
* @param {String} ...encoding
|
|
121
128
|
* @return {String|Array}
|
|
122
|
-
*
|
|
129
|
+
*
|
|
130
|
+
* @api public
|
|
123
131
|
*/
|
|
124
132
|
req.acceptsEncodings = function() {
|
|
125
133
|
const accept = accepts(this);
|
|
@@ -133,7 +141,8 @@ req.acceptsEncodings = function() {
|
|
|
133
141
|
*
|
|
134
142
|
* @param {String} ...charset
|
|
135
143
|
* @return {String|Array}
|
|
136
|
-
*
|
|
144
|
+
*
|
|
145
|
+
* @api public
|
|
137
146
|
*/
|
|
138
147
|
req.acceptsCharsets = function() {
|
|
139
148
|
const accept = accepts(this);
|
|
@@ -147,7 +156,8 @@ req.acceptsCharsets = function() {
|
|
|
147
156
|
*
|
|
148
157
|
* @param {String} ...lang
|
|
149
158
|
* @return {String|Array}
|
|
150
|
-
*
|
|
159
|
+
*
|
|
160
|
+
* @api public
|
|
151
161
|
*/
|
|
152
162
|
req.acceptsLanguages = function() {
|
|
153
163
|
const accept = accepts(this);
|
|
@@ -176,8 +186,10 @@ req.acceptsLanguages = function() {
|
|
|
176
186
|
* @param {number} size
|
|
177
187
|
* @param {object} [options]
|
|
178
188
|
* @param {boolean} [options.combine=false]
|
|
189
|
+
*
|
|
179
190
|
* @return {number|array}
|
|
180
|
-
*
|
|
191
|
+
*
|
|
192
|
+
* @api public
|
|
181
193
|
*/
|
|
182
194
|
req.range = function range(size, options) {
|
|
183
195
|
const range = this.get('Range');
|
|
@@ -211,7 +223,8 @@ req.range = function range(size, options) {
|
|
|
211
223
|
*
|
|
212
224
|
* @param {String|Array} types...
|
|
213
225
|
* @return {String|false|null}
|
|
214
|
-
*
|
|
226
|
+
*
|
|
227
|
+
* @api public
|
|
215
228
|
*/
|
|
216
229
|
req.is = function is(types) {
|
|
217
230
|
let arr = types;
|
|
@@ -239,7 +252,8 @@ req.is = function is(types) {
|
|
|
239
252
|
* supplies https for you this may be enabled.
|
|
240
253
|
*
|
|
241
254
|
* @return {String}
|
|
242
|
-
*
|
|
255
|
+
*
|
|
256
|
+
* @api public
|
|
243
257
|
*/
|
|
244
258
|
|
|
245
259
|
defineGetter(req, 'protocol', function protocol() {
|
|
@@ -254,12 +268,13 @@ defineGetter(req, 'protocol', function protocol() {
|
|
|
254
268
|
|
|
255
269
|
// Note: X-Forwarded-Proto is normally only ever a
|
|
256
270
|
// single value, but this is to be safe.
|
|
257
|
-
const header = this.get('X-Forwarded-Proto') || proto
|
|
258
|
-
const index = header.indexOf(',')
|
|
271
|
+
const header = this.get('X-Forwarded-Proto') || proto;
|
|
272
|
+
const index = header.indexOf(',');
|
|
259
273
|
|
|
260
|
-
return index !== -1
|
|
261
|
-
|
|
262
|
-
|
|
274
|
+
return index !== -1 ?
|
|
275
|
+
header.substring(0, index).trim()
|
|
276
|
+
:
|
|
277
|
+
header.trim();
|
|
263
278
|
});
|
|
264
279
|
|
|
265
280
|
|
|
@@ -269,7 +284,8 @@ defineGetter(req, 'protocol', function protocol() {
|
|
|
269
284
|
* req.protocol === 'https'
|
|
270
285
|
*
|
|
271
286
|
* @return {Boolean}
|
|
272
|
-
*
|
|
287
|
+
*
|
|
288
|
+
* @api public
|
|
273
289
|
*/
|
|
274
290
|
defineGetter(req, 'secure', function secure() {
|
|
275
291
|
return this.protocol === 'https';
|
|
@@ -283,7 +299,8 @@ defineGetter(req, 'secure', function secure() {
|
|
|
283
299
|
* "trust proxy" is set.
|
|
284
300
|
*
|
|
285
301
|
* @return {String}
|
|
286
|
-
*
|
|
302
|
+
*
|
|
303
|
+
* @api public
|
|
287
304
|
*/
|
|
288
305
|
defineGetter(req, 'ip', function ip() {
|
|
289
306
|
const trust = this.app.get('trust proxy fn');
|
|
@@ -299,8 +316,9 @@ defineGetter(req, 'ip', function ip() {
|
|
|
299
316
|
* where "proxy2" is the furthest down-stream and "proxy1" and
|
|
300
317
|
* "proxy2" were trusted.
|
|
301
318
|
*
|
|
302
|
-
* @return {Array}
|
|
303
|
-
*
|
|
319
|
+
* @return {Array} addresses
|
|
320
|
+
*
|
|
321
|
+
* @api public
|
|
304
322
|
*/
|
|
305
323
|
defineGetter(req, 'ips', function ips() {
|
|
306
324
|
const trust = this.app.get('trust proxy fn');
|
|
@@ -308,37 +326,9 @@ defineGetter(req, 'ips', function ips() {
|
|
|
308
326
|
|
|
309
327
|
// reverse the order (to farthest -> closest)
|
|
310
328
|
// and remove socket address
|
|
311
|
-
|
|
329
|
+
addresses.reverse().pop()
|
|
312
330
|
|
|
313
|
-
return
|
|
314
|
-
});
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
/**
|
|
318
|
-
* Return subdomains as an array.
|
|
319
|
-
*
|
|
320
|
-
* Subdomains are the dot-separated parts of the host before the main domain of
|
|
321
|
-
* the app. By default, the domain of the app is assumed to be the last two
|
|
322
|
-
* parts of the host. This can be changed by setting "subdomain offset".
|
|
323
|
-
*
|
|
324
|
-
* For example, if the domain is "tobi.ferrets.example.com":
|
|
325
|
-
* If "subdomain offset" is not set, req.subdomains is `["ferrets", "tobi"]`.
|
|
326
|
-
* If "subdomain offset" is 3, req.subdomains is `["tobi"]`.
|
|
327
|
-
*
|
|
328
|
-
* @return {Array}
|
|
329
|
-
* @public
|
|
330
|
-
*/
|
|
331
|
-
defineGetter(req, 'subdomains', function subdomains() {
|
|
332
|
-
const hostname = this.hostname;
|
|
333
|
-
|
|
334
|
-
if (!hostname) return [];
|
|
335
|
-
|
|
336
|
-
const offset = this.app.get('subdomain offset');
|
|
337
|
-
const subdomains = !isIP(hostname)
|
|
338
|
-
? hostname.split('.').reverse()
|
|
339
|
-
: [hostname];
|
|
340
|
-
|
|
341
|
-
return subdomains.slice(offset);
|
|
331
|
+
return addresses;
|
|
342
332
|
});
|
|
343
333
|
|
|
344
334
|
|
|
@@ -346,7 +336,8 @@ defineGetter(req, 'subdomains', function subdomains() {
|
|
|
346
336
|
* Short-hand for `url.parse(req.url).pathname`.
|
|
347
337
|
*
|
|
348
338
|
* @return {String}
|
|
349
|
-
*
|
|
339
|
+
*
|
|
340
|
+
* @api public
|
|
350
341
|
*/
|
|
351
342
|
defineGetter(req, 'path', function path() {
|
|
352
343
|
return parse(this).pathname;
|
|
@@ -359,7 +350,8 @@ defineGetter(req, 'path', function path() {
|
|
|
359
350
|
* or "Host" as a fallback.
|
|
360
351
|
*
|
|
361
352
|
* @return {String}
|
|
362
|
-
*
|
|
353
|
+
*
|
|
354
|
+
* @api public
|
|
363
355
|
*/
|
|
364
356
|
defineGetter(req, 'hostname', function hostname() {
|
|
365
357
|
const host = this.get('X-Forwarded-Host') ?? this.get('Host');
|
|
@@ -373,23 +365,24 @@ defineGetter(req, 'hostname', function hostname() {
|
|
|
373
365
|
* still match.
|
|
374
366
|
*
|
|
375
367
|
* @return {Boolean}
|
|
376
|
-
*
|
|
368
|
+
*
|
|
369
|
+
* @api public
|
|
377
370
|
*/
|
|
378
371
|
defineGetter(req, 'fresh', function() {
|
|
379
372
|
const method = this.method;
|
|
380
|
-
const res = this.res
|
|
381
|
-
const status = res.statusCode
|
|
373
|
+
const res = this.res;
|
|
374
|
+
const status = res.statusCode;
|
|
382
375
|
|
|
383
376
|
// GET or HEAD for weak freshness validation only
|
|
384
|
-
if ('GET'
|
|
377
|
+
if (method !== 'GET' && method !== 'HEAD')
|
|
385
378
|
return false;
|
|
386
379
|
|
|
387
380
|
// 2xx or 304 as per rfc2616 14.26
|
|
388
381
|
if ((status >= 200 && status < 300) || 304 === status) {
|
|
389
382
|
return fresh(this.headers, {
|
|
390
|
-
'etag': res.get('
|
|
383
|
+
'etag': res.get('etag'),
|
|
391
384
|
'last-modified': res.get('Last-Modified')
|
|
392
|
-
})
|
|
385
|
+
});
|
|
393
386
|
}
|
|
394
387
|
|
|
395
388
|
return false;
|
|
@@ -402,7 +395,8 @@ defineGetter(req, 'fresh', function() {
|
|
|
402
395
|
* resource has changed.
|
|
403
396
|
*
|
|
404
397
|
* @return {Boolean}
|
|
405
|
-
*
|
|
398
|
+
*
|
|
399
|
+
* @api public
|
|
406
400
|
*/
|
|
407
401
|
defineGetter(req, 'stale', function stale() {
|
|
408
402
|
return !this.fresh;
|
|
@@ -413,26 +407,10 @@ defineGetter(req, 'stale', function stale() {
|
|
|
413
407
|
* Check if the request was an _XMLHttpRequest_.
|
|
414
408
|
*
|
|
415
409
|
* @return {Boolean}
|
|
416
|
-
*
|
|
410
|
+
*
|
|
411
|
+
* @api public
|
|
417
412
|
*/
|
|
418
413
|
defineGetter(req, 'xhr', function xhr() {
|
|
419
414
|
const val = this.get('X-Requested-With') || '';
|
|
420
415
|
return val.toLowerCase() === 'xmlhttprequest';
|
|
421
416
|
});
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
/**
|
|
425
|
-
* Helper function for creating a getter on an object.
|
|
426
|
-
*
|
|
427
|
-
* @param {Object} obj
|
|
428
|
-
* @param {String} name
|
|
429
|
-
* @param {Function} getter
|
|
430
|
-
* @private
|
|
431
|
-
*/
|
|
432
|
-
function defineGetter(obj, name, getter) {
|
|
433
|
-
Object.defineProperty(obj, name, {
|
|
434
|
-
configurable: true,
|
|
435
|
-
enumerable: true,
|
|
436
|
-
get: getter
|
|
437
|
-
});
|
|
438
|
-
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* /nodester
|
|
3
|
+
* MIT Licensed
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
module.exports = {
|
|
9
|
+
defineGetter: _defineGetter
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Helper function for creating a getter on an object.
|
|
14
|
+
*
|
|
15
|
+
* @param {Object} obj
|
|
16
|
+
* @param {String} name
|
|
17
|
+
* @param {Function} getter
|
|
18
|
+
*
|
|
19
|
+
* @api private
|
|
20
|
+
*/
|
|
21
|
+
function _defineGetter(obj, name, getter) {
|
|
22
|
+
Object.defineProperty(obj, name, {
|
|
23
|
+
configurable: true,
|
|
24
|
+
enumerable: true,
|
|
25
|
+
get: getter
|
|
26
|
+
});
|
|
27
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* /nodester
|
|
3
|
+
* MIT Licensed
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
// Constants.
|
|
9
|
+
const CHARSET_REGEX = /;\s*charset\s*=/;
|
|
10
|
+
|
|
11
|
+
// Utils:
|
|
12
|
+
const mime = require('mime');
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
module.exports = {
|
|
16
|
+
setHeader: _setHeader,
|
|
17
|
+
appendHeader: _appendHeader,
|
|
18
|
+
getHeader: _getHeader,
|
|
19
|
+
removeHeader: _removeHeader,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Set header `field` to `value`, or pass
|
|
24
|
+
* an object of header fields.
|
|
25
|
+
*
|
|
26
|
+
* Examples:
|
|
27
|
+
*
|
|
28
|
+
* res.set('Foo', ['bar', 'baz']);
|
|
29
|
+
* res.set('Accept', 'application/json');
|
|
30
|
+
* res.set({ Accept: 'text/plain', 'X-API-Key': 'tobi' });
|
|
31
|
+
*
|
|
32
|
+
* Aliased as `res.header()`.
|
|
33
|
+
*
|
|
34
|
+
* @param {String|Object} keyOrObject
|
|
35
|
+
* @param {String|Array} value
|
|
36
|
+
*
|
|
37
|
+
* @return {ServerResponse} for chaining
|
|
38
|
+
*
|
|
39
|
+
* @alias setHeader
|
|
40
|
+
* @api public
|
|
41
|
+
*/
|
|
42
|
+
function _setHeader(keyOrObject, value) {
|
|
43
|
+
// As a pair:
|
|
44
|
+
if (arguments.length === 2) {
|
|
45
|
+
let _value = Array.isArray(value) ? value.map(String) : String(value);
|
|
46
|
+
|
|
47
|
+
// Add charset to content-type:
|
|
48
|
+
if (keyOrObject.toLowerCase() === 'content-type') {
|
|
49
|
+
|
|
50
|
+
if (Array.isArray(_value)) {
|
|
51
|
+
throw new TypeError('Content-Type cannot be set to an Array');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (!CHARSET_REGEX.test(_value)) {
|
|
55
|
+
const charset = mime.charsets.getType(_value.split(';')[0]);
|
|
56
|
+
|
|
57
|
+
if (charset) {
|
|
58
|
+
_value += '; charset=' + charset.toLowerCase();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
this.setHeader(keyOrObject, _value);
|
|
64
|
+
}
|
|
65
|
+
// As an object
|
|
66
|
+
else if (typeof keyOrObject === 'object') {
|
|
67
|
+
for (let key in keyOrObject) {
|
|
68
|
+
this.setHeader(key, keyOrObject[key]);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
const err = new TypeError(`'keyOrObject' must be of type String|Object.`);
|
|
73
|
+
Error.captureStackTrace(err, _setHeader);
|
|
74
|
+
throw err;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return this;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Append additional header `field` with value `val`.
|
|
83
|
+
*
|
|
84
|
+
* Example:
|
|
85
|
+
*
|
|
86
|
+
* res.append.header('Link', ['<http://localhost/>', '<http://localhost:3000/>']);
|
|
87
|
+
* res.append.header('Set-Cookie', 'foo=bar; Path=/; HttpOnly');
|
|
88
|
+
* res.append.header('Warning', '199 Miscellaneous warning');
|
|
89
|
+
*
|
|
90
|
+
* @param {String} field
|
|
91
|
+
* @param {String|Array} value
|
|
92
|
+
* @return {ServerResponse} for chaining
|
|
93
|
+
*
|
|
94
|
+
* @alias appendHeader
|
|
95
|
+
* @api public
|
|
96
|
+
*/
|
|
97
|
+
function _appendHeader(field, value) {
|
|
98
|
+
const prev = this.get(field);
|
|
99
|
+
let _value = value;
|
|
100
|
+
|
|
101
|
+
if (prev) {
|
|
102
|
+
// concat the new and prev values:
|
|
103
|
+
_value = Array.isArray(prev) ? prev.concat(value)
|
|
104
|
+
: Array.isArray(value) ? [prev].concat(value)
|
|
105
|
+
: [prev, value]
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return this.setHeader(field, _value);
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Get value for header `key`.
|
|
114
|
+
*
|
|
115
|
+
* @param {String} key
|
|
116
|
+
*
|
|
117
|
+
* @return {String}
|
|
118
|
+
*
|
|
119
|
+
* @alias getHeader
|
|
120
|
+
* @api public
|
|
121
|
+
*/
|
|
122
|
+
function _getHeader(key) {
|
|
123
|
+
return this.getHeader(key);
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Remove value for header `key`.
|
|
128
|
+
*
|
|
129
|
+
* @param {String} key
|
|
130
|
+
*
|
|
131
|
+
* @return {String}
|
|
132
|
+
*
|
|
133
|
+
* @alias removeHeader
|
|
134
|
+
* @api public
|
|
135
|
+
*/
|
|
136
|
+
function _removeHeader(key) {
|
|
137
|
+
return this.removeHeader(key);
|
|
138
|
+
};
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* /nodester
|
|
3
|
+
* MIT Licensed
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
const { ServerResponse } = require('http');
|
|
9
|
+
|
|
10
|
+
const {
|
|
11
|
+
setHeader,
|
|
12
|
+
appendHeader,
|
|
13
|
+
getHeader,
|
|
14
|
+
removeHeader,
|
|
15
|
+
} = require('./headers');
|
|
16
|
+
|
|
17
|
+
const calculateEtag = require('etag');
|
|
18
|
+
|
|
19
|
+
// Utils:
|
|
20
|
+
const statuses = require('statuses');
|
|
21
|
+
const mime = require('mime');
|
|
22
|
+
const {
|
|
23
|
+
setCharset
|
|
24
|
+
} = require('./utils');
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
const response = ServerResponse.prototype;
|
|
28
|
+
|
|
29
|
+
// Mixin custom methods:
|
|
30
|
+
response.status = _status;
|
|
31
|
+
response.sendStatus = _sendStatus;
|
|
32
|
+
|
|
33
|
+
response.send = _send;
|
|
34
|
+
response.json = _json;
|
|
35
|
+
|
|
36
|
+
// Headers:
|
|
37
|
+
response.set = setHeader;
|
|
38
|
+
response.append = appendHeader;
|
|
39
|
+
response.get = getHeader;
|
|
40
|
+
response.remove = removeHeader;
|
|
41
|
+
|
|
42
|
+
// ContentType:
|
|
43
|
+
response.setContentType = _setContentType;
|
|
44
|
+
response.getContentType = _getContentType;
|
|
45
|
+
// Mixins\
|
|
46
|
+
|
|
47
|
+
module.exports = response;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Set status `code`.
|
|
51
|
+
*
|
|
52
|
+
* @param {Number} code
|
|
53
|
+
*
|
|
54
|
+
* @return {ServerResponse}
|
|
55
|
+
*
|
|
56
|
+
* @alias status
|
|
57
|
+
* @api public
|
|
58
|
+
*/
|
|
59
|
+
function _status(code) {
|
|
60
|
+
this.statusCode = code;
|
|
61
|
+
return this;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Send given HTTP status code.
|
|
67
|
+
*
|
|
68
|
+
* Sets the response status to `statusCode` and the body of the
|
|
69
|
+
* response to the standard description from node's http.STATUS_CODES
|
|
70
|
+
* or the statusCode number if no description.
|
|
71
|
+
*
|
|
72
|
+
* Examples:
|
|
73
|
+
*
|
|
74
|
+
* res.sendStatus(200);
|
|
75
|
+
*
|
|
76
|
+
* @param {number} statusCode
|
|
77
|
+
*
|
|
78
|
+
* @alias _sendStatus
|
|
79
|
+
* @api public
|
|
80
|
+
*/
|
|
81
|
+
function _sendStatus(statusCode) {
|
|
82
|
+
const body = statuses.message[statusCode] || String(statusCode);
|
|
83
|
+
|
|
84
|
+
this.statusCode = statusCode;
|
|
85
|
+
this.setContentType('txt');
|
|
86
|
+
|
|
87
|
+
return this.send(body);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Sends a response.
|
|
92
|
+
*
|
|
93
|
+
* Examples:
|
|
94
|
+
*
|
|
95
|
+
* res.send(Buffer.from('wahoo'));
|
|
96
|
+
* res.send({ some: 'json' });
|
|
97
|
+
* res.send('<p>some html</p>');
|
|
98
|
+
*
|
|
99
|
+
* @param {string|number|boolean|object|Buffer} body
|
|
100
|
+
*
|
|
101
|
+
* @alias send
|
|
102
|
+
* @api public
|
|
103
|
+
*/
|
|
104
|
+
function _send(body) {
|
|
105
|
+
const req = this.req;
|
|
106
|
+
const app = this.app;
|
|
107
|
+
|
|
108
|
+
let chunk = body;
|
|
109
|
+
let encoding;
|
|
110
|
+
let type;
|
|
111
|
+
|
|
112
|
+
switch (typeof chunk) {
|
|
113
|
+
// strings defaulting to html:
|
|
114
|
+
case 'string':
|
|
115
|
+
if (!this.getContentType()) {
|
|
116
|
+
this.setContentType('html');
|
|
117
|
+
}
|
|
118
|
+
break;
|
|
119
|
+
case 'boolean':
|
|
120
|
+
case 'number':
|
|
121
|
+
case 'object':
|
|
122
|
+
if (chunk === null) {
|
|
123
|
+
chunk = '';
|
|
124
|
+
}
|
|
125
|
+
else if (Buffer.isBuffer(chunk)) {
|
|
126
|
+
if (!this.getContentType()) {
|
|
127
|
+
this.setContentType('bin');
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
return this.json(chunk);
|
|
132
|
+
}
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Write strings in utf-8:
|
|
137
|
+
if (typeof chunk === 'string') {
|
|
138
|
+
encoding = 'utf8';
|
|
139
|
+
type = this.getContentType();
|
|
140
|
+
|
|
141
|
+
// reflect this in content-type
|
|
142
|
+
if (typeof type === 'string') {
|
|
143
|
+
this.setContentType( setCharset(type, 'utf-8') );
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Freshness of the REQUEST:
|
|
148
|
+
if (req.fresh) {
|
|
149
|
+
this.statusCode = 304;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Strip irrelevant headers for:
|
|
153
|
+
// - 204 (No Content)
|
|
154
|
+
// - 303 (Not Modified)
|
|
155
|
+
if (
|
|
156
|
+
this.statusCode === 204
|
|
157
|
+
||
|
|
158
|
+
this.statusCode === 304
|
|
159
|
+
) {
|
|
160
|
+
this.remove('Content-Type');
|
|
161
|
+
this.remove('Content-Length');
|
|
162
|
+
this.remove('Transfer-Encoding');
|
|
163
|
+
chunk = '';
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Alter headers for 205 (Reset Content):
|
|
167
|
+
if (this.statusCode === 205) {
|
|
168
|
+
this.set('Content-Length', '0');
|
|
169
|
+
this.remove('Transfer-Encoding');
|
|
170
|
+
chunk = ''
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (req.method === 'HEAD') {
|
|
174
|
+
// skip body for HEAD.
|
|
175
|
+
this.end();
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
// Calculate etag:
|
|
179
|
+
const etag = calculateEtag(chunk);
|
|
180
|
+
this.set('etag', etag);
|
|
181
|
+
|
|
182
|
+
// Respond.
|
|
183
|
+
this.end(chunk, encoding);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return this;
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Send JSON response.
|
|
192
|
+
*
|
|
193
|
+
* @param {string|number|boolean|object} obj
|
|
194
|
+
*
|
|
195
|
+
* @alias json
|
|
196
|
+
* @api public
|
|
197
|
+
*/
|
|
198
|
+
function _json(obj) {
|
|
199
|
+
|
|
200
|
+
// Ensure content-type:
|
|
201
|
+
if (!this.getContentType()) {
|
|
202
|
+
this.setContentType('application/json');
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const body = JSON.stringify(obj);
|
|
206
|
+
return this.send(body);
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Set _Content-Type_ response header with `type` through `mime.getType()`
|
|
211
|
+
* when it does not contain "/", or set the Content-Type to `type` otherwise.
|
|
212
|
+
*
|
|
213
|
+
* Examples:
|
|
214
|
+
*
|
|
215
|
+
* res.type('.html');
|
|
216
|
+
* res.type('html');
|
|
217
|
+
* res.type('json');
|
|
218
|
+
* res.type('application/json');
|
|
219
|
+
* res.type('png');
|
|
220
|
+
*
|
|
221
|
+
* @param {String} type
|
|
222
|
+
*
|
|
223
|
+
* @return {ServerResponse} for chaining
|
|
224
|
+
*
|
|
225
|
+
* @alias setContentType
|
|
226
|
+
* @api public
|
|
227
|
+
*/
|
|
228
|
+
function _setContentType(type) {
|
|
229
|
+
const contentType = type.indexOf('/') === -1 ?
|
|
230
|
+
mime.getType(type)
|
|
231
|
+
:
|
|
232
|
+
type;
|
|
233
|
+
|
|
234
|
+
this.setHeader('Content-Type', contentType);
|
|
235
|
+
return this;
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Returns _Content-Type_ header.
|
|
240
|
+
*
|
|
241
|
+
* @return {String} type
|
|
242
|
+
*
|
|
243
|
+
* @alias getContentType
|
|
244
|
+
* @api public
|
|
245
|
+
*/
|
|
246
|
+
function _getContentType() {
|
|
247
|
+
return this.getHeader('Content-Type');
|
|
248
|
+
};
|