nodester 0.1.4 → 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 +89 -0
- 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 +16 -16
- package/lib/facades/mixins/index.js +67 -13
- package/lib/factories/responses/rest.js +25 -13
- package/lib/http/codes/descriptions.js +82 -0
- package/lib/http/codes/index.js +70 -145
- package/lib/http/codes/symbols.js +82 -0
- 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 +13 -4
- package/lib/middlewares/ql/sequelize/interpreter/QueryLexer.js +4 -3
- package/lib/middlewares/render/index.js +62 -0
- package/lib/models/associate.js +25 -1
- package/lib/models/define.js +26 -19
- package/lib/models/mixins.js +8 -1
- package/lib/{queries → query}/traverse.js +118 -77
- package/lib/router/handlers.util.js +1 -0
- package/lib/router/index.js +194 -99
- package/lib/router/markers.js +7 -0
- package/lib/router/route.js +5 -0
- package/lib/router/routes.util.js +16 -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 +20 -10
- package/lib/database/utils.js +0 -19
- package/lib/enums/Enum.js +0 -16
- 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/preprocessors/BodyPreprocessor.js +0 -61
- package/lib/queries/Colander.js +0 -107
- package/lib/queries/NodesterQueryParams.js +0 -145
- package/lib/services/includes.service.js +0 -79
- package/lib/services/jwt.service.js +0 -147
- package/lib/stacks/MiddlewareStack.js +0 -159
package/lib/http/response.js
DELETED
|
@@ -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
|
-
}
|