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.
Files changed (58) hide show
  1. package/Readme.md +16 -55
  2. package/lib/application/index.js +174 -63
  3. package/lib/body/extract.js +15 -4
  4. package/lib/constants/Bounds.js +15 -0
  5. package/lib/constants/Clauses.js +13 -0
  6. package/lib/constants/ResponseFormats.js +2 -2
  7. package/lib/controllers/methods/index.js +7 -0
  8. package/lib/controllers/mixins/index.js +36 -36
  9. package/lib/database/connection.js +6 -0
  10. package/lib/database/migration.js +14 -4
  11. package/lib/facades/methods/index.js +10 -9
  12. package/lib/facades/mixins/index.js +67 -13
  13. package/lib/factories/responses/rest.js +25 -13
  14. package/lib/http/{request.js → request/index.js} +53 -75
  15. package/lib/http/request/utils.js +27 -0
  16. package/lib/http/response/headers.js +138 -0
  17. package/lib/http/response/index.js +248 -0
  18. package/lib/http/response/utils.js +38 -0
  19. package/lib/middlewares/SearchParams/index.js +25 -0
  20. package/lib/middlewares/cookies/index.js +44 -0
  21. package/lib/middlewares/etag/index.js +32 -15
  22. package/lib/middlewares/formidable/index.js +30 -25
  23. package/lib/middlewares/ql/sequelize/index.js +11 -2
  24. package/lib/middlewares/ql/sequelize/interpreter/QueryLexer.js +2 -1
  25. package/lib/middlewares/render/index.js +62 -0
  26. package/lib/models/associate.js +25 -1
  27. package/lib/models/define.js +19 -15
  28. package/lib/models/mixins.js +7 -0
  29. package/lib/query/traverse.js +96 -63
  30. package/lib/router/handlers.util.js +1 -0
  31. package/lib/router/index.js +193 -98
  32. package/lib/router/markers.js +7 -0
  33. package/lib/router/route.js +5 -0
  34. package/lib/router/routes.util.js +12 -14
  35. package/lib/router/utils.js +7 -0
  36. package/lib/stacks/MarkersStack.js +41 -3
  37. package/lib/stacks/MiddlewaresStack.js +200 -0
  38. package/lib/structures/Enum.js +46 -0
  39. package/lib/structures/Filter.js +156 -0
  40. package/lib/structures/Params.js +55 -0
  41. package/lib/tools/sql.tool.js +7 -0
  42. package/lib/utils/objects.util.js +31 -24
  43. package/lib/utils/sanitizations.util.js +10 -4
  44. package/lib/validators/arguments.js +68 -0
  45. package/lib/validators/dates.js +7 -0
  46. package/lib/validators/numbers.js +7 -0
  47. package/package.json +11 -8
  48. package/lib/database/utils.js +0 -19
  49. package/lib/enums/Enum.js +0 -16
  50. package/lib/filters/Filter.js +0 -109
  51. package/lib/http/response.js +0 -1074
  52. package/lib/http/utils.js +0 -254
  53. package/lib/params/Params.js +0 -37
  54. package/lib/policies/Role.js +0 -77
  55. package/lib/policies/RoleExtracting.js +0 -97
  56. package/lib/services/includes.service.js +0 -79
  57. package/lib/services/jwt.service.js +0 -147
  58. package/lib/stacks/MiddlewareStack.js +0 -159
@@ -1,1074 +0,0 @@
1
- /*!
2
- * /nodester
3
- * MIT Licensed
4
- */
5
- 'use strict';
6
-
7
- // Constants.
8
- const charsetRegExp = /;\s*charset\s*=/;
9
-
10
- // Utils:
11
- const contentDisposition = require('content-disposition');
12
- const createError = require('http-errors')
13
- const encodeUrl = require('encodeurl');
14
- const escapeHtml = require('escape-html');
15
- const { ServerResponse } = require('http');
16
- const onFinished = require('on-finished');
17
- const statuses = require('statuses')
18
- const sign = require('cookie-signature').sign;
19
- const cookie = require('cookie');
20
- const send = require('send');
21
- const mime = send.mime;
22
- const vary = require('vary');
23
- const {
24
- normalizeType,
25
- normalizeTypes,
26
- setCharset
27
- } = require('./utils');
28
- const path = require('path');
29
- const extname = path.extname;
30
- const resolve = path.resolve;
31
- const { isAbsolute } = require('../utils/path.util');
32
-
33
-
34
- const res = ServerResponse.prototype;
35
- module.exports = res;
36
-
37
- /**
38
- * Set status `code`.
39
- *
40
- * @param {Number} code
41
- * @return {ServerResponse}
42
- *
43
- * @public
44
- */
45
- res.status = function status(code) {
46
- this.statusCode = code;
47
- return this;
48
- };
49
-
50
-
51
- /**
52
- * Set Link header field with the given `links`.
53
- *
54
- * Examples:
55
- *
56
- * res.links({
57
- * next: 'http://api.example.com/users?page=2',
58
- * last: 'http://api.example.com/users?page=5'
59
- * });
60
- *
61
- * @param {Object} links
62
- * @return {ServerResponse}
63
- *
64
- * @public
65
- */
66
- res.links = function(links){
67
- const link = this.get('Link') || '';
68
-
69
- if (link) {
70
- link += ', ';
71
- }
72
-
73
- const linkRel = Object.keys(links)
74
- .map((rel) => '<' + links[rel] + '>; rel="' + rel + '"')
75
- .join(', ');
76
- this.set('Link', link + linkRel);
77
- };
78
-
79
-
80
- /**
81
- * Send a response.
82
- *
83
- * Examples:
84
- *
85
- * res.send(Buffer.from('wahoo'));
86
- * res.send({ some: 'json' });
87
- * res.send('<p>some html</p>');
88
- *
89
- * @param {string|number|boolean|object|Buffer} body
90
- *
91
- * @public
92
- */
93
- res.send = function send(body) {
94
- const req = this.req;
95
-
96
- let chunk = body;
97
- let encoding;
98
- let type;
99
-
100
- // Settings.
101
- const app = this.app;
102
-
103
- switch (typeof chunk) {
104
- // string defaulting to html
105
- case 'string':
106
- if (!this.get('Content-Type')) {
107
- this.type('html');
108
- }
109
- break;
110
- case 'boolean':
111
- case 'number':
112
- case 'object':
113
- if (chunk === null) {
114
- chunk = '';
115
- }
116
- else if (Buffer.isBuffer(chunk)) {
117
- if (!this.get('Content-Type')) {
118
- this.type('bin');
119
- }
120
- }
121
- else {
122
- return this.json(chunk);
123
- }
124
- break;
125
- }
126
-
127
- // write strings in utf-8
128
- if (typeof chunk === 'string') {
129
- encoding = 'utf8';
130
- type = this.get('Content-Type');
131
-
132
- // reflect this in content-type
133
- if (typeof type === 'string') {
134
- this.set('Content-Type', setCharset(type, 'utf-8'));
135
- }
136
- }
137
-
138
- // freshness:
139
- // if (req.fresh) {
140
- // this.statusCode = 304;
141
- // }
142
-
143
- // Strip irrelevant headers for:
144
- // - 204 (No Content)
145
- // - 303 (Not Modified)
146
- if (
147
- this.statusCode === 204
148
- ||
149
- this.statusCode === 304
150
- ) {
151
- this.removeHeader('Content-Type');
152
- this.removeHeader('Content-Length');
153
- this.removeHeader('Transfer-Encoding');
154
- chunk = '';
155
- }
156
-
157
- // Alter headers for 205 (Reset Content):
158
- if (this.statusCode === 205) {
159
- this.set('Content-Length', '0')
160
- this.removeHeader('Transfer-Encoding')
161
- chunk = ''
162
- }
163
-
164
- if (req.method === 'HEAD') {
165
- // skip body for HEAD.
166
- this.end();
167
- }
168
- else {
169
- // Respond.
170
- this.end(chunk, encoding);
171
- }
172
-
173
- return this;
174
- };
175
-
176
-
177
- /**
178
- * Send JSON response.
179
- *
180
- * Examples:
181
- *
182
- * res.json(null);
183
- * res.json({ user: 'tj' });
184
- *
185
- * @param {string|number|boolean|object} obj
186
- *
187
- * @public
188
- */
189
- res.json = function json(obj) {
190
-
191
- // If content-type not set:
192
- if (!this.get('Content-Type')) {
193
- this.set('Content-Type', 'application/json');
194
- }
195
-
196
- const body = JSON.stringify(obj);
197
- return this.send(body);
198
- };
199
-
200
-
201
- /**
202
- * Send JSON response with JSONP callback support.
203
- *
204
- * Examples:
205
- *
206
- * res.jsonp(null);
207
- * res.jsonp({ user: 'tj' });
208
- *
209
- * @param {string|number|boolean|object} obj
210
- *
211
- * @public
212
- */
213
- res.jsonp = function jsonp(val) {
214
- const app = this.app;
215
- // Settings:
216
- const escape = app.get('json escape')
217
- const replacer = app.get('json replacer');
218
- const spaces = app.get('json spaces');
219
-
220
- let body = stringify(val, replacer, spaces, escape)
221
- let callback = this.req.query[app.get('jsonp callback name')];
222
-
223
- // content-type
224
- if (!this.get('Content-Type')) {
225
- this.set('X-Content-Type-Options', 'nosniff');
226
- this.set('Content-Type', 'application/json');
227
- }
228
-
229
- // fixup callback
230
- if (Array.isArray(callback)) {
231
- callback = callback[0];
232
- }
233
-
234
- // jsonp
235
- if (typeof callback === 'string' && callback.length !== 0) {
236
- this.set('X-Content-Type-Options', 'nosniff');
237
- this.set('Content-Type', 'text/javascript');
238
-
239
- // restrict callback charset
240
- callback = callback.replace(/[^\[\]\w$.]/g, '');
241
-
242
- if (body === undefined) {
243
- // empty argument.
244
- body = '';
245
- }
246
- else if (typeof body === 'string') {
247
- // replace chars not allowed in JavaScript that are in JSON.
248
- body = body.replace(/\u2028/g, '\\u2028')
249
- .replace(/\u2029/g, '\\u2029');
250
- }
251
-
252
- // the /**/ is a specific security mitigation for "Rosetta Flash JSONP abuse"
253
- // the typeof check is just to reduce client error noise
254
- body = '/**/ typeof ' + callback + ' === \'function\' && ' + callback + '(' + body + ');';
255
- }
256
-
257
- return this.send(body);
258
- };
259
-
260
-
261
- /**
262
- * Send given HTTP status code.
263
- *
264
- * Sets the response status to `statusCode` and the body of the
265
- * response to the standard description from node's http.STATUS_CODES
266
- * or the statusCode number if no description.
267
- *
268
- * Examples:
269
- *
270
- * res.sendStatus(200);
271
- *
272
- * @param {number} statusCode
273
- *
274
- * @public
275
- */
276
- res.sendStatus = function sendStatus(statusCode) {
277
- const body = statuses.message[statusCode] || String(statusCode)
278
-
279
- this.statusCode = statusCode;
280
- this.type('txt');
281
-
282
- return this.send(body);
283
- };
284
-
285
-
286
- /**
287
- * Transfer the file at the given `path`.
288
- *
289
- * Automatically sets the _Content-Type_ response header field.
290
- * The callback `callback(err)` is invoked when the transfer is complete
291
- * or when an error occurs. Be sure to check `res.headersSent`
292
- * if you wish to attempt responding, as the header and some data
293
- * may have already been transferred.
294
- *
295
- * Options:
296
- *
297
- * - `maxAge` defaulting to 0 (can be string converted by `ms`)
298
- * - `root` root directory for relative filenames
299
- * - `headers` object of headers to serve with file
300
- * - `dotfiles` serve dotfiles, defaulting to false; can be `"allow"` to send them
301
- *
302
- * Other options are passed along to `send`.
303
- *
304
- * Examples:
305
- *
306
- * The following example illustrates how `res.sendFile()` may
307
- * be used as an alternative for the `static()` middleware for
308
- * dynamic situations. The code backing `res.sendFile()` is actually
309
- * the same code, so HTTP cache support etc is identical.
310
- *
311
- * app.get('/user/:uid/photos/:file', function(req, res){
312
- * var uid = req.params.uid
313
- * , file = req.params.file;
314
- *
315
- * req.user.mayViewFilesFrom(uid, function(yes){
316
- * if (yes) {
317
- * res.sendFile('/uploads/' + uid + '/' + file);
318
- * }
319
- else {
320
- * res.send(403, 'Sorry! you cant see that.');
321
- * }
322
- * });
323
- * });
324
- *
325
- * @public
326
- */
327
- res.sendFile = function sendFile(path, options, callback) {
328
- const req = this.req;
329
- const res = this;
330
- const next = req.next;
331
-
332
- let opts = options || {};
333
- let done = callback;
334
-
335
- if (!path) {
336
- throw new TypeError('path argument is required to res.sendFile');
337
- }
338
-
339
- if (typeof path !== 'string') {
340
- throw new TypeError('path must be a string to res.sendFile')
341
- }
342
-
343
- // support function as second arg
344
- if (typeof options === 'function') {
345
- done = options;
346
- opts = {};
347
- }
348
-
349
- if (!opts.root && !isAbsolute(path)) {
350
- throw new TypeError('path must be absolute or specify root to res.sendFile');
351
- }
352
-
353
- // create file stream
354
- const pathname = encodeURI(path);
355
- const file = send(req, pathname, opts);
356
-
357
- // transfer
358
- sendfile(res, file, opts, function (err) {
359
- if (done)
360
- return done(err);
361
-
362
- if (err && err.code === 'EISDIR')
363
- return next();
364
-
365
- // next() all but write errors
366
- if (err && err.code !== 'ECONNABORTED' && err.syscall !== 'write') {
367
- next(err);
368
- }
369
- });
370
- };
371
-
372
-
373
- /**
374
- * Transfer the file at the given `path`.
375
- *
376
- * Automatically sets the _Content-Type_ response header field.
377
- * The callback `callback(err)` is invoked when the transfer is complete
378
- * or when an error occurs. Be sure to check `res.headersSent`
379
- * if you wish to attempt responding, as the header and some data
380
- * may have already been transferred.
381
- *
382
- * Options:
383
- *
384
- * - `maxAge` defaulting to 0 (can be string converted by `ms`)
385
- * - `root` root directory for relative filenames
386
- * - `headers` object of headers to serve with file
387
- * - `dotfiles` serve dotfiles, defaulting to false; can be `"allow"` to send them
388
- *
389
- * Other options are passed along to `send`.
390
- *
391
- * Examples:
392
- *
393
- * The following example illustrates how `res.sendfile()` may
394
- * be used as an alternative for the `static()` middleware for
395
- * dynamic situations. The code backing `res.sendfile()` is actually
396
- * the same code, so HTTP cache support etc is identical.
397
- *
398
- * app.get('/user/:uid/photos/:file', function(req, res){
399
- * var uid = req.params.uid
400
- * , file = req.params.file;
401
- *
402
- * req.user.mayViewFilesFrom(uid, function(yes){
403
- * if (yes) {
404
- * res.sendfile('/uploads/' + uid + '/' + file);
405
- * }
406
- else {
407
- * res.send(403, 'Sorry! you cant see that.');
408
- * }
409
- * });
410
- * });
411
- *
412
- * @public
413
- */
414
- res.sendfile = function (path, options, callback) {
415
- const req = this.req;
416
- const res = this;
417
- const next = req.next;
418
-
419
- let opts = options || {};
420
- let done = callback;
421
-
422
- // support function as second arg
423
- if (typeof options === 'function') {
424
- done = options;
425
- opts = {};
426
- }
427
-
428
- // create file stream
429
- const file = send(req, path, opts);
430
-
431
- // transfer
432
- sendfile(res, file, opts, function (err) {
433
- if (done)
434
- return done(err);
435
-
436
- if (err && err.code === 'EISDIR')
437
- return next();
438
-
439
- // next() all but write errors
440
- if (err && err.code !== 'ECONNABORTED' && err.syscall !== 'write') {
441
- next(err);
442
- }
443
- });
444
- };
445
-
446
-
447
- /**
448
- * Transfer the file at the given `path` as an attachment.
449
- *
450
- * Optionally providing an alternate attachment `filename`,
451
- * and optional callback `callback(err)`. The callback is invoked
452
- * when the data transfer is complete, or when an error has
453
- * occurred. Be sure to check `res.headersSent` if you plan to respond.
454
- *
455
- * Optionally providing an `options` object to use with `res.sendFile()`.
456
- * This function will set the `Content-Disposition` header, overriding
457
- * any `Content-Disposition` header passed as header options in order
458
- * to set the attachment and filename.
459
- *
460
- * This method uses `res.sendFile()`.
461
- *
462
- * @public
463
- */
464
- res.download = function download (path, filename, options, callback) {
465
- let done = callback;
466
- let name = filename;
467
- let opts = options || null
468
-
469
- // support function as second or third arg
470
- if (typeof filename === 'function') {
471
- done = filename;
472
- name = null;
473
- opts = null
474
- }
475
- else if (typeof options === 'function') {
476
- done = options
477
- opts = null
478
- }
479
-
480
- // support optional filename, where options may be in it's place
481
- if (typeof filename === 'object' &&
482
- (typeof options === 'function' || options === undefined)) {
483
- name = null
484
- opts = filename
485
- }
486
-
487
- // set Content-Disposition when file is sent
488
- const headers = {
489
- 'Content-Disposition': contentDisposition(name || path)
490
- };
491
-
492
- // merge user-provided headers
493
- if (opts && opts.headers) {
494
- const keys = Object.keys(opts.headers)
495
- for (let i = 0; i < keys.length; i++) {
496
- const key = keys[i]
497
- if (key.toLowerCase() !== 'content-disposition') {
498
- headers[key] = opts.headers[key]
499
- }
500
- }
501
- }
502
-
503
- // merge user-provided options
504
- opts = Object.create(opts)
505
- opts.headers = headers
506
-
507
- // Resolve the full path for sendFile
508
- const fullPath = !opts.root ?
509
- resolve(path)
510
- :
511
- path;
512
-
513
- // send file
514
- return this.sendFile(fullPath, opts, done)
515
- };
516
-
517
-
518
- /**
519
- * Set _Content-Type_ response header with `type` through `mime.lookup()`
520
- * when it does not contain "/", or set the Content-Type to `type` otherwise.
521
- *
522
- * Examples:
523
- *
524
- * res.type('.html');
525
- * res.type('html');
526
- * res.type('json');
527
- * res.type('application/json');
528
- * res.type('png');
529
- *
530
- * @param {String} type
531
- * @return {ServerResponse} for chaining
532
- *
533
- * @public
534
- */
535
- res.contentType =
536
- res.type = function contentType(type) {
537
- const _contentType = type.indexOf('/') === -1 ?
538
- mime.lookup(type)
539
- :
540
- type;
541
-
542
- return this.set('Content-Type', _contentType);
543
- };
544
-
545
-
546
- /**
547
- * Respond to the Acceptable formats using an `obj`
548
- * of mime-type callbacks.
549
- *
550
- * This method uses `req.accepted`, an array of
551
- * acceptable types ordered by their quality values.
552
- * When "Accept" is not present the _first_ callback
553
- * is invoked, otherwise the first match is used. When
554
- * no match is performed the server responds with
555
- * 406 "Not Acceptable".
556
- *
557
- * Content-Type is set for you, however if you choose
558
- * you may alter this within the callback using `res.type()`
559
- * or `res.set('Content-Type', ...)`.
560
- *
561
- * res.format({
562
- * 'text/plain': function(){
563
- * res.send('hey');
564
- * },
565
- *
566
- * 'text/html': function(){
567
- * res.send('<p>hey</p>');
568
- * },
569
- *
570
- * 'application/json': function () {
571
- * res.send({ message: 'hey' });
572
- * }
573
- * });
574
- *
575
- * In addition to canonicalized MIME types you may
576
- * also use extnames mapped to these types:
577
- *
578
- * res.format({
579
- * text: function(){
580
- * res.send('hey');
581
- * },
582
- *
583
- * html: function(){
584
- * res.send('<p>hey</p>');
585
- * },
586
- *
587
- * json: function(){
588
- * res.send({ message: 'hey' });
589
- * }
590
- * });
591
- *
592
- * By default Express passes an `Error`
593
- * with a `.status` of 406 to `next(err)`
594
- * if a match is not made. If you provide
595
- * a `.default` callback it will be invoked
596
- * instead.
597
- *
598
- * @param {Object} obj
599
- * @return {ServerResponse} for chaining
600
- *
601
- * @public
602
- */
603
- res.format = function(obj){
604
- const req = this.req;
605
- const next = req.next;
606
-
607
- const keys = Object.keys(obj)
608
- .filter((v) => v !== 'default');
609
-
610
- const key = keys.length > 0 ?
611
- req.accepts(keys)
612
- :
613
- false;
614
-
615
- this.vary('Accept');
616
-
617
- if (key) {
618
- this.set('Content-Type', normalizeType(key).value);
619
- obj[key](req, this, next);
620
- }
621
- else if (obj.default) {
622
- obj.default(req, this, next)
623
- }
624
- else {
625
- next(createError(406, {
626
- types: normalizeTypes(keys).map( o => o.value )
627
- }))
628
- }
629
-
630
- return this;
631
- };
632
-
633
-
634
- /**
635
- * Set _Content-Disposition_ header to _attachment_ with optional `filename`.
636
- *
637
- * @param {String} filename
638
- * @return {ServerResponse}
639
- *
640
- * @public
641
- */
642
- res.attachment = function attachment(filename) {
643
- if (filename) {
644
- this.type(extname(filename));
645
- }
646
-
647
- this.set('Content-Disposition', contentDisposition(filename));
648
-
649
- return this;
650
- };
651
-
652
-
653
- /**
654
- * Append additional header `field` with value `val`.
655
- *
656
- * Example:
657
- *
658
- * res.append('Link', ['<http://localhost/>', '<http://localhost:3000/>']);
659
- * res.append('Set-Cookie', 'foo=bar; Path=/; HttpOnly');
660
- * res.append('Warning', '199 Miscellaneous warning');
661
- *
662
- * @param {String} field
663
- * @param {String|Array} val
664
- * @return {ServerResponse} for chaining
665
- *
666
- * @public
667
- */
668
- res.append = function append(field, val) {
669
- const prev = this.get(field);
670
- let value = val;
671
-
672
- if (prev) {
673
- // concat the new and prev vals
674
- value = Array.isArray(prev) ? prev.concat(val)
675
- : Array.isArray(val) ? [prev].concat(val)
676
- : [prev, val]
677
- }
678
-
679
- return this.set(field, value);
680
- };
681
-
682
-
683
- /**
684
- * Set header `field` to `val`, or pass
685
- * an object of header fields.
686
- *
687
- * Examples:
688
- *
689
- * res.set('Foo', ['bar', 'baz']);
690
- * res.set('Accept', 'application/json');
691
- * res.set({ Accept: 'text/plain', 'X-API-Key': 'tobi' });
692
- *
693
- * Aliased as `res.header()`.
694
- *
695
- * @param {String|Object} field
696
- * @param {String|Array} val
697
- * @return {ServerResponse} for chaining
698
- *
699
- * @public
700
- */
701
- res.set =
702
- res.header = function header(field, val) {
703
- if (arguments.length === 2) {
704
- let value = Array.isArray(val) ?
705
- val.map(String)
706
- :
707
- String(val);
708
-
709
- // add charset to content-type
710
- if (field.toLowerCase() === 'content-type') {
711
- if (Array.isArray(value)) {
712
- throw new TypeError('Content-Type cannot be set to an Array');
713
- }
714
-
715
- if (!charsetRegExp.test(value)) {
716
- const charset = mime.charsets.lookup(value.split(';')[0]);
717
-
718
- if (charset) {
719
- value += '; charset=' + charset.toLowerCase();
720
- }
721
- }
722
- }
723
-
724
- this.setHeader(field, value);
725
- }
726
- else {
727
- for (let key in field) {
728
- this.set(key, field[key]);
729
- }
730
- }
731
- return this;
732
- };
733
-
734
-
735
- /**
736
- * Get value for header `field`.
737
- *
738
- * @param {String} field
739
- * @return {String}
740
- *
741
- * @public
742
- */
743
- res.get = function(field){
744
- return this.getHeader(field);
745
- };
746
-
747
-
748
- /**
749
- * Clear cookie `name`.
750
- *
751
- * @param {String} name
752
- * @param {Object} [options]
753
- * @return {ServerResponse} for chaining
754
- *
755
- * @public
756
- */
757
- res.clearCookie = function clearCookie(name, options) {
758
- const defaultOpts = { expires: new Date(1), path: '/' };
759
- const opts = merge(defaultOpts, options);
760
-
761
- return this.cookie(name, '', opts);
762
- };
763
-
764
-
765
- /**
766
- * Set cookie `name` to `value`, with the given `options`.
767
- *
768
- * Options:
769
- *
770
- * - `maxAge` max-age in milliseconds, converted to `expires`
771
- * - `signed` sign the cookie
772
- * - `path` defaults to "/"
773
- *
774
- * Examples:
775
- *
776
- * // "Remember Me" for 15 minutes
777
- * res.cookie('rememberme', '1', { expires: new Date(Date.now() + 900000), httpOnly: true });
778
- *
779
- * // same as above
780
- * res.cookie('rememberme', '1', { maxAge: 900000, httpOnly: true })
781
- *
782
- * @param {String} name
783
- * @param {String|Object} value
784
- * @param {Object} [options]
785
- * @return {ServerResponse} for chaining
786
- *
787
- * @public
788
- */
789
- res.cookie = function (name, value, options) {
790
- let opts = merge({}, options);
791
- const secret = this.req.secret;
792
- const signed = !!opts.signed;
793
-
794
- if (signed && !secret) {
795
- throw new Error('cookieParser("secret") required for signed cookies');
796
- }
797
-
798
- let val = typeof value === 'object'
799
- ? 'j:' + JSON.stringify(value)
800
- : String(value);
801
-
802
- if (signed) {
803
- val = 's:' + sign(val, secret);
804
- }
805
-
806
- if (opts.maxAge != null) {
807
- const maxAge = opts.maxAge - 0
808
-
809
- if (!isNaN(maxAge)) {
810
- opts.expires = new Date(Date.now() + maxAge)
811
- opts.maxAge = Math.floor(maxAge / 1000)
812
- }
813
- }
814
-
815
- if (opts.path == null) {
816
- opts.path = '/';
817
- }
818
-
819
- this.append('Set-Cookie', cookie.serialize(name, String(val), opts));
820
-
821
- return this;
822
- };
823
-
824
-
825
- /**
826
- * Set the location header to `url`.
827
- *
828
- * The given `url` can also be "back", which redirects
829
- * to the _Referrer_ or _Referer_ headers or "/".
830
- *
831
- * Examples:
832
- *
833
- * res.location('/foo/bar').;
834
- * res.location('http://example.com');
835
- * res.location('../login');
836
- *
837
- * @param {String} url
838
- * @return {ServerResponse} for chaining
839
- *
840
- * @public
841
- */
842
- res.location = function location(url) {
843
- let to = url;
844
-
845
- // "back" is an alias for the referrer
846
- if (url === 'back') {
847
- to = this.req.get('Referrer') || '/';
848
- }
849
-
850
- // set location
851
- return this.set('Location', encodeUrl(to));
852
- };
853
-
854
-
855
- /**
856
- * Redirect to the given `url` with optional response `status`
857
- * defaulting to 302.
858
- *
859
- * The resulting `url` is determined by `res.location()`, so
860
- * it will play nicely with mounted apps, relative paths,
861
- * `"back"` etc.
862
- *
863
- * Examples:
864
- *
865
- * res.redirect('/foo/bar');
866
- * res.redirect('http://example.com');
867
- * res.redirect(301, 'http://example.com');
868
- * res.redirect('../login'); // /blog/post/1 -> /blog/login
869
- *
870
- * @public
871
- */
872
- res.redirect = function redirect(url) {
873
- let address = url;
874
- let body;
875
- let status = 302;
876
-
877
- // allow status / url
878
- if (arguments.length === 2) {
879
- if (typeof arguments[0] === 'number') {
880
- status = arguments[0];
881
- address = arguments[1];
882
- }
883
- else {
884
- deprecate('res.redirect(url, status): Use res.redirect(status, url) instead');
885
- status = arguments[1];
886
- }
887
- }
888
-
889
- // Set location header
890
- address = this.location(address).get('Location');
891
-
892
- // Support text/{plain,html} by default
893
- this.format({
894
- text: function(){
895
- body = statuses.message[status] + '. Redirecting to ' + address
896
- },
897
-
898
- html: function(){
899
- const url = escapeHtml(address);
900
- body = '<p>' + statuses.message[status] + '. Redirecting to <a href="' + url + '">' + url + '</a></p>'
901
- },
902
-
903
- default: function(){
904
- body = '';
905
- }
906
- });
907
-
908
- // Respond
909
- this.statusCode = status;
910
- this.set('Content-Length', Buffer.byteLength(body));
911
-
912
- if (this.req.method === 'HEAD') {
913
- this.end();
914
- }
915
- else {
916
- this.end(body);
917
- }
918
- };
919
-
920
-
921
- /**
922
- * Add `field` to Vary. If already present in the Vary set, then
923
- * this call is simply ignored.
924
- *
925
- * @param {Array|String} field
926
- * @return {ServerResponse} for chaining
927
- *
928
- * @public
929
- */
930
- res.vary = function(field) {
931
- vary(this, field);
932
- return this;
933
- };
934
-
935
-
936
- /**
937
- * Render `view` with the given `options` and optional callback `fn`.
938
- * When a callback function is given a response will _not_ be made
939
- * automatically, otherwise a response of _200_ and _text/html_ is given.
940
- *
941
- * Options:
942
- *
943
- * - `cache` boolean hinting to the engine it should cache
944
- * - `filename` filename of the view being rendered
945
- *
946
- * @public
947
- */
948
- res.render = function render(view, options, callback) {
949
- const app = this.req.app;
950
- let done = callback;
951
- let opts = options || {};
952
- const req = this.req;
953
- const self = this;
954
-
955
- // support callback function as second arg
956
- if (typeof options === 'function') {
957
- done = options;
958
- opts = {};
959
- }
960
-
961
- // merge res.locals
962
- opts._locals = self.locals;
963
-
964
- // default callback to respond
965
- done = done || function (err, str) {
966
- if (err) return req.next(err);
967
- self.send(str);
968
- };
969
-
970
- // render
971
- app.render(view, opts, done);
972
- };
973
-
974
- // pipe the send file stream
975
- function sendfile(res, file, options, callback) {
976
- let done = false;
977
- let streaming;
978
-
979
- // request aborted
980
- function onaborted() {
981
- if (done) return;
982
- done = true;
983
-
984
- const err = new Error('Request aborted');
985
- err.code = 'ECONNABORTED';
986
- callback(err);
987
- }
988
-
989
- // directory
990
- function ondirectory() {
991
- if (done)
992
- return;
993
-
994
- done = true;
995
-
996
- const err = new Error('EISDIR, read');
997
- err.code = 'EISDIR';
998
- callback(err);
999
- }
1000
-
1001
- // errors
1002
- function onerror(err) {
1003
- if (done)
1004
- return;
1005
-
1006
- done = true;
1007
- callback(err);
1008
- }
1009
-
1010
- // ended
1011
- function onend() {
1012
- if (done)
1013
- return;
1014
-
1015
- done = true;
1016
- callback();
1017
- }
1018
-
1019
- // file
1020
- function onfile() {
1021
- streaming = false;
1022
- }
1023
-
1024
- // finished
1025
- function onfinish(err) {
1026
- if (err && err.code === 'ECONNRESET')
1027
- return onaborted();
1028
-
1029
- if (err)
1030
- return onerror(err);
1031
-
1032
- if (done)
1033
- return;
1034
-
1035
- setImmediate(function () {
1036
- if (streaming !== false && !done) {
1037
- onaborted();
1038
- return;
1039
- }
1040
-
1041
- if (done) return;
1042
- done = true;
1043
- callback();
1044
- });
1045
- }
1046
-
1047
- // streaming
1048
- function onstream() {
1049
- streaming = true;
1050
- }
1051
-
1052
- file.on('directory', ondirectory);
1053
- file.on('end', onend);
1054
- file.on('error', onerror);
1055
- file.on('file', onfile);
1056
- file.on('stream', onstream);
1057
- onFinished(res, onfinish);
1058
-
1059
- if (options.headers) {
1060
- // set headers on successful transfer
1061
- file.on('headers', function headers(res) {
1062
- const _headers = options.headers;
1063
- const keys = Object.keys(_headers);
1064
-
1065
- for (let i = 0; i < keys.length; i++) {
1066
- const key = keys[i];
1067
- res.setHeader(key, _headers[k]);
1068
- }
1069
- });
1070
- }
1071
-
1072
- // pipe
1073
- file.pipe(res);
1074
- }