@eggjs/koa 2.14.2 → 2.15.1
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 +8 -100
- package/lib/application.d.ts +110 -0
- package/lib/application.js +250 -304
- package/lib/context.d.ts +104 -0
- package/lib/context.js +212 -242
- package/lib/request.d.ts +317 -0
- package/lib/request.js +525 -724
- package/lib/response.d.ts +210 -0
- package/lib/response.js +441 -586
- package/lib/types.d.ts +11 -0
- package/lib/types.js +3 -0
- package/package.json +26 -43
- package/dist/koa.mjs +0 -4
package/lib/response.js
CHANGED
|
@@ -1,588 +1,443 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Module dependencies.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
const contentDisposition = require('content-disposition');
|
|
9
|
-
const getType = require('cache-content-type');
|
|
10
|
-
const onFinish = require('on-finished');
|
|
11
|
-
const escape = require('escape-html');
|
|
12
|
-
const typeis = require('type-is').is;
|
|
13
|
-
const statuses = require('statuses');
|
|
14
|
-
const destroy = require('destroy');
|
|
15
|
-
const assert = require('assert');
|
|
16
|
-
const extname = require('path').extname;
|
|
17
|
-
const vary = require('vary');
|
|
18
|
-
const only = require('only');
|
|
19
|
-
const util = require('util');
|
|
20
|
-
const encodeUrl = require('encodeurl');
|
|
21
|
-
const Stream = require('stream');
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Prototype.
|
|
25
|
-
*/
|
|
26
|
-
|
|
27
|
-
module.exports = {
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Return the request socket.
|
|
31
|
-
*
|
|
32
|
-
* @return {Connection}
|
|
33
|
-
* @api public
|
|
34
|
-
*/
|
|
35
|
-
|
|
36
|
-
get socket() {
|
|
37
|
-
return this.res.socket;
|
|
38
|
-
},
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Return response header.
|
|
42
|
-
*
|
|
43
|
-
* @return {Object}
|
|
44
|
-
* @api public
|
|
45
|
-
*/
|
|
46
|
-
|
|
47
|
-
get header() {
|
|
48
|
-
const { res } = this;
|
|
49
|
-
return typeof res.getHeaders === 'function'
|
|
50
|
-
? res.getHeaders()
|
|
51
|
-
: res._headers || {}; // Node < 7.7
|
|
52
|
-
},
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Return response header, alias as response.header
|
|
56
|
-
*
|
|
57
|
-
* @return {Object}
|
|
58
|
-
* @api public
|
|
59
|
-
*/
|
|
60
|
-
|
|
61
|
-
get headers() {
|
|
62
|
-
return this.header;
|
|
63
|
-
},
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Get response status code.
|
|
67
|
-
*
|
|
68
|
-
* @return {Number}
|
|
69
|
-
* @api public
|
|
70
|
-
*/
|
|
71
|
-
|
|
72
|
-
get status() {
|
|
73
|
-
return this.res.statusCode;
|
|
74
|
-
},
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Set response status code.
|
|
78
|
-
*
|
|
79
|
-
* @param {Number} code
|
|
80
|
-
* @api public
|
|
81
|
-
*/
|
|
82
|
-
|
|
83
|
-
set status(code) {
|
|
84
|
-
if (this.headerSent) return;
|
|
85
|
-
|
|
86
|
-
assert(Number.isInteger(code), 'status code must be a number');
|
|
87
|
-
assert(code >= 100 && code <= 999, `invalid status code: ${code}`);
|
|
88
|
-
this._explicitStatus = true;
|
|
89
|
-
this.res.statusCode = code;
|
|
90
|
-
if (this.req.httpVersionMajor < 2) this.res.statusMessage = statuses[code];
|
|
91
|
-
if (this.body && statuses.empty[code]) this.body = null;
|
|
92
|
-
},
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Get response status message
|
|
96
|
-
*
|
|
97
|
-
* @return {String}
|
|
98
|
-
* @api public
|
|
99
|
-
*/
|
|
100
|
-
|
|
101
|
-
get message() {
|
|
102
|
-
return this.res.statusMessage || statuses[this.status];
|
|
103
|
-
},
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Set response status message
|
|
107
|
-
*
|
|
108
|
-
* @param {String} msg
|
|
109
|
-
* @api public
|
|
110
|
-
*/
|
|
111
|
-
|
|
112
|
-
set message(msg) {
|
|
113
|
-
this.res.statusMessage = msg;
|
|
114
|
-
},
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Get response body.
|
|
118
|
-
*
|
|
119
|
-
* @return {Mixed}
|
|
120
|
-
* @api public
|
|
121
|
-
*/
|
|
122
|
-
|
|
123
|
-
get body() {
|
|
124
|
-
return this._body;
|
|
125
|
-
},
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Set response body.
|
|
129
|
-
*
|
|
130
|
-
* @param {String|Buffer|Object|Stream} val
|
|
131
|
-
* @api public
|
|
132
|
-
*/
|
|
133
|
-
|
|
134
|
-
set body(val) {
|
|
135
|
-
const original = this._body;
|
|
136
|
-
this._body = val;
|
|
137
|
-
|
|
138
|
-
// no content
|
|
139
|
-
if (null == val) {
|
|
140
|
-
if (!statuses.empty[this.status]) this.status = 204;
|
|
141
|
-
if (val === null) this._explicitNullBody = true;
|
|
142
|
-
this.remove('Content-Type');
|
|
143
|
-
this.remove('Content-Length');
|
|
144
|
-
this.remove('Transfer-Encoding');
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// set the status
|
|
149
|
-
if (!this._explicitStatus) this.status = 200;
|
|
150
|
-
|
|
151
|
-
// set the content-type only if not yet set
|
|
152
|
-
const setType = !this.has('Content-Type');
|
|
153
|
-
|
|
154
|
-
// string
|
|
155
|
-
if ('string' === typeof val) {
|
|
156
|
-
if (setType) this.type = /^\s*</.test(val) ? 'html' : 'text';
|
|
157
|
-
this.length = Buffer.byteLength(val);
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// buffer
|
|
162
|
-
if (Buffer.isBuffer(val)) {
|
|
163
|
-
if (setType) this.type = 'bin';
|
|
164
|
-
this.length = val.length;
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// stream
|
|
169
|
-
if (val instanceof Stream) {
|
|
170
|
-
onFinish(this.res, destroy.bind(null, val));
|
|
171
|
-
if (original != val) {
|
|
172
|
-
val.once('error', err => this.ctx.onerror(err));
|
|
173
|
-
// overwriting
|
|
174
|
-
if (null != original) this.remove('Content-Length');
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
if (setType) this.type = 'bin';
|
|
178
|
-
return;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// json
|
|
182
|
-
this.remove('Content-Length');
|
|
183
|
-
this.type = 'json';
|
|
184
|
-
},
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* Set Content-Length field to `n`.
|
|
188
|
-
*
|
|
189
|
-
* @param {Number} n
|
|
190
|
-
* @api public
|
|
191
|
-
*/
|
|
192
|
-
|
|
193
|
-
set length(n) {
|
|
194
|
-
if (!this.has('Transfer-Encoding')) {
|
|
195
|
-
this.set('Content-Length', n);
|
|
196
|
-
}
|
|
197
|
-
},
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Return parsed response Content-Length when present.
|
|
201
|
-
*
|
|
202
|
-
* @return {Number}
|
|
203
|
-
* @api public
|
|
204
|
-
*/
|
|
205
|
-
|
|
206
|
-
get length() {
|
|
207
|
-
if (this.has('Content-Length')) {
|
|
208
|
-
return parseInt(this.get('Content-Length'), 10) || 0;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
const { body } = this;
|
|
212
|
-
if (!body || body instanceof Stream) return undefined;
|
|
213
|
-
if ('string' === typeof body) return Buffer.byteLength(body);
|
|
214
|
-
if (Buffer.isBuffer(body)) return body.length;
|
|
215
|
-
return Buffer.byteLength(JSON.stringify(body));
|
|
216
|
-
},
|
|
217
|
-
|
|
218
|
-
/**
|
|
219
|
-
* Check if a header has been written to the socket.
|
|
220
|
-
*
|
|
221
|
-
* @return {Boolean}
|
|
222
|
-
* @api public
|
|
223
|
-
*/
|
|
224
|
-
|
|
225
|
-
get headerSent() {
|
|
226
|
-
return this.res.headersSent;
|
|
227
|
-
},
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* Vary on `field`.
|
|
231
|
-
*
|
|
232
|
-
* @param {String} field
|
|
233
|
-
* @api public
|
|
234
|
-
*/
|
|
235
|
-
|
|
236
|
-
vary(field) {
|
|
237
|
-
if (this.headerSent) return;
|
|
238
|
-
|
|
239
|
-
vary(this.res, field);
|
|
240
|
-
},
|
|
241
|
-
|
|
242
|
-
/**
|
|
243
|
-
* Perform a 302 redirect to `url`.
|
|
244
|
-
*
|
|
245
|
-
* The string "back" is special-cased
|
|
246
|
-
* to provide Referrer support, when Referrer
|
|
247
|
-
* is not present `alt` or "/" is used.
|
|
248
|
-
*
|
|
249
|
-
* Examples:
|
|
250
|
-
*
|
|
251
|
-
* this.redirect('back');
|
|
252
|
-
* this.redirect('back', '/index.html');
|
|
253
|
-
* this.redirect('/login');
|
|
254
|
-
* this.redirect('http://google.com');
|
|
255
|
-
*
|
|
256
|
-
* @param {String} url
|
|
257
|
-
* @param {String} [alt]
|
|
258
|
-
* @api public
|
|
259
|
-
*/
|
|
260
|
-
|
|
261
|
-
redirect(url, alt) {
|
|
262
|
-
// location
|
|
263
|
-
if ('back' === url) url = this.ctx.get('Referrer') || alt || '/';
|
|
264
|
-
this.set('Location', encodeUrl(url));
|
|
265
|
-
|
|
266
|
-
// status
|
|
267
|
-
if (!statuses.redirect[this.status]) this.status = 302;
|
|
268
|
-
|
|
269
|
-
// html
|
|
270
|
-
if (this.ctx.accepts('html')) {
|
|
271
|
-
url = escape(url);
|
|
272
|
-
this.type = 'text/html; charset=utf-8';
|
|
273
|
-
this.body = `Redirecting to <a href="${url}">${url}</a>.`;
|
|
274
|
-
return;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
// text
|
|
278
|
-
this.type = 'text/plain; charset=utf-8';
|
|
279
|
-
this.body = `Redirecting to ${url}.`;
|
|
280
|
-
},
|
|
281
|
-
|
|
282
|
-
/**
|
|
283
|
-
* Set Content-Disposition header to "attachment" with optional `filename`.
|
|
284
|
-
*
|
|
285
|
-
* @param {String} filename
|
|
286
|
-
* @api public
|
|
287
|
-
*/
|
|
288
|
-
|
|
289
|
-
attachment(filename, options) {
|
|
290
|
-
if (filename) this.type = extname(filename);
|
|
291
|
-
this.set('Content-Disposition', contentDisposition(filename, options));
|
|
292
|
-
},
|
|
293
|
-
|
|
294
|
-
/**
|
|
295
|
-
* Set Content-Type response header with `type` through `mime.lookup()`
|
|
296
|
-
* when it does not contain a charset.
|
|
297
|
-
*
|
|
298
|
-
* Examples:
|
|
299
|
-
*
|
|
300
|
-
* this.type = '.html';
|
|
301
|
-
* this.type = 'html';
|
|
302
|
-
* this.type = 'json';
|
|
303
|
-
* this.type = 'application/json';
|
|
304
|
-
* this.type = 'png';
|
|
305
|
-
*
|
|
306
|
-
* @param {String} type
|
|
307
|
-
* @api public
|
|
308
|
-
*/
|
|
309
|
-
|
|
310
|
-
set type(type) {
|
|
311
|
-
type = getType(type);
|
|
312
|
-
if (type) {
|
|
313
|
-
this.set('Content-Type', type);
|
|
314
|
-
} else {
|
|
315
|
-
this.remove('Content-Type');
|
|
316
|
-
}
|
|
317
|
-
},
|
|
318
|
-
|
|
319
|
-
/**
|
|
320
|
-
* Set the Last-Modified date using a string or a Date.
|
|
321
|
-
*
|
|
322
|
-
* this.response.lastModified = new Date();
|
|
323
|
-
* this.response.lastModified = '2013-09-13';
|
|
324
|
-
*
|
|
325
|
-
* @param {String|Date} type
|
|
326
|
-
* @api public
|
|
327
|
-
*/
|
|
328
|
-
|
|
329
|
-
set lastModified(val) {
|
|
330
|
-
if ('string' === typeof val) val = new Date(val);
|
|
331
|
-
this.set('Last-Modified', val.toUTCString());
|
|
332
|
-
},
|
|
333
|
-
|
|
334
|
-
/**
|
|
335
|
-
* Get the Last-Modified date in Date form, if it exists.
|
|
336
|
-
*
|
|
337
|
-
* @return {Date}
|
|
338
|
-
* @api public
|
|
339
|
-
*/
|
|
340
|
-
|
|
341
|
-
get lastModified() {
|
|
342
|
-
const date = this.get('last-modified');
|
|
343
|
-
if (date) return new Date(date);
|
|
344
|
-
},
|
|
345
|
-
|
|
346
|
-
/**
|
|
347
|
-
* Set the ETag of a response.
|
|
348
|
-
* This will normalize the quotes if necessary.
|
|
349
|
-
*
|
|
350
|
-
* this.response.etag = 'md5hashsum';
|
|
351
|
-
* this.response.etag = '"md5hashsum"';
|
|
352
|
-
* this.response.etag = 'W/"123456789"';
|
|
353
|
-
*
|
|
354
|
-
* @param {String} etag
|
|
355
|
-
* @api public
|
|
356
|
-
*/
|
|
357
|
-
|
|
358
|
-
set etag(val) {
|
|
359
|
-
if (!/^(W\/)?"/.test(val)) val = `"${val}"`;
|
|
360
|
-
this.set('ETag', val);
|
|
361
|
-
},
|
|
362
|
-
|
|
363
|
-
/**
|
|
364
|
-
* Get the ETag of a response.
|
|
365
|
-
*
|
|
366
|
-
* @return {String}
|
|
367
|
-
* @api public
|
|
368
|
-
*/
|
|
369
|
-
|
|
370
|
-
get etag() {
|
|
371
|
-
return this.get('ETag');
|
|
372
|
-
},
|
|
373
|
-
|
|
374
|
-
/**
|
|
375
|
-
* Return the response mime type void of
|
|
376
|
-
* parameters such as "charset".
|
|
377
|
-
*
|
|
378
|
-
* @return {String}
|
|
379
|
-
* @api public
|
|
380
|
-
*/
|
|
381
|
-
|
|
382
|
-
get type() {
|
|
383
|
-
const type = this.get('Content-Type');
|
|
384
|
-
if (!type) return '';
|
|
385
|
-
return type.split(';', 1)[0];
|
|
386
|
-
},
|
|
387
|
-
|
|
388
|
-
/**
|
|
389
|
-
* Check whether the response is one of the listed types.
|
|
390
|
-
* Pretty much the same as `this.request.is()`.
|
|
391
|
-
*
|
|
392
|
-
* @param {String|String[]} [type]
|
|
393
|
-
* @param {String[]} [types]
|
|
394
|
-
* @return {String|false}
|
|
395
|
-
* @api public
|
|
396
|
-
*/
|
|
397
|
-
|
|
398
|
-
is(type, ...types) {
|
|
399
|
-
return typeis(this.type, type, ...types);
|
|
400
|
-
},
|
|
401
|
-
|
|
402
|
-
/**
|
|
403
|
-
* Return response header.
|
|
404
|
-
*
|
|
405
|
-
* Examples:
|
|
406
|
-
*
|
|
407
|
-
* this.get('Content-Type');
|
|
408
|
-
* // => "text/plain"
|
|
409
|
-
*
|
|
410
|
-
* this.get('content-type');
|
|
411
|
-
* // => "text/plain"
|
|
412
|
-
*
|
|
413
|
-
* @param {String} field
|
|
414
|
-
* @return {String}
|
|
415
|
-
* @api public
|
|
416
|
-
*/
|
|
417
|
-
|
|
418
|
-
get(field) {
|
|
419
|
-
return this.header[field.toLowerCase()] || '';
|
|
420
|
-
},
|
|
421
|
-
|
|
422
|
-
/**
|
|
423
|
-
* Returns true if the header identified by name is currently set in the outgoing headers.
|
|
424
|
-
* The header name matching is case-insensitive.
|
|
425
|
-
*
|
|
426
|
-
* Examples:
|
|
427
|
-
*
|
|
428
|
-
* this.has('Content-Type');
|
|
429
|
-
* // => true
|
|
430
|
-
*
|
|
431
|
-
* this.get('content-type');
|
|
432
|
-
* // => true
|
|
433
|
-
*
|
|
434
|
-
* @param {String} field
|
|
435
|
-
* @return {boolean}
|
|
436
|
-
* @api public
|
|
437
|
-
*/
|
|
438
|
-
|
|
439
|
-
has(field) {
|
|
440
|
-
return typeof this.res.hasHeader === 'function'
|
|
441
|
-
? this.res.hasHeader(field)
|
|
442
|
-
// Node < 7.7
|
|
443
|
-
: field.toLowerCase() in this.headers;
|
|
444
|
-
},
|
|
445
|
-
|
|
446
|
-
/**
|
|
447
|
-
* Set header `field` to `val` or pass
|
|
448
|
-
* an object of header fields.
|
|
449
|
-
*
|
|
450
|
-
* Examples:
|
|
451
|
-
*
|
|
452
|
-
* this.set('Foo', ['bar', 'baz']);
|
|
453
|
-
* this.set('Accept', 'application/json');
|
|
454
|
-
* this.set({ Accept: 'text/plain', 'X-API-Key': 'tobi' });
|
|
455
|
-
*
|
|
456
|
-
* @param {String|Object|Array} field
|
|
457
|
-
* @param {String} val
|
|
458
|
-
* @api public
|
|
459
|
-
*/
|
|
460
|
-
|
|
461
|
-
set(field, val) {
|
|
462
|
-
if (this.headerSent) return;
|
|
463
|
-
|
|
464
|
-
if (2 === arguments.length) {
|
|
465
|
-
if (Array.isArray(val)) val = val.map(v => typeof v === 'string' ? v : String(v));
|
|
466
|
-
else if (typeof val !== 'string') val = String(val);
|
|
467
|
-
this.res.setHeader(field, val);
|
|
468
|
-
} else {
|
|
469
|
-
for (const key in field) {
|
|
470
|
-
this.set(key, field[key]);
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
},
|
|
474
|
-
|
|
475
|
-
/**
|
|
476
|
-
* Append additional header `field` with value `val`.
|
|
477
|
-
*
|
|
478
|
-
* Examples:
|
|
479
|
-
*
|
|
480
|
-
* ```
|
|
481
|
-
* this.append('Link', ['<http://localhost/>', '<http://localhost:3000/>']);
|
|
482
|
-
* this.append('Set-Cookie', 'foo=bar; Path=/; HttpOnly');
|
|
483
|
-
* this.append('Warning', '199 Miscellaneous warning');
|
|
484
|
-
* ```
|
|
485
|
-
*
|
|
486
|
-
* @param {String} field
|
|
487
|
-
* @param {String|Array} val
|
|
488
|
-
* @api public
|
|
489
|
-
*/
|
|
490
|
-
|
|
491
|
-
append(field, val) {
|
|
492
|
-
const prev = this.get(field);
|
|
493
|
-
|
|
494
|
-
if (prev) {
|
|
495
|
-
val = Array.isArray(prev)
|
|
496
|
-
? prev.concat(val)
|
|
497
|
-
: [prev].concat(val);
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
return this.set(field, val);
|
|
501
|
-
},
|
|
502
|
-
|
|
503
|
-
/**
|
|
504
|
-
* Remove header `field`.
|
|
505
|
-
*
|
|
506
|
-
* @param {String} name
|
|
507
|
-
* @api public
|
|
508
|
-
*/
|
|
509
|
-
|
|
510
|
-
remove(field) {
|
|
511
|
-
if (this.headerSent) return;
|
|
512
|
-
|
|
513
|
-
this.res.removeHeader(field);
|
|
514
|
-
},
|
|
515
|
-
|
|
516
|
-
/**
|
|
517
|
-
* Checks if the request is writable.
|
|
518
|
-
* Tests for the existence of the socket
|
|
519
|
-
* as node sometimes does not set it.
|
|
520
|
-
*
|
|
521
|
-
* @return {Boolean}
|
|
522
|
-
* @api private
|
|
523
|
-
*/
|
|
524
|
-
|
|
525
|
-
get writable() {
|
|
526
|
-
// can't write any more after response finished
|
|
527
|
-
// response.writableEnded is available since Node > 12.9
|
|
528
|
-
// https://nodejs.org/api/http.html#http_response_writableended
|
|
529
|
-
// response.finished is undocumented feature of previous Node versions
|
|
530
|
-
// https://stackoverflow.com/questions/16254385/undocumented-response-finished-in-node-js
|
|
531
|
-
if (this.res.writableEnded || this.res.finished) return false;
|
|
532
|
-
|
|
533
|
-
const socket = this.res.socket;
|
|
534
|
-
// There are already pending outgoing res, but still writable
|
|
535
|
-
// https://github.com/nodejs/node/blob/v4.4.7/lib/_http_server.js#L486
|
|
536
|
-
if (!socket) return true;
|
|
537
|
-
return socket.writable;
|
|
538
|
-
},
|
|
539
|
-
|
|
540
|
-
/**
|
|
541
|
-
* Inspect implementation.
|
|
542
|
-
*
|
|
543
|
-
* @return {Object}
|
|
544
|
-
* @api public
|
|
545
|
-
*/
|
|
546
|
-
|
|
547
|
-
inspect() {
|
|
548
|
-
if (!this.res) return;
|
|
549
|
-
const o = this.toJSON();
|
|
550
|
-
o.body = this.body;
|
|
551
|
-
return o;
|
|
552
|
-
},
|
|
553
|
-
|
|
554
|
-
/**
|
|
555
|
-
* Return JSON representation.
|
|
556
|
-
*
|
|
557
|
-
* @return {Object}
|
|
558
|
-
* @api public
|
|
559
|
-
*/
|
|
560
|
-
|
|
561
|
-
toJSON() {
|
|
562
|
-
return only(this, [
|
|
563
|
-
'status',
|
|
564
|
-
'message',
|
|
565
|
-
'header'
|
|
566
|
-
]);
|
|
567
|
-
},
|
|
568
|
-
|
|
569
|
-
/**
|
|
570
|
-
* Flush any set headers and begin the body
|
|
571
|
-
*/
|
|
572
|
-
|
|
573
|
-
flushHeaders() {
|
|
574
|
-
this.res.flushHeaders();
|
|
575
|
-
}
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
576
4
|
};
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const node_assert_1 = __importDefault(require("node:assert"));
|
|
7
|
+
const node_path_1 = require("node:path");
|
|
8
|
+
const node_util_1 = __importDefault(require("node:util"));
|
|
9
|
+
const node_stream_1 = __importDefault(require("node:stream"));
|
|
10
|
+
const content_disposition_1 = __importDefault(require("content-disposition"));
|
|
11
|
+
const cache_content_type_1 = __importDefault(require("cache-content-type"));
|
|
12
|
+
const on_finished_1 = __importDefault(require("on-finished"));
|
|
13
|
+
const escape_html_1 = __importDefault(require("escape-html"));
|
|
14
|
+
const type_is_1 = require("type-is");
|
|
15
|
+
const statuses_1 = __importDefault(require("statuses"));
|
|
16
|
+
const destroy_1 = __importDefault(require("destroy"));
|
|
17
|
+
const vary_1 = __importDefault(require("vary"));
|
|
18
|
+
const only_1 = __importDefault(require("only"));
|
|
19
|
+
const encodeurl_1 = __importDefault(require("encodeurl"));
|
|
20
|
+
class Response {
|
|
21
|
+
app;
|
|
22
|
+
req;
|
|
23
|
+
res;
|
|
24
|
+
ctx;
|
|
25
|
+
request;
|
|
26
|
+
constructor(app, ctx, req, res) {
|
|
27
|
+
this.app = app;
|
|
28
|
+
this.req = req;
|
|
29
|
+
this.res = res;
|
|
30
|
+
this.ctx = ctx;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Return the request socket.
|
|
34
|
+
*/
|
|
35
|
+
get socket() {
|
|
36
|
+
return this.res.socket;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Return response header.
|
|
40
|
+
*/
|
|
41
|
+
get header() {
|
|
42
|
+
return this.res.getHeaders() || {};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Return response header, alias as response.header
|
|
46
|
+
*/
|
|
47
|
+
get headers() {
|
|
48
|
+
return this.header;
|
|
49
|
+
}
|
|
50
|
+
_explicitStatus;
|
|
51
|
+
/**
|
|
52
|
+
* Get response status code.
|
|
53
|
+
*/
|
|
54
|
+
get status() {
|
|
55
|
+
return this.res.statusCode;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Set response status code.
|
|
59
|
+
*/
|
|
60
|
+
set status(code) {
|
|
61
|
+
if (this.headerSent)
|
|
62
|
+
return;
|
|
63
|
+
(0, node_assert_1.default)(Number.isInteger(code), 'status code must be a number');
|
|
64
|
+
(0, node_assert_1.default)(code >= 100 && code <= 999, `invalid status code: ${code}`);
|
|
65
|
+
this._explicitStatus = true;
|
|
66
|
+
this.res.statusCode = code;
|
|
67
|
+
if (this.req.httpVersionMajor < 2)
|
|
68
|
+
this.res.statusMessage = statuses_1.default[code];
|
|
69
|
+
if (this.body && statuses_1.default.empty[code])
|
|
70
|
+
this.body = null;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Get response status message
|
|
74
|
+
*/
|
|
75
|
+
get message() {
|
|
76
|
+
return this.res.statusMessage || statuses_1.default[this.status];
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Set response status message
|
|
80
|
+
*/
|
|
81
|
+
set message(msg) {
|
|
82
|
+
this.res.statusMessage = msg;
|
|
83
|
+
}
|
|
84
|
+
_body;
|
|
85
|
+
_explicitNullBody;
|
|
86
|
+
/**
|
|
87
|
+
* Get response body.
|
|
88
|
+
*/
|
|
89
|
+
get body() {
|
|
90
|
+
return this._body;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Set response body.
|
|
94
|
+
*/
|
|
95
|
+
set body(val) {
|
|
96
|
+
const original = this._body;
|
|
97
|
+
this._body = val;
|
|
98
|
+
// no content
|
|
99
|
+
if (val == null) {
|
|
100
|
+
if (!statuses_1.default.empty[this.status])
|
|
101
|
+
this.status = 204;
|
|
102
|
+
if (val === null)
|
|
103
|
+
this._explicitNullBody = true;
|
|
104
|
+
this.remove('Content-Type');
|
|
105
|
+
this.remove('Content-Length');
|
|
106
|
+
this.remove('Transfer-Encoding');
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
// set the status
|
|
110
|
+
if (!this._explicitStatus)
|
|
111
|
+
this.status = 200;
|
|
112
|
+
// set the content-type only if not yet set
|
|
113
|
+
const setType = !this.has('Content-Type');
|
|
114
|
+
// string
|
|
115
|
+
if (typeof val === 'string') {
|
|
116
|
+
if (setType)
|
|
117
|
+
this.type = /^\s*</.test(val) ? 'html' : 'text';
|
|
118
|
+
this.length = Buffer.byteLength(val);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
// buffer
|
|
122
|
+
if (Buffer.isBuffer(val)) {
|
|
123
|
+
if (setType)
|
|
124
|
+
this.type = 'bin';
|
|
125
|
+
this.length = val.length;
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
// stream
|
|
129
|
+
if (val instanceof node_stream_1.default) {
|
|
130
|
+
(0, on_finished_1.default)(this.res, destroy_1.default.bind(null, val));
|
|
131
|
+
// eslint-disable-next-line eqeqeq
|
|
132
|
+
if (original != val) {
|
|
133
|
+
val.once('error', err => this.ctx.onerror(err));
|
|
134
|
+
// overwriting
|
|
135
|
+
if (original != null)
|
|
136
|
+
this.remove('Content-Length');
|
|
137
|
+
}
|
|
138
|
+
if (setType)
|
|
139
|
+
this.type = 'bin';
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
// json
|
|
143
|
+
this.remove('Content-Length');
|
|
144
|
+
this.type = 'json';
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Set Content-Length field to `n`.
|
|
148
|
+
*/
|
|
149
|
+
set length(n) {
|
|
150
|
+
if (n === undefined)
|
|
151
|
+
return;
|
|
152
|
+
if (!this.has('Transfer-Encoding')) {
|
|
153
|
+
this.set('Content-Length', n);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Return parsed response Content-Length when present.
|
|
158
|
+
*/
|
|
159
|
+
get length() {
|
|
160
|
+
if (this.has('Content-Length')) {
|
|
161
|
+
return parseInt(this.get('Content-Length'), 10) || 0;
|
|
162
|
+
}
|
|
163
|
+
const { body } = this;
|
|
164
|
+
if (!body || body instanceof node_stream_1.default)
|
|
165
|
+
return undefined;
|
|
166
|
+
if (typeof body === 'string')
|
|
167
|
+
return Buffer.byteLength(body);
|
|
168
|
+
if (Buffer.isBuffer(body))
|
|
169
|
+
return body.length;
|
|
170
|
+
return Buffer.byteLength(JSON.stringify(body));
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Check if a header has been written to the socket.
|
|
174
|
+
*/
|
|
175
|
+
get headerSent() {
|
|
176
|
+
return this.res.headersSent;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Vary on `field`.
|
|
180
|
+
*/
|
|
181
|
+
vary(field) {
|
|
182
|
+
if (this.headerSent)
|
|
183
|
+
return;
|
|
184
|
+
(0, vary_1.default)(this.res, field);
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Perform a 302 redirect to `url`.
|
|
188
|
+
*
|
|
189
|
+
* The string "back" is special-cased
|
|
190
|
+
* to provide Referrer support, when Referrer
|
|
191
|
+
* is not present `alt` or "/" is used.
|
|
192
|
+
*
|
|
193
|
+
* Examples:
|
|
194
|
+
*
|
|
195
|
+
* this.redirect('back');
|
|
196
|
+
* this.redirect('back', '/index.html');
|
|
197
|
+
* this.redirect('/login');
|
|
198
|
+
* this.redirect('http://google.com');
|
|
199
|
+
*/
|
|
200
|
+
redirect(url, alt) {
|
|
201
|
+
// location
|
|
202
|
+
if (url === 'back')
|
|
203
|
+
url = this.ctx.get('Referrer') || alt || '/';
|
|
204
|
+
this.set('Location', (0, encodeurl_1.default)(url));
|
|
205
|
+
// status
|
|
206
|
+
if (!statuses_1.default.redirect[this.status])
|
|
207
|
+
this.status = 302;
|
|
208
|
+
// html
|
|
209
|
+
if (this.ctx.accepts('html')) {
|
|
210
|
+
url = (0, escape_html_1.default)(url);
|
|
211
|
+
this.type = 'text/html; charset=utf-8';
|
|
212
|
+
this.body = `Redirecting to <a href="${url}">${url}</a>.`;
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
// text
|
|
216
|
+
this.type = 'text/plain; charset=utf-8';
|
|
217
|
+
this.body = `Redirecting to ${url}.`;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Set Content-Disposition header to "attachment" with optional `filename`.
|
|
221
|
+
*/
|
|
222
|
+
attachment(filename, options) {
|
|
223
|
+
if (filename)
|
|
224
|
+
this.type = (0, node_path_1.extname)(filename);
|
|
225
|
+
this.set('Content-Disposition', (0, content_disposition_1.default)(filename, options));
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Set Content-Type response header with `type` through `mime.lookup()`
|
|
229
|
+
* when it does not contain a charset.
|
|
230
|
+
*
|
|
231
|
+
* Examples:
|
|
232
|
+
*
|
|
233
|
+
* this.type = '.html';
|
|
234
|
+
* this.type = 'html';
|
|
235
|
+
* this.type = 'json';
|
|
236
|
+
* this.type = 'application/json';
|
|
237
|
+
* this.type = 'png';
|
|
238
|
+
*/
|
|
239
|
+
set type(type) {
|
|
240
|
+
type = (0, cache_content_type_1.default)(type);
|
|
241
|
+
if (type) {
|
|
242
|
+
this.set('Content-Type', type);
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
this.remove('Content-Type');
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Return the response mime type void of
|
|
250
|
+
* parameters such as "charset".
|
|
251
|
+
*/
|
|
252
|
+
get type() {
|
|
253
|
+
const type = this.get('Content-Type');
|
|
254
|
+
if (!type)
|
|
255
|
+
return '';
|
|
256
|
+
return type.split(';', 1)[0];
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Check whether the response is one of the listed types.
|
|
260
|
+
* Pretty much the same as `this.request.is()`.
|
|
261
|
+
*/
|
|
262
|
+
is(type, ...types) {
|
|
263
|
+
return (0, type_is_1.is)(this.type, type, ...types);
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Set the Last-Modified date using a string or a Date.
|
|
267
|
+
*
|
|
268
|
+
* this.response.lastModified = new Date();
|
|
269
|
+
* this.response.lastModified = '2013-09-13';
|
|
270
|
+
*/
|
|
271
|
+
set lastModified(val) {
|
|
272
|
+
if (typeof val === 'string')
|
|
273
|
+
val = new Date(val);
|
|
274
|
+
if (val) {
|
|
275
|
+
this.set('Last-Modified', val.toUTCString());
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Get the Last-Modified date in Date form, if it exists.
|
|
280
|
+
*/
|
|
281
|
+
get lastModified() {
|
|
282
|
+
const date = this.get('last-modified');
|
|
283
|
+
if (date)
|
|
284
|
+
return new Date(date);
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Set the ETag of a response.
|
|
288
|
+
* This will normalize the quotes if necessary.
|
|
289
|
+
*
|
|
290
|
+
* this.response.etag = 'md5hashsum';
|
|
291
|
+
* this.response.etag = '"md5hashsum"';
|
|
292
|
+
* this.response.etag = 'W/"123456789"';
|
|
293
|
+
*/
|
|
294
|
+
set etag(val) {
|
|
295
|
+
if (!/^(W\/)?"/.test(val))
|
|
296
|
+
val = `"${val}"`;
|
|
297
|
+
this.set('ETag', val);
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Get the ETag of a response.
|
|
301
|
+
*/
|
|
302
|
+
get etag() {
|
|
303
|
+
return this.get('ETag');
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Return response header.
|
|
307
|
+
*
|
|
308
|
+
* Examples:
|
|
309
|
+
*
|
|
310
|
+
* this.get('Content-Type');
|
|
311
|
+
* // => "text/plain"
|
|
312
|
+
*
|
|
313
|
+
* this.get('content-type');
|
|
314
|
+
* // => "text/plain"
|
|
315
|
+
*/
|
|
316
|
+
get(field) {
|
|
317
|
+
return (this.header[field.toLowerCase()] || '');
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Returns true if the header identified by name is currently set in the outgoing headers.
|
|
321
|
+
* The header name matching is case-insensitive.
|
|
322
|
+
*
|
|
323
|
+
* Examples:
|
|
324
|
+
*
|
|
325
|
+
* this.has('Content-Type');
|
|
326
|
+
* // => true
|
|
327
|
+
*
|
|
328
|
+
* this.get('content-type');
|
|
329
|
+
* // => true
|
|
330
|
+
*/
|
|
331
|
+
has(field) {
|
|
332
|
+
return this.res.hasHeader(field);
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Set header `field` to `val` or pass
|
|
336
|
+
* an object of header fields.
|
|
337
|
+
*
|
|
338
|
+
* Examples:
|
|
339
|
+
*
|
|
340
|
+
* this.set('Foo', ['bar', 'baz']);
|
|
341
|
+
* this.set('Accept', 'application/json');
|
|
342
|
+
* this.set({ Accept: 'text/plain', 'X-API-Key': 'tobi' });
|
|
343
|
+
*/
|
|
344
|
+
set(field, val) {
|
|
345
|
+
if (this.headerSent)
|
|
346
|
+
return;
|
|
347
|
+
if (typeof field === 'string') {
|
|
348
|
+
if (Array.isArray(val)) {
|
|
349
|
+
val = val.map(v => {
|
|
350
|
+
return typeof v === 'string' ? v : String(v);
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
else if (typeof val !== 'string') {
|
|
354
|
+
val = String(val);
|
|
355
|
+
}
|
|
356
|
+
this.res.setHeader(field, val);
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
for (const key in field) {
|
|
360
|
+
this.set(key, field[key]);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Append additional header `field` with value `val`.
|
|
366
|
+
*
|
|
367
|
+
* Examples:
|
|
368
|
+
*
|
|
369
|
+
* ```
|
|
370
|
+
* this.append('Link', ['<http://localhost/>', '<http://localhost:3000/>']);
|
|
371
|
+
* this.append('Set-Cookie', 'foo=bar; Path=/; HttpOnly');
|
|
372
|
+
* this.append('Warning', '199 Miscellaneous warning');
|
|
373
|
+
*/
|
|
374
|
+
append(field, val) {
|
|
375
|
+
const prev = this.get(field);
|
|
376
|
+
let value = val;
|
|
377
|
+
if (prev) {
|
|
378
|
+
value = Array.isArray(prev)
|
|
379
|
+
? prev.concat(value)
|
|
380
|
+
: [prev].concat(val);
|
|
381
|
+
}
|
|
382
|
+
return this.set(field, value);
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Remove header `field`.
|
|
386
|
+
*/
|
|
387
|
+
remove(field) {
|
|
388
|
+
if (this.headerSent)
|
|
389
|
+
return;
|
|
390
|
+
this.res.removeHeader(field);
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Checks if the request is writable.
|
|
394
|
+
* Tests for the existence of the socket
|
|
395
|
+
* as node sometimes does not set it.
|
|
396
|
+
*/
|
|
397
|
+
get writable() {
|
|
398
|
+
// can't write any more after response finished
|
|
399
|
+
// response.writableEnded is available since Node > 12.9
|
|
400
|
+
// https://nodejs.org/api/http.html#http_response_writableended
|
|
401
|
+
// response.finished is undocumented feature of previous Node versions
|
|
402
|
+
// https://stackoverflow.com/questions/16254385/undocumented-response-finished-in-node-js
|
|
403
|
+
if (this.res.writableEnded || this.res.finished)
|
|
404
|
+
return false;
|
|
405
|
+
const socket = this.res.socket;
|
|
406
|
+
// There are already pending outgoing res, but still writable
|
|
407
|
+
// https://github.com/nodejs/node/blob/v4.4.7/lib/_http_server.js#L486
|
|
408
|
+
if (!socket)
|
|
409
|
+
return true;
|
|
410
|
+
return socket.writable;
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Inspect implementation.
|
|
414
|
+
*/
|
|
415
|
+
inspect() {
|
|
416
|
+
if (!this.res)
|
|
417
|
+
return;
|
|
418
|
+
const o = this.toJSON();
|
|
419
|
+
o.body = this.body;
|
|
420
|
+
return o;
|
|
421
|
+
}
|
|
422
|
+
[node_util_1.default.inspect.custom]() {
|
|
423
|
+
return this.inspect();
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Return JSON representation.
|
|
427
|
+
*/
|
|
428
|
+
toJSON() {
|
|
429
|
+
return (0, only_1.default)(this, [
|
|
430
|
+
'status',
|
|
431
|
+
'message',
|
|
432
|
+
'header',
|
|
433
|
+
]);
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Flush any set headers and begin the body
|
|
437
|
+
*/
|
|
438
|
+
flushHeaders() {
|
|
439
|
+
this.res.flushHeaders();
|
|
440
|
+
}
|
|
588
441
|
}
|
|
442
|
+
exports.default = Response;
|
|
443
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVzcG9uc2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvcmVzcG9uc2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBQSw4REFBaUM7QUFDakMseUNBQW9DO0FBQ3BDLDBEQUE2QjtBQUM3Qiw4REFBaUM7QUFFakMsOEVBQXFEO0FBQ3JELDRFQUF5QztBQUN6Qyw4REFBbUM7QUFDbkMsOERBQWlDO0FBQ2pDLHFDQUF1QztBQUN2Qyx3REFBZ0M7QUFDaEMsc0RBQThCO0FBQzlCLGdEQUF3QjtBQUN4QixnREFBd0I7QUFDeEIsMERBQWtDO0FBS2xDLE1BQXFCLFFBQVE7SUFDM0IsR0FBRyxDQUFjO0lBQ2pCLEdBQUcsQ0FBa0I7SUFDckIsR0FBRyxDQUFpQjtJQUNwQixHQUFHLENBQW9CO0lBQ3ZCLE9BQU8sQ0FBVTtJQUVqQixZQUFZLEdBQWdCLEVBQUUsR0FBc0IsRUFBRSxHQUFvQixFQUFFLEdBQW1CO1FBQzdGLElBQUksQ0FBQyxHQUFHLEdBQUcsR0FBRyxDQUFDO1FBQ2YsSUFBSSxDQUFDLEdBQUcsR0FBRyxHQUFHLENBQUM7UUFDZixJQUFJLENBQUMsR0FBRyxHQUFHLEdBQUcsQ0FBQztRQUNmLElBQUksQ0FBQyxHQUFHLEdBQUcsR0FBRyxDQUFDO0lBQ2pCLENBQUM7SUFFRDs7T0FFRztJQUNILElBQUksTUFBTTtRQUNSLE9BQU8sSUFBSSxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUM7SUFDekIsQ0FBQztJQUVEOztPQUVHO0lBQ0gsSUFBSSxNQUFNO1FBQ1IsT0FBTyxJQUFJLENBQUMsR0FBRyxDQUFDLFVBQVUsRUFBRSxJQUFJLEVBQUUsQ0FBQztJQUNyQyxDQUFDO0lBRUQ7O09BRUc7SUFDSCxJQUFJLE9BQU87UUFDVCxPQUFPLElBQUksQ0FBQyxNQUFNLENBQUM7SUFDckIsQ0FBQztJQUVELGVBQWUsQ0FBVTtJQUV6Qjs7T0FFRztJQUNILElBQUksTUFBTTtRQUNSLE9BQU8sSUFBSSxDQUFDLEdBQUcsQ0FB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
|