@adonisjs/http-server 6.8.2-1 → 6.8.2-10

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 (103) hide show
  1. package/LICENSE.md +1 -1
  2. package/README.md +3 -6
  3. package/build/factories/http_context.d.ts +10 -0
  4. package/build/factories/http_context.js +27 -0
  5. package/build/factories/http_server.d.ts +8 -0
  6. package/build/factories/http_server.js +26 -0
  7. package/build/factories/main.js +8 -0
  8. package/build/factories/qs_parser_factory.d.ts +10 -0
  9. package/build/factories/qs_parser_factory.js +18 -0
  10. package/build/factories/request.d.ts +10 -0
  11. package/build/factories/request.js +31 -0
  12. package/build/factories/response.d.ts +10 -0
  13. package/build/factories/response.js +34 -0
  14. package/build/factories/router.d.ts +10 -0
  15. package/build/factories/router.js +25 -0
  16. package/build/factories/server_factory.d.ts +10 -0
  17. package/build/factories/server_factory.js +34 -0
  18. package/build/index.js +8 -0
  19. package/build/src/cookies/client.d.ts +25 -0
  20. package/build/src/cookies/client.js +42 -0
  21. package/build/src/cookies/drivers/encrypted.d.ts +12 -0
  22. package/build/src/cookies/drivers/encrypted.js +20 -0
  23. package/build/src/cookies/drivers/plain.d.ts +12 -0
  24. package/build/src/cookies/drivers/plain.js +20 -0
  25. package/build/src/cookies/drivers/signed.d.ts +12 -0
  26. package/build/src/cookies/drivers/signed.js +20 -0
  27. package/build/src/cookies/parser.d.ts +28 -0
  28. package/build/src/cookies/parser.js +98 -0
  29. package/build/src/cookies/serializer.d.ts +22 -0
  30. package/build/src/cookies/serializer.js +40 -0
  31. package/build/src/debug.js +8 -0
  32. package/build/src/define_config.d.ts +3 -0
  33. package/build/src/define_config.js +11 -0
  34. package/build/src/define_middleware.d.ts +5 -0
  35. package/build/src/define_middleware.js +18 -0
  36. package/build/src/exception_handler.d.ts +93 -6
  37. package/build/src/exception_handler.js +203 -44
  38. package/build/src/exceptions.d.ts +6 -0
  39. package/build/src/exceptions.js +11 -0
  40. package/build/src/helpers.d.ts +14 -0
  41. package/build/src/helpers.js +43 -0
  42. package/build/src/http_context/local_storage.d.ts +3 -0
  43. package/build/src/http_context/local_storage.js +25 -0
  44. package/build/src/http_context/main.d.ts +36 -0
  45. package/build/src/http_context/main.js +54 -0
  46. package/build/src/qs.d.ts +4 -0
  47. package/build/src/qs.js +12 -0
  48. package/build/src/redirect.d.ts +24 -0
  49. package/build/src/redirect.js +60 -0
  50. package/build/src/request.d.ts +466 -0
  51. package/build/src/request.js +542 -0
  52. package/build/src/response.d.ts +425 -1
  53. package/build/src/response.js +611 -11
  54. package/build/src/router/brisk.d.ts +22 -0
  55. package/build/src/router/brisk.js +44 -2
  56. package/build/src/router/executor.d.ts +6 -1
  57. package/build/src/router/executor.js +14 -1
  58. package/build/src/router/factories/use_return_value.d.ts +4 -0
  59. package/build/src/router/factories/use_return_value.js +16 -3
  60. package/build/src/router/group.d.ts +47 -0
  61. package/build/src/router/group.js +88 -0
  62. package/build/src/router/lookup_store/main.d.ts +32 -0
  63. package/build/src/router/lookup_store/main.js +49 -0
  64. package/build/src/router/lookup_store/route_finder.d.ts +13 -0
  65. package/build/src/router/lookup_store/route_finder.js +21 -0
  66. package/build/src/router/lookup_store/url_builder.d.ts +36 -0
  67. package/build/src/router/lookup_store/url_builder.js +99 -2
  68. package/build/src/router/main.d.ts +92 -4
  69. package/build/src/router/main.js +146 -0
  70. package/build/src/router/matchers.d.ts +13 -0
  71. package/build/src/router/matchers.js +21 -0
  72. package/build/src/router/parser.d.ts +5 -0
  73. package/build/src/router/parser.js +17 -0
  74. package/build/src/router/resource.d.ts +30 -1
  75. package/build/src/router/resource.js +93 -1
  76. package/build/src/router/route.d.ts +65 -0
  77. package/build/src/router/route.js +151 -2
  78. package/build/src/router/store.d.ts +54 -0
  79. package/build/src/router/store.js +110 -2
  80. package/build/src/server/factories/final_handler.d.ts +7 -1
  81. package/build/src/server/factories/final_handler.js +15 -2
  82. package/build/src/server/factories/middleware_handler.d.ts +3 -0
  83. package/build/src/server/factories/middleware_handler.js +11 -0
  84. package/build/src/server/factories/write_response.d.ts +4 -0
  85. package/build/src/server/factories/write_response.js +12 -0
  86. package/build/src/server/main.d.ts +48 -0
  87. package/build/src/server/main.js +142 -5
  88. package/build/src/types/base.d.ts +12 -0
  89. package/build/src/types/base.js +8 -0
  90. package/build/src/types/main.js +8 -0
  91. package/build/src/types/middleware.d.ts +18 -0
  92. package/build/src/types/middleware.js +8 -0
  93. package/build/src/types/qs.d.ts +53 -0
  94. package/build/src/types/qs.js +8 -0
  95. package/build/src/types/request.d.ts +32 -0
  96. package/build/src/types/request.js +8 -0
  97. package/build/src/types/response.d.ts +27 -0
  98. package/build/src/types/response.js +8 -0
  99. package/build/src/types/route.d.ts +89 -1
  100. package/build/src/types/route.js +8 -0
  101. package/build/src/types/server.d.ts +36 -1
  102. package/build/src/types/server.js +8 -0
  103. package/package.json +52 -74
@@ -1,3 +1,11 @@
1
+ /*
2
+ * @adonisjs/http-server
3
+ *
4
+ * (c) AdonisJS
5
+ *
6
+ * For the full copyright and license information, please view the LICENSE
7
+ * file that was distributed with this source code.
8
+ */
1
9
  import etag from 'etag';
2
10
  import vary from 'vary';
3
11
  import fresh from 'fresh';
@@ -5,44 +13,92 @@ import mime from 'mime-types';
5
13
  import destroy from 'destroy';
6
14
  import { extname } from 'node:path';
7
15
  import onFinished from 'on-finished';
8
- import { promisify } from 'node:util';
9
16
  import json from '@poppinss/utils/json';
10
17
  import Macroable from '@poppinss/macroable';
11
- import { createReadStream, stat } from 'node:fs';
18
+ import { createReadStream } from 'node:fs';
19
+ import { stat } from 'node:fs/promises';
12
20
  import { RuntimeException } from '@poppinss/utils';
13
21
  import contentDisposition from 'content-disposition';
14
22
  import { Redirect } from './redirect.js';
15
23
  import { CookieSerializer } from './cookies/serializer.js';
16
24
  import { E_HTTP_REQUEST_ABORTED } from './exceptions.js';
17
- const statFn = promisify(stat);
18
25
  const CACHEABLE_HTTP_METHODS = ['GET', 'HEAD'];
26
+ /**
27
+ * The response is a wrapper over [ServerResponse](https://nodejs.org/api/http.html#http_class_http_serverresponse)
28
+ * streamlining the process of writing response body and automatically setting up appropriate headers.
29
+ */
19
30
  export class Response extends Macroable {
20
31
  request;
21
32
  response;
33
+ /**
34
+ * Query string parser
35
+ */
22
36
  #qs;
37
+ /**
38
+ * Outgoing headers
39
+ */
23
40
  #headers = {};
41
+ /**
42
+ * Has explicit status been set
43
+ */
24
44
  #hasExplicitStatus = false;
45
+ /**
46
+ * Cookies serializer to serialize the outgoing cookies
47
+ */
25
48
  #cookieSerializer;
49
+ /**
50
+ * Router is used to make the redirect URLs from routes
51
+ */
26
52
  #router;
53
+ /**
54
+ * Response config
55
+ */
27
56
  #config;
57
+ /**
58
+ * Does response has body set that will written to the
59
+ * response socket at the end of the request
60
+ */
28
61
  get hasLazyBody() {
29
62
  return !!(this.lazyBody.content || this.lazyBody.fileToStream || this.lazyBody.stream);
30
63
  }
64
+ /**
65
+ * Find if the response has non-stream content
66
+ */
31
67
  get hasContent() {
32
68
  return !!this.lazyBody.content;
33
69
  }
70
+ /**
71
+ * Returns true when response body is set using "response.stream"
72
+ * method
73
+ */
34
74
  get hasStream() {
35
75
  return !!this.lazyBody.stream;
36
76
  }
77
+ /**
78
+ * Returns true when response body is set using "response.download"
79
+ * or "response.attachment" methods
80
+ */
37
81
  get hasFileToStream() {
38
82
  return !!this.lazyBody.fileToStream;
39
83
  }
84
+ /**
85
+ * Returns the response content. Check if the response
86
+ * has content using the "hasContent" method
87
+ */
40
88
  get content() {
41
89
  return this.lazyBody.content;
42
90
  }
91
+ /**
92
+ * Returns reference to the stream set using "response.stream"
93
+ * method
94
+ */
43
95
  get outgoingStream() {
44
96
  return this.lazyBody.stream?.[0];
45
97
  }
98
+ /**
99
+ * Returns reference to the file path set using "response.stream"
100
+ * method.
101
+ */
46
102
  get fileToStream() {
47
103
  return this.lazyBody.fileToStream
48
104
  ? {
@@ -51,7 +107,16 @@ export class Response extends Macroable {
51
107
  }
52
108
  : undefined;
53
109
  }
110
+ /**
111
+ * Lazy body is used to set the response body. However, do not
112
+ * write it on the socket immediately unless `response.finish`
113
+ * is called.
114
+ */
54
115
  lazyBody = {};
116
+ /**
117
+ * The ctx will be set by the context itself. It creates a circular
118
+ * reference
119
+ */
55
120
  ctx;
56
121
  constructor(request, response, encryption, config, router, qs) {
57
122
  super();
@@ -62,30 +127,68 @@ export class Response extends Macroable {
62
127
  this.#router = router;
63
128
  this.#cookieSerializer = new CookieSerializer(encryption);
64
129
  }
130
+ /**
131
+ * Returns a boolean telling if response is finished or not.
132
+ * Any more attempts to update headers or body will result
133
+ * in raised exceptions.
134
+ */
65
135
  get finished() {
66
136
  return this.response.writableFinished;
67
137
  }
138
+ /**
139
+ * Returns a boolean telling if response headers has been sent or not.
140
+ * Any more attempts to update headers will result in raised
141
+ * exceptions.
142
+ */
68
143
  get headersSent() {
69
144
  return this.response.headersSent;
70
145
  }
146
+ /**
147
+ * Returns a boolean telling if response headers and body is written
148
+ * or not. When value is `true`, you can feel free to write headers
149
+ * and body.
150
+ */
71
151
  get isPending() {
72
152
  return !this.headersSent && !this.finished;
73
153
  }
154
+ /**
155
+ * Normalizes header value to a string or an array of string
156
+ */
74
157
  #castHeaderValue(value) {
75
158
  return Array.isArray(value) ? value.map(String) : String(value);
76
159
  }
160
+ /**
161
+ * Ends the response by flushing headers and writing body
162
+ */
77
163
  #endResponse(body, statusCode) {
78
- this.flushHeaders(statusCode);
164
+ this.writeHead(statusCode);
165
+ // avoid ArgumentsAdaptorTrampoline from V8 (inspired by fastify)
79
166
  const res = this.response;
80
167
  res.end(body, null, null);
81
168
  }
169
+ /**
170
+ * Returns type for the content body. Only following types are allowed
171
+ *
172
+ * - Dates
173
+ * - Arrays
174
+ * - Booleans
175
+ * - Objects
176
+ * - Strings
177
+ * - Buffer
178
+ */
82
179
  #getDataType(content) {
83
180
  if (Buffer.isBuffer(content)) {
84
181
  return 'buffer';
85
182
  }
183
+ /**
184
+ * Date instance
185
+ */
86
186
  if (content instanceof Date) {
87
187
  return 'date';
88
188
  }
189
+ /**
190
+ * Regular expression
191
+ */
89
192
  if (content instanceof RegExp) {
90
193
  return 'regexp';
91
194
  }
@@ -96,17 +199,34 @@ export class Response extends Macroable {
96
199
  dataType === 'bigint') {
97
200
  return dataType;
98
201
  }
202
+ /**
203
+ * Object
204
+ */
99
205
  if (dataType === 'object') {
100
206
  return 'object';
101
207
  }
102
208
  throw new RuntimeException(`Cannot serialize "${dataType}" to HTTP response`);
103
209
  }
210
+ /**
211
+ * Writes the body with appropriate response headers. Etag header is set
212
+ * when `generateEtag` is set to `true`.
213
+ *
214
+ * Empty body results in `204`.
215
+ */
104
216
  writeBody(content, generateEtag, jsonpCallbackName) {
105
217
  const hasEmptyBody = content === null || content === undefined || content === '';
218
+ /**
219
+ * Set status to "204" when body is empty. The `safeStatus` method only
220
+ * sets the status when no explicit status has been set already
221
+ */
106
222
  if (hasEmptyBody) {
107
223
  this.safeStatus(204);
108
224
  }
109
225
  const statusCode = this.response.statusCode;
226
+ /**
227
+ * Do not process body when status code is less than 200 or is 204 or 304. As per
228
+ * https://tools.ietf.org/html/rfc7230#section-3.3.2
229
+ */
110
230
  if (statusCode && (statusCode < 200 || statusCode === 204 || statusCode === 304)) {
111
231
  this.removeHeader('Content-Type');
112
232
  this.removeHeader('Content-Length');
@@ -114,12 +234,27 @@ export class Response extends Macroable {
114
234
  this.#endResponse();
115
235
  return;
116
236
  }
237
+ /**
238
+ * Body is empty and status code is not "204", "304" and neither under 200.
239
+ */
117
240
  if (hasEmptyBody) {
118
241
  this.removeHeader('Content-Length');
119
242
  this.#endResponse();
120
243
  return;
121
244
  }
245
+ /**
246
+ * Javascript data type for the content. We only handle a subset
247
+ * of data types. Check [[this.getDataType]] method for more
248
+ * info
249
+ */
122
250
  const dataType = this.#getDataType(content);
251
+ /**
252
+ * ----------------------------------------
253
+ * SERIALIZE CONTENT TO A STRING
254
+ * ----------------------------------------
255
+ *
256
+ * Transforming date, number, boolean and object to a string
257
+ */
123
258
  if (dataType === 'object') {
124
259
  content = json.safeStringify(content);
125
260
  }
@@ -132,13 +267,39 @@ export class Response extends Macroable {
132
267
  else if (dataType === 'date') {
133
268
  content = content.toISOString();
134
269
  }
270
+ /*
271
+ * ----------------------------------------
272
+ * MORE MODIFICATIONS FOR JSONP BODY
273
+ * ----------------------------------------
274
+ *
275
+ * If JSONP callback exists, then update the body to be a
276
+ * valid JSONP response
277
+ */
135
278
  if (jsonpCallbackName) {
279
+ /*
280
+ * replace chars not allowed in JavaScript that are in JSON
281
+ * https://github.com/rack/rack-contrib/pull/37
282
+ */
136
283
  content = content.replace(/\u2028/g, '\\u2028').replace(/\u2029/g, '\\u2029');
284
+ // the /**/ is a specific security mitigation for "Rosetta Flash JSONP abuse"
285
+ // https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2014-4671
286
+ // http://miki.it/blog/2014/7/8/abusing-jsonp-with-rosetta-flash/
287
+ // http://drops.wooyun.org/tips/2554
137
288
  content = `/**/ typeof ${jsonpCallbackName} === 'function' && ${jsonpCallbackName}(${content});`;
138
289
  }
290
+ /*
291
+ * ----------------------------------------
292
+ * FINALY GENERATE AN ETAG
293
+ * ----------------------------------------
294
+ *
295
+ * Generate etag if instructed.
296
+ */
139
297
  if (generateEtag) {
140
298
  this.setEtag(content);
141
299
  }
300
+ /**
301
+ * End response when cache is fresh
302
+ */
142
303
  if (generateEtag && this.fresh()) {
143
304
  this.removeHeader('Content-Type');
144
305
  this.removeHeader('Content-Length');
@@ -146,7 +307,29 @@ export class Response extends Macroable {
146
307
  this.#endResponse(null, 304);
147
308
  return;
148
309
  }
310
+ /*
311
+ * ----------------------------------------
312
+ * SET CONTENT-LENGTH HEADER
313
+ * ----------------------------------------
314
+ */
149
315
  this.header('Content-Length', Buffer.byteLength(content));
316
+ /**
317
+ * ----------------------------------------
318
+ * SET CONTENT-TYPE HEADER
319
+ * ----------------------------------------
320
+ *
321
+ * - If it is a JSONP response, then we always set the content type
322
+ * to "text/javascript"
323
+ *
324
+ * - String are checked for HTML and "text/plain" or "text/html" is set
325
+ * accordingly.
326
+ *
327
+ * - "text/plain" is set for "numbers" and "booleans" and "dates"
328
+ *
329
+ * - "application/octet-stream" is set for buffers
330
+ *
331
+ * - "application/json" is set for objects and arrays
332
+ */
150
333
  if (jsonpCallbackName) {
151
334
  this.header('X-Content-Type-Options', 'nosniff');
152
335
  this.safeHeader('Content-Type', 'text/javascript; charset=utf-8');
@@ -174,53 +357,110 @@ export class Response extends Macroable {
174
357
  }
175
358
  this.#endResponse(content);
176
359
  }
360
+ /**
361
+ * Stream the body to the response and handles cleaning up the stream
362
+ */
177
363
  streamBody(body, errorCallback) {
178
364
  return new Promise((resolve) => {
179
365
  let finished = false;
366
+ /*
367
+ * Listen for errors on the stream and properly destroy
368
+ * stream
369
+ */
180
370
  body.on('error', (error) => {
371
+ /* c8 ignore next 3 */
181
372
  if (finished) {
182
373
  return;
183
374
  }
184
375
  finished = true;
185
376
  destroy(body);
186
377
  this.type('text');
187
- if (typeof errorCallback === 'function') {
188
- this.#endResponse(...errorCallback(error));
378
+ if (!this.headersSent) {
379
+ if (typeof errorCallback === 'function') {
380
+ this.#endResponse(...errorCallback(error));
381
+ }
382
+ else {
383
+ this.#endResponse(error.code === 'ENOENT' ? 'File not found' : 'Cannot process file', error.code === 'ENOENT' ? 404 : 500);
384
+ }
189
385
  }
190
386
  else {
191
- this.#endResponse(error.code === 'ENOENT' ? 'File not found' : 'Cannot process file', error.code === 'ENOENT' ? 404 : 500);
192
- resolve();
387
+ this.response.destroy();
193
388
  }
389
+ resolve();
194
390
  });
195
- body.on('end', resolve);
391
+ /*
392
+ * Listen for end and resolve the promise
393
+ */
394
+ body.on('end', () => {
395
+ if (!this.headersSent) {
396
+ this.#endResponse();
397
+ }
398
+ resolve();
399
+ });
400
+ /*
401
+ * Cleanup stream when finishing response
402
+ */
196
403
  onFinished(this.response, () => {
197
404
  finished = true;
198
405
  destroy(body);
199
406
  });
407
+ /*
408
+ * Pipe stream
409
+ */
200
410
  this.flushHeaders();
201
411
  body.pipe(this.response);
202
412
  });
203
413
  }
414
+ /**
415
+ * Downloads a file by streaming it to the response
416
+ */
204
417
  async streamFileForDownload(filePath, generateEtag, errorCallback) {
205
418
  try {
206
- const stats = await statFn(filePath);
419
+ const stats = await stat(filePath);
207
420
  if (!stats || !stats.isFile()) {
208
421
  throw new TypeError('response.download only accepts path to a file');
209
422
  }
423
+ /*
424
+ * Set appropriate headers
425
+ */
210
426
  this.header('Last-Modified', stats.mtime.toUTCString());
211
427
  this.type(extname(filePath));
428
+ /*
429
+ * Set the etag when instructed.
430
+ */
212
431
  if (generateEtag) {
213
432
  this.setEtag(stats, true);
214
433
  }
434
+ /*
435
+ * Do not stream files for HEAD request, but set the appropriate
436
+ * status code.
437
+ *
438
+ * 200: When not using etags or cache is not fresh. This forces browser
439
+ * to always make a GET request
440
+ *
441
+ * 304: When etags are used and cache is fresh
442
+ */
215
443
  if (this.request.method === 'HEAD') {
216
444
  this.#endResponse(null, generateEtag && this.fresh() ? 304 : 200);
217
445
  return;
218
446
  }
447
+ /*
448
+ * Regardless of request method, if we are using etags and
449
+ * cache is fresh, then we must respond with 304
450
+ */
219
451
  if (generateEtag && this.fresh()) {
220
452
  this.#endResponse(null, 304);
221
453
  return;
222
454
  }
455
+ /*
456
+ * Fix for https://tools.ietf.org/html/rfc7232#section-4.1. It is
457
+ * recommended to ignore headers other than Cache-Control,
458
+ * Content-Location, Date, ETag, Expires, and Vary.
459
+ */
223
460
  this.header('Content-length', stats.size);
461
+ /*
462
+ * Finally stream the file
463
+ */
224
464
  return this.streamBody(createReadStream(filePath), errorCallback);
225
465
  }
226
466
  catch (error) {
@@ -234,20 +474,55 @@ export class Response extends Macroable {
234
474
  }
235
475
  }
236
476
  }
237
- flushHeaders(statusCode) {
477
+ /**
478
+ * Writes headers with the Node.js res object using the
479
+ * response.setHeader method
480
+ */
481
+ flushHeaders() {
482
+ if (!this.headersSent) {
483
+ for (let key in this.#headers) {
484
+ const value = this.#headers[key];
485
+ if (value) {
486
+ this.response.setHeader(key, value);
487
+ }
488
+ }
489
+ }
490
+ }
491
+ /**
492
+ * Calls res.writeHead on the Node.js res object.
493
+ */
494
+ writeHead(statusCode) {
238
495
  this.response.writeHead(statusCode || this.response.statusCode, this.#headers);
239
496
  return this;
240
497
  }
498
+ /**
499
+ * Returns the existing value for a given HTTP response
500
+ * header.
501
+ */
241
502
  getHeader(key) {
242
503
  const value = this.#headers[key.toLowerCase()];
243
504
  return value === undefined ? this.response.getHeader(key) : value;
244
505
  }
506
+ /**
507
+ * Get response headers
508
+ */
245
509
  getHeaders() {
246
510
  return {
247
511
  ...this.response.getHeaders(),
248
512
  ...this.#headers,
249
513
  };
250
514
  }
515
+ /**
516
+ * Set header on the response. To `append` values to the existing header, we suggest
517
+ * using [[append]] method.
518
+ *
519
+ * If `value` is non existy, then header won't be set.
520
+ *
521
+ * @example
522
+ * ```js
523
+ * response.header('content-type', 'application/json')
524
+ * ```
525
+ */
251
526
  header(key, value) {
252
527
  if (value === null || value === undefined) {
253
528
  return this;
@@ -255,6 +530,17 @@ export class Response extends Macroable {
255
530
  this.#headers[key.toLowerCase()] = this.#castHeaderValue(value);
256
531
  return this;
257
532
  }
533
+ /**
534
+ * Append value to an existing header. To replace the value, we suggest using
535
+ * [[header]] method.
536
+ *
537
+ * If `value` is not existy, then header won't be set.
538
+ *
539
+ * @example
540
+ * ```js
541
+ * response.append('set-cookie', 'username=virk')
542
+ * ```
543
+ */
258
544
  append(key, value) {
259
545
  if (value === null || value === undefined) {
260
546
  return this;
@@ -262,6 +548,10 @@ export class Response extends Macroable {
262
548
  key = key.toLowerCase();
263
549
  let existingHeader = this.getHeader(key);
264
550
  let casted = this.#castHeaderValue(value);
551
+ /**
552
+ * If there isn't any header, then setHeader right
553
+ * away
554
+ */
265
555
  if (!existingHeader) {
266
556
  this.#headers[key] = casted;
267
557
  return this;
@@ -273,12 +563,18 @@ export class Response extends Macroable {
273
563
  this.#headers[key] = casted;
274
564
  return this;
275
565
  }
566
+ /**
567
+ * Adds HTTP response header, when it doesn't exists already.
568
+ */
276
569
  safeHeader(key, value) {
277
570
  if (!this.getHeader(key)) {
278
571
  this.header(key, value);
279
572
  }
280
573
  return this;
281
574
  }
575
+ /**
576
+ * Removes the existing response header from being sent.
577
+ */
282
578
  removeHeader(key) {
283
579
  key = key.toLowerCase();
284
580
  this.response.removeHeader(key);
@@ -287,14 +583,24 @@ export class Response extends Macroable {
287
583
  }
288
584
  return this;
289
585
  }
586
+ /**
587
+ * Returns the status code for the response
588
+ */
290
589
  getStatus() {
291
590
  return this.response.statusCode;
292
591
  }
592
+ /**
593
+ * Set HTTP status code
594
+ */
293
595
  status(code) {
294
596
  this.#hasExplicitStatus = true;
295
597
  this.response.statusCode = code;
296
598
  return this;
297
599
  }
600
+ /**
601
+ * Set's status code only when it's not explictly
602
+ * set
603
+ */
298
604
  safeStatus(code) {
299
605
  if (this.#hasExplicitStatus) {
300
606
  return this;
@@ -302,19 +608,65 @@ export class Response extends Macroable {
302
608
  this.response.statusCode = code;
303
609
  return this;
304
610
  }
611
+ /**
612
+ * Set response type by looking up for the mime-type using
613
+ * partial types like file extensions.
614
+ *
615
+ * Make sure to read [mime-types](https://www.npmjs.com/package/mime-types) docs
616
+ * too.
617
+ *
618
+ * @example
619
+ * ```js
620
+ * response.type('.json') // Content-type: application/json
621
+ * ```
622
+ */
305
623
  type(type, charset) {
306
624
  type = charset ? `${type}; charset=${charset}` : type;
307
625
  this.header('Content-Type', mime.contentType(type));
308
626
  return this;
309
627
  }
628
+ /**
629
+ * Set the Vary HTTP header
630
+ */
310
631
  vary(field) {
311
632
  vary(this.response, field);
312
633
  return this;
313
634
  }
635
+ /**
636
+ * Set etag by computing hash from the body. This class will set the etag automatically
637
+ * when `etag = true` in the defined config object.
638
+ *
639
+ * Use this function, when you want to compute etag manually for some other resons.
640
+ */
314
641
  setEtag(body, weak = false) {
315
642
  this.header('Etag', etag(body, { weak }));
316
643
  return this;
317
644
  }
645
+ /**
646
+ * Returns a boolean telling if the new response etag evaluates same
647
+ * as the request header `if-none-match`. In case of `true`, the
648
+ * server must return `304` response, telling the browser to
649
+ * use the client cache.
650
+ *
651
+ * You won't have to deal with this method directly, since AdonisJs will
652
+ * handle this for you when `http.etag = true` inside `config/app.js` file.
653
+ *
654
+ * However, this is how you can use it manually.
655
+ *
656
+ * @example
657
+ * ```js
658
+ * const responseBody = view.render('some-view')
659
+ *
660
+ * // sets the HTTP etag header for response
661
+ * response.setEtag(responseBody)
662
+ *
663
+ * if (response.fresh()) {
664
+ * response.sendStatus(304)
665
+ * } else {
666
+ * response.send(responseBody)
667
+ * }
668
+ * ```
669
+ */
318
670
  fresh() {
319
671
  if (this.request.method && !CACHEABLE_HTTP_METHODS.includes(this.request.method)) {
320
672
  return false;
@@ -325,35 +677,124 @@ export class Response extends Macroable {
325
677
  }
326
678
  return false;
327
679
  }
680
+ /**
681
+ * Returns the response body. Returns null when response
682
+ * body is a stream
683
+ */
328
684
  getBody() {
329
685
  if (this.lazyBody.content) {
330
686
  return this.lazyBody.content[0];
331
687
  }
332
688
  return null;
333
689
  }
690
+ /**
691
+ * Send the body as response and optionally generate etag. The default value
692
+ * is read from `config/app.js` file, using `http.etag` property.
693
+ *
694
+ * This method buffers the body if `explicitEnd = true`, which is the default
695
+ * behavior and do not change, unless you know what you are doing.
696
+ */
334
697
  send(body, generateEtag = this.#config.etag) {
335
698
  this.lazyBody.content = [body, generateEtag];
336
699
  }
700
+ /**
701
+ * Alias of [[send]]
702
+ */
337
703
  json(body, generateEtag = this.#config.etag) {
338
704
  return this.send(body, generateEtag);
339
705
  }
706
+ /**
707
+ * Writes response as JSONP. The callback name is resolved as follows, with priority
708
+ * from top to bottom.
709
+ *
710
+ * 1. Explicitly defined as 2nd Param.
711
+ * 2. Fetch from request query string.
712
+ * 3. Use the config value `http.jsonpCallbackName` from `config/app.js`.
713
+ * 4. Fallback to `callback`.
714
+ *
715
+ * This method buffers the body if `explicitEnd = true`, which is the default
716
+ * behavior and do not change, unless you know what you are doing.
717
+ */
340
718
  jsonp(body, callbackName = this.#config.jsonpCallbackName, generateEtag = this.#config.etag) {
341
719
  this.lazyBody.content = [body, generateEtag, callbackName];
342
720
  }
721
+ /**
722
+ * Pipe stream to the response. This method will gracefully destroy
723
+ * the stream, avoiding memory leaks.
724
+ *
725
+ * If `raiseErrors=false`, then this method will self handle all the exceptions by
726
+ * writing a generic HTTP response. To have more control over the error, it is
727
+ * recommended to set `raiseErrors=true` and wrap this function inside a
728
+ * `try/catch` statement.
729
+ *
730
+ * Streaming a file from the disk and showing 404 when file is missing.
731
+ *
732
+ * @example
733
+ * ```js
734
+ * // Errors handled automatically with generic HTTP response
735
+ * response.stream(fs.createReadStream('file.txt'))
736
+ *
737
+ * // Manually handle (note the await call)
738
+ * try {
739
+ * await response.stream(fs.createReadStream('file.txt'))
740
+ * } catch () {
741
+ * response.status(404).send('File not found')
742
+ * }
743
+ * ```
744
+ */
343
745
  stream(body, errorCallback) {
344
746
  if (typeof body.pipe !== 'function' || !body.readable || typeof body.read !== 'function') {
345
747
  throw new TypeError('response.stream accepts a readable stream only');
346
748
  }
347
749
  this.lazyBody.stream = [body, errorCallback];
348
750
  }
751
+ /**
752
+ * Download file by streaming it from the file path. This method will setup
753
+ * appropriate `Content-type`, `Content-type` and `Last-modified` headers.
754
+ *
755
+ * Unexpected stream errors are handled gracefully to avoid memory leaks.
756
+ *
757
+ * If `raiseErrors=false`, then this method will self handle all the exceptions by
758
+ * writing a generic HTTP response. To have more control over the error, it is
759
+ * recommended to set `raiseErrors=true` and wrap this function inside a
760
+ * `try/catch` statement.
761
+ *
762
+ * @example
763
+ * ```js
764
+ * // Errors handled automatically with generic HTTP response
765
+ * response.download('somefile.jpg')
766
+ *
767
+ * // Manually handle (note the await call)
768
+ * try {
769
+ * await response.download('somefile.jpg')
770
+ * } catch (error) {
771
+ * response.status(error.code === 'ENOENT' ? 404 : 500)
772
+ * response.send('Cannot process file')
773
+ * }
774
+ * ```
775
+ */
349
776
  download(filePath, generateEtag = this.#config.etag, errorCallback) {
350
777
  this.lazyBody.fileToStream = [filePath, generateEtag, errorCallback];
351
778
  }
779
+ /**
780
+ * Download the file by forcing the user to save the file vs displaying it
781
+ * within the browser.
782
+ *
783
+ * Internally calls [[download]]
784
+ */
352
785
  attachment(filePath, name, disposition, generateEtag, errorCallback) {
353
786
  name = name || filePath;
354
787
  this.header('Content-Disposition', contentDisposition(name, { type: disposition }));
355
788
  return this.download(filePath, generateEtag, errorCallback);
356
789
  }
790
+ /**
791
+ * Set the location header.
792
+ *
793
+ * @example
794
+ * ```js
795
+ * response.location('/login')
796
+ * ```
797
+ */
357
798
  location(url) {
358
799
  this.header('Location', url);
359
800
  return this;
@@ -371,19 +812,35 @@ export class Response extends Macroable {
371
812
  }
372
813
  return handler;
373
814
  }
815
+ /**
816
+ * Abort the request with custom body and a status code. 400 is
817
+ * used when status is not defined
818
+ */
374
819
  abort(body, status) {
375
820
  throw E_HTTP_REQUEST_ABORTED.invoke(body, status || 400);
376
821
  }
822
+ /**
823
+ * Abort the request with custom body and a status code when
824
+ * passed condition returns `true`
825
+ */
377
826
  abortIf(condition, body, status) {
378
827
  if (condition) {
379
828
  this.abort(body, status);
380
829
  }
381
830
  }
831
+ /**
832
+ * Abort the request with custom body and a status code when
833
+ * passed condition returns `false`
834
+ */
382
835
  abortUnless(condition, body, status) {
383
836
  if (!condition) {
384
837
  this.abort(body, status);
385
838
  }
386
839
  }
840
+ /**
841
+ * Set signed cookie as the response header. The inline options overrides
842
+ * all options from the config.
843
+ */
387
844
  cookie(key, value, options) {
388
845
  options = Object.assign({}, this.#config.cookie, options);
389
846
  const serialized = this.#cookieSerializer.sign(key, value, options);
@@ -393,6 +850,10 @@ export class Response extends Macroable {
393
850
  this.append('set-cookie', serialized);
394
851
  return this;
395
852
  }
853
+ /**
854
+ * Set encrypted cookie as the response header. The inline options overrides
855
+ * all options from the config.
856
+ */
396
857
  encryptedCookie(key, value, options) {
397
858
  options = Object.assign({}, this.#config.cookie, options);
398
859
  const serialized = this.#cookieSerializer.encrypt(key, value, options);
@@ -402,6 +863,10 @@ export class Response extends Macroable {
402
863
  this.append('set-cookie', serialized);
403
864
  return this;
404
865
  }
866
+ /**
867
+ * Set unsigned cookie as the response header. The inline options overrides
868
+ * all options from the config.
869
+ */
405
870
  plainCookie(key, value, options) {
406
871
  options = Object.assign({}, this.#config.cookie, options);
407
872
  const serialized = this.#cookieSerializer.encode(key, value, options);
@@ -411,6 +876,9 @@ export class Response extends Macroable {
411
876
  this.append('set-cookie', serialized);
412
877
  return this;
413
878
  }
879
+ /**
880
+ * Clear existing cookie.
881
+ */
414
882
  clearCookie(key, options) {
415
883
  options = Object.assign({}, this.#config.cookie, options);
416
884
  options.expires = new Date(1);
@@ -419,6 +887,12 @@ export class Response extends Macroable {
419
887
  this.append('set-cookie', serialized);
420
888
  return this;
421
889
  }
890
+ /**
891
+ * Finishes the response by writing the lazy body, when `explicitEnd = true`
892
+ * and response is already pending.
893
+ *
894
+ * Calling this method twice or when `explicitEnd = false` is noop.
895
+ */
422
896
  finish() {
423
897
  if (!this.isPending) {
424
898
  return;
@@ -437,170 +911,296 @@ export class Response extends Macroable {
437
911
  }
438
912
  this.#endResponse();
439
913
  }
914
+ /**
915
+ * Shorthand method to finish request with "100" status code
916
+ */
440
917
  continue() {
441
918
  this.status(100);
442
919
  return this.send(null, false);
443
920
  }
921
+ /**
922
+ * Shorthand method to finish request with "101" status code
923
+ */
444
924
  switchingProtocols() {
445
925
  this.status(101);
446
926
  return this.send(null, false);
447
927
  }
928
+ /**
929
+ * Shorthand method to finish request with "200" status code
930
+ */
448
931
  ok(body, generateEtag) {
449
932
  this.status(200);
450
933
  return this.send(body, generateEtag);
451
934
  }
935
+ /**
936
+ * Shorthand method to finish request with "201" status code
937
+ */
452
938
  created(body, generateEtag) {
453
939
  this.status(201);
454
940
  return this.send(body, generateEtag);
455
941
  }
942
+ /**
943
+ * Shorthand method to finish request with "202" status code
944
+ */
456
945
  accepted(body, generateEtag) {
457
946
  this.status(202);
458
947
  return this.send(body, generateEtag);
459
948
  }
949
+ /**
950
+ * Shorthand method to finish request with "203" status code
951
+ */
460
952
  nonAuthoritativeInformation(body, generateEtag) {
461
953
  this.status(203);
462
954
  return this.send(body, generateEtag);
463
955
  }
956
+ /**
957
+ * Shorthand method to finish request with "204" status code
958
+ */
464
959
  noContent() {
465
960
  this.status(204);
466
961
  return this.send(null, false);
467
962
  }
963
+ /**
964
+ * Shorthand method to finish request with "205" status code
965
+ */
468
966
  resetContent() {
469
967
  this.status(205);
470
968
  return this.send(null, false);
471
969
  }
970
+ /**
971
+ * Shorthand method to finish request with "206" status code
972
+ */
472
973
  partialContent(body, generateEtag) {
473
974
  this.status(206);
474
975
  return this.send(body, generateEtag);
475
976
  }
977
+ /**
978
+ * Shorthand method to finish request with "300" status code
979
+ */
476
980
  multipleChoices(body, generateEtag) {
477
981
  this.status(300);
478
982
  return this.send(body, generateEtag);
479
983
  }
984
+ /**
985
+ * Shorthand method to finish request with "301" status code
986
+ */
480
987
  movedPermanently(body, generateEtag) {
481
988
  this.status(301);
482
989
  return this.send(body, generateEtag);
483
990
  }
991
+ /**
992
+ * Shorthand method to finish request with "302" status code
993
+ */
484
994
  movedTemporarily(body, generateEtag) {
485
995
  this.status(302);
486
996
  return this.send(body, generateEtag);
487
997
  }
998
+ /**
999
+ * Shorthand method to finish request with "303" status code
1000
+ */
488
1001
  seeOther(body, generateEtag) {
489
1002
  this.status(303);
490
1003
  return this.send(body, generateEtag);
491
1004
  }
1005
+ /**
1006
+ * Shorthand method to finish request with "304" status code
1007
+ */
492
1008
  notModified(body, generateEtag) {
493
1009
  this.status(304);
494
1010
  return this.send(body, generateEtag);
495
1011
  }
1012
+ /**
1013
+ * Shorthand method to finish request with "305" status code
1014
+ */
496
1015
  useProxy(body, generateEtag) {
497
1016
  this.status(305);
498
1017
  return this.send(body, generateEtag);
499
1018
  }
1019
+ /**
1020
+ * Shorthand method to finish request with "307" status code
1021
+ */
500
1022
  temporaryRedirect(body, generateEtag) {
501
1023
  this.status(307);
502
1024
  return this.send(body, generateEtag);
503
1025
  }
1026
+ /**
1027
+ * Shorthand method to finish request with "400" status code
1028
+ */
504
1029
  badRequest(body, generateEtag) {
505
1030
  this.status(400);
506
1031
  return this.send(body, generateEtag);
507
1032
  }
1033
+ /**
1034
+ * Shorthand method to finish request with "401" status code
1035
+ */
508
1036
  unauthorized(body, generateEtag) {
509
1037
  this.status(401);
510
1038
  return this.send(body, generateEtag);
511
1039
  }
1040
+ /**
1041
+ * Shorthand method to finish request with "402" status code
1042
+ */
512
1043
  paymentRequired(body, generateEtag) {
513
1044
  this.status(402);
514
1045
  return this.send(body, generateEtag);
515
1046
  }
1047
+ /**
1048
+ * Shorthand method to finish request with "403" status code
1049
+ */
516
1050
  forbidden(body, generateEtag) {
517
1051
  this.status(403);
518
1052
  return this.send(body, generateEtag);
519
1053
  }
1054
+ /**
1055
+ * Shorthand method to finish request with "404" status code
1056
+ */
520
1057
  notFound(body, generateEtag) {
521
1058
  this.status(404);
522
1059
  return this.send(body, generateEtag);
523
1060
  }
1061
+ /**
1062
+ * Shorthand method to finish request with "405" status code
1063
+ */
524
1064
  methodNotAllowed(body, generateEtag) {
525
1065
  this.status(405);
526
1066
  return this.send(body, generateEtag);
527
1067
  }
1068
+ /**
1069
+ * Shorthand method to finish request with "406" status code
1070
+ */
528
1071
  notAcceptable(body, generateEtag) {
529
1072
  this.status(406);
530
1073
  return this.send(body, generateEtag);
531
1074
  }
1075
+ /**
1076
+ * Shorthand method to finish request with "407" status code
1077
+ */
532
1078
  proxyAuthenticationRequired(body, generateEtag) {
533
1079
  this.status(407);
534
1080
  return this.send(body, generateEtag);
535
1081
  }
1082
+ /**
1083
+ * Shorthand method to finish request with "408" status code
1084
+ */
536
1085
  requestTimeout(body, generateEtag) {
537
1086
  this.status(408);
538
1087
  return this.send(body, generateEtag);
539
1088
  }
1089
+ /**
1090
+ * Shorthand method to finish request with "409" status code
1091
+ */
540
1092
  conflict(body, generateEtag) {
541
1093
  this.status(409);
542
1094
  return this.send(body, generateEtag);
543
1095
  }
1096
+ /**
1097
+ * Shorthand method to finish request with "401" status code
1098
+ */
544
1099
  gone(body, generateEtag) {
545
1100
  this.status(410);
546
1101
  return this.send(body, generateEtag);
547
1102
  }
1103
+ /**
1104
+ * Shorthand method to finish request with "411" status code
1105
+ */
548
1106
  lengthRequired(body, generateEtag) {
549
1107
  this.status(411);
550
1108
  return this.send(body, generateEtag);
551
1109
  }
1110
+ /**
1111
+ * Shorthand method to finish request with "412" status code
1112
+ */
552
1113
  preconditionFailed(body, generateEtag) {
553
1114
  this.status(412);
554
1115
  return this.send(body, generateEtag);
555
1116
  }
1117
+ /**
1118
+ * Shorthand method to finish request with "413" status code
1119
+ */
556
1120
  requestEntityTooLarge(body, generateEtag) {
557
1121
  this.status(413);
558
1122
  return this.send(body, generateEtag);
559
1123
  }
1124
+ /**
1125
+ * Shorthand method to finish request with "414" status code
1126
+ */
560
1127
  requestUriTooLong(body, generateEtag) {
561
1128
  this.status(414);
562
1129
  return this.send(body, generateEtag);
563
1130
  }
1131
+ /**
1132
+ * Shorthand method to finish request with "415" status code
1133
+ */
564
1134
  unsupportedMediaType(body, generateEtag) {
565
1135
  this.status(415);
566
1136
  return this.send(body, generateEtag);
567
1137
  }
1138
+ /**
1139
+ * Shorthand method to finish request with "416" status code
1140
+ */
568
1141
  requestedRangeNotSatisfiable(body, generateEtag) {
569
1142
  this.status(416);
570
1143
  return this.send(body, generateEtag);
571
1144
  }
1145
+ /**
1146
+ * Shorthand method to finish request with "417" status code
1147
+ */
572
1148
  expectationFailed(body, generateEtag) {
573
1149
  this.status(417);
574
1150
  return this.send(body, generateEtag);
575
1151
  }
1152
+ /**
1153
+ * Shorthand method to finish request with "422" status code
1154
+ */
576
1155
  unprocessableEntity(body, generateEtag) {
577
1156
  this.status(422);
578
1157
  return this.send(body, generateEtag);
579
1158
  }
1159
+ /**
1160
+ * Shorthand method to finish request with "429" status code
1161
+ */
580
1162
  tooManyRequests(body, generateEtag) {
581
1163
  this.status(429);
582
1164
  return this.send(body, generateEtag);
583
1165
  }
1166
+ /**
1167
+ * Shorthand method to finish request with "500" status code
1168
+ */
584
1169
  internalServerError(body, generateEtag) {
585
1170
  this.status(500);
586
1171
  return this.send(body, generateEtag);
587
1172
  }
1173
+ /**
1174
+ * Shorthand method to finish request with "501" status code
1175
+ */
588
1176
  notImplemented(body, generateEtag) {
589
1177
  this.status(501);
590
1178
  return this.send(body, generateEtag);
591
1179
  }
1180
+ /**
1181
+ * Shorthand method to finish request with "502" status code
1182
+ */
592
1183
  badGateway(body, generateEtag) {
593
1184
  this.status(502);
594
1185
  return this.send(body, generateEtag);
595
1186
  }
1187
+ /**
1188
+ * Shorthand method to finish request with "503" status code
1189
+ */
596
1190
  serviceUnavailable(body, generateEtag) {
597
1191
  this.status(503);
598
1192
  return this.send(body, generateEtag);
599
1193
  }
1194
+ /**
1195
+ * Shorthand method to finish request with "504" status code
1196
+ */
600
1197
  gatewayTimeout(body, generateEtag) {
601
1198
  this.status(504);
602
1199
  return this.send(body, generateEtag);
603
1200
  }
1201
+ /**
1202
+ * Shorthand method to finish request with "505" status code
1203
+ */
604
1204
  httpVersionNotSupported(body, generateEtag) {
605
1205
  this.status(505);
606
1206
  return this.send(body, generateEtag);