@adonisjs/http-server 7.0.0-1 → 7.0.0-2

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 (62) hide show
  1. package/build/chunk-Z63E3STR.js +4436 -0
  2. package/build/chunk-Z63E3STR.js.map +1 -0
  3. package/build/factories/main.js +332 -14
  4. package/build/factories/main.js.map +1 -0
  5. package/build/index.js +309 -22
  6. package/build/index.js.map +1 -0
  7. package/build/src/router/lookup_store/main.d.ts +1 -3
  8. package/build/src/router/lookup_store/route_finder.d.ts +5 -1
  9. package/build/src/router/main.d.ts +2 -2
  10. package/build/src/router/resource.d.ts +19 -7
  11. package/build/src/types/main.js +1 -15
  12. package/build/src/types/main.js.map +1 -0
  13. package/package.json +52 -51
  14. package/build/factories/http_context.js +0 -51
  15. package/build/factories/http_server.js +0 -26
  16. package/build/factories/qs_parser_factory.js +0 -44
  17. package/build/factories/request.js +0 -73
  18. package/build/factories/response.js +0 -77
  19. package/build/factories/router.js +0 -45
  20. package/build/factories/server_factory.js +0 -65
  21. package/build/src/cookies/client.js +0 -84
  22. package/build/src/cookies/drivers/encrypted.js +0 -36
  23. package/build/src/cookies/drivers/plain.js +0 -33
  24. package/build/src/cookies/drivers/signed.js +0 -36
  25. package/build/src/cookies/parser.js +0 -167
  26. package/build/src/cookies/serializer.js +0 -79
  27. package/build/src/debug.js +0 -10
  28. package/build/src/define_config.js +0 -68
  29. package/build/src/define_middleware.js +0 -35
  30. package/build/src/exception_handler.js +0 -306
  31. package/build/src/exceptions.js +0 -38
  32. package/build/src/helpers.js +0 -105
  33. package/build/src/http_context/local_storage.js +0 -39
  34. package/build/src/http_context/main.js +0 -105
  35. package/build/src/qs.js +0 -25
  36. package/build/src/redirect.js +0 -140
  37. package/build/src/request.js +0 -865
  38. package/build/src/response.js +0 -1208
  39. package/build/src/router/brisk.js +0 -85
  40. package/build/src/router/executor.js +0 -30
  41. package/build/src/router/factories/use_return_value.js +0 -22
  42. package/build/src/router/group.js +0 -207
  43. package/build/src/router/lookup_store/main.js +0 -86
  44. package/build/src/router/lookup_store/route_finder.js +0 -49
  45. package/build/src/router/lookup_store/url_builder.js +0 -209
  46. package/build/src/router/main.js +0 -316
  47. package/build/src/router/matchers.js +0 -36
  48. package/build/src/router/parser.js +0 -17
  49. package/build/src/router/resource.js +0 -216
  50. package/build/src/router/route.js +0 -293
  51. package/build/src/router/store.js +0 -195
  52. package/build/src/server/factories/final_handler.js +0 -30
  53. package/build/src/server/factories/middleware_handler.js +0 -16
  54. package/build/src/server/factories/write_response.js +0 -24
  55. package/build/src/server/main.js +0 -292
  56. package/build/src/types/base.js +0 -9
  57. package/build/src/types/middleware.js +0 -9
  58. package/build/src/types/qs.js +0 -9
  59. package/build/src/types/request.js +0 -9
  60. package/build/src/types/response.js +0 -9
  61. package/build/src/types/route.js +0 -9
  62. package/build/src/types/server.js +0 -9
@@ -1,1208 +0,0 @@
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
- */
9
- import etag from 'etag';
10
- import vary from 'vary';
11
- import fresh from 'fresh';
12
- import mime from 'mime-types';
13
- import destroy from 'destroy';
14
- import { extname } from 'node:path';
15
- import onFinished from 'on-finished';
16
- import json from '@poppinss/utils/json';
17
- import Macroable from '@poppinss/macroable';
18
- import { createReadStream } from 'node:fs';
19
- import { stat } from 'node:fs/promises';
20
- import { RuntimeException } from '@poppinss/utils';
21
- import contentDisposition from 'content-disposition';
22
- import { Redirect } from './redirect.js';
23
- import { CookieSerializer } from './cookies/serializer.js';
24
- import { E_HTTP_REQUEST_ABORTED } from './exceptions.js';
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
- */
30
- export class Response extends Macroable {
31
- request;
32
- response;
33
- /**
34
- * Query string parser
35
- */
36
- #qs;
37
- /**
38
- * Outgoing headers
39
- */
40
- #headers = {};
41
- /**
42
- * Has explicit status been set
43
- */
44
- #hasExplicitStatus = false;
45
- /**
46
- * Cookies serializer to serialize the outgoing cookies
47
- */
48
- #cookieSerializer;
49
- /**
50
- * Router is used to make the redirect URLs from routes
51
- */
52
- #router;
53
- /**
54
- * Response config
55
- */
56
- #config;
57
- /**
58
- * Does response has body set that will written to the
59
- * response socket at the end of the request
60
- */
61
- get hasLazyBody() {
62
- return !!(this.lazyBody.content || this.lazyBody.fileToStream || this.lazyBody.stream);
63
- }
64
- /**
65
- * Find if the response has non-stream content
66
- */
67
- get hasContent() {
68
- return !!this.lazyBody.content;
69
- }
70
- /**
71
- * Returns true when response body is set using "response.stream"
72
- * method
73
- */
74
- get hasStream() {
75
- return !!this.lazyBody.stream;
76
- }
77
- /**
78
- * Returns true when response body is set using "response.download"
79
- * or "response.attachment" methods
80
- */
81
- get hasFileToStream() {
82
- return !!this.lazyBody.fileToStream;
83
- }
84
- /**
85
- * Returns the response content. Check if the response
86
- * has content using the "hasContent" method
87
- */
88
- get content() {
89
- return this.lazyBody.content;
90
- }
91
- /**
92
- * Returns reference to the stream set using "response.stream"
93
- * method
94
- */
95
- get outgoingStream() {
96
- return this.lazyBody.stream?.[0];
97
- }
98
- /**
99
- * Returns reference to the file path set using "response.stream"
100
- * method.
101
- */
102
- get fileToStream() {
103
- return this.lazyBody.fileToStream
104
- ? {
105
- path: this.lazyBody.fileToStream[0],
106
- generateEtag: this.lazyBody.fileToStream[1],
107
- }
108
- : undefined;
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
- */
115
- lazyBody = {};
116
- /**
117
- * The ctx will be set by the context itself. It creates a circular
118
- * reference
119
- */
120
- ctx;
121
- constructor(request, response, encryption, config, router, qs) {
122
- super();
123
- this.request = request;
124
- this.response = response;
125
- this.#qs = qs;
126
- this.#config = config;
127
- this.#router = router;
128
- this.#cookieSerializer = new CookieSerializer(encryption);
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
- */
135
- get finished() {
136
- return this.response.writableFinished;
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
- */
143
- get headersSent() {
144
- return this.response.headersSent;
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
- */
151
- get isPending() {
152
- return !this.headersSent && !this.finished;
153
- }
154
- /**
155
- * Normalizes header value to a string or an array of string
156
- */
157
- #castHeaderValue(value) {
158
- return Array.isArray(value) ? value.map(String) : String(value);
159
- }
160
- /**
161
- * Ends the response by flushing headers and writing body
162
- */
163
- #endResponse(body, statusCode) {
164
- this.writeHead(statusCode);
165
- // avoid ArgumentsAdaptorTrampoline from V8 (inspired by fastify)
166
- const res = this.response;
167
- res.end(body, null, null);
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
- */
179
- #getDataType(content) {
180
- if (Buffer.isBuffer(content)) {
181
- return 'buffer';
182
- }
183
- /**
184
- * Date instance
185
- */
186
- if (content instanceof Date) {
187
- return 'date';
188
- }
189
- /**
190
- * Regular expression
191
- */
192
- if (content instanceof RegExp) {
193
- return 'regexp';
194
- }
195
- const dataType = typeof content;
196
- if (dataType === 'number' ||
197
- dataType === 'boolean' ||
198
- dataType === 'string' ||
199
- dataType === 'bigint') {
200
- return dataType;
201
- }
202
- /**
203
- * Object
204
- */
205
- if (dataType === 'object') {
206
- return 'object';
207
- }
208
- throw new RuntimeException(`Cannot serialize "${dataType}" to HTTP response`);
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
- */
216
- writeBody(content, generateEtag, jsonpCallbackName) {
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
- */
222
- if (hasEmptyBody) {
223
- this.safeStatus(204);
224
- }
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
- */
230
- if (statusCode && (statusCode < 200 || statusCode === 204 || statusCode === 304)) {
231
- this.removeHeader('Content-Type');
232
- this.removeHeader('Content-Length');
233
- this.removeHeader('Transfer-Encoding');
234
- this.#endResponse();
235
- return;
236
- }
237
- /**
238
- * Body is empty and status code is not "204", "304" and neither under 200.
239
- */
240
- if (hasEmptyBody) {
241
- this.removeHeader('Content-Length');
242
- this.#endResponse();
243
- return;
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
- */
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
- */
258
- if (dataType === 'object') {
259
- content = json.safeStringify(content);
260
- }
261
- else if (dataType === 'number' ||
262
- dataType === 'boolean' ||
263
- dataType === 'bigint' ||
264
- dataType === 'regexp') {
265
- content = String(content);
266
- }
267
- else if (dataType === 'date') {
268
- content = content.toISOString();
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
- */
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
- */
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
288
- content = `/**/ typeof ${jsonpCallbackName} === 'function' && ${jsonpCallbackName}(${content});`;
289
- }
290
- /*
291
- * ----------------------------------------
292
- * FINALY GENERATE AN ETAG
293
- * ----------------------------------------
294
- *
295
- * Generate etag if instructed.
296
- */
297
- if (generateEtag) {
298
- this.setEtag(content);
299
- }
300
- /**
301
- * End response when cache is fresh
302
- */
303
- if (generateEtag && this.fresh()) {
304
- this.removeHeader('Content-Type');
305
- this.removeHeader('Content-Length');
306
- this.removeHeader('Transfer-Encoding');
307
- this.#endResponse(null, 304);
308
- return;
309
- }
310
- /*
311
- * ----------------------------------------
312
- * SET CONTENT-LENGTH HEADER
313
- * ----------------------------------------
314
- */
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
- */
333
- if (jsonpCallbackName) {
334
- this.header('X-Content-Type-Options', 'nosniff');
335
- this.safeHeader('Content-Type', 'text/javascript; charset=utf-8');
336
- }
337
- else {
338
- switch (dataType) {
339
- case 'string':
340
- const type = /^\s*</.test(content) ? 'text/html' : 'text/plain';
341
- this.safeHeader('Content-Type', `${type}; charset=utf-8`);
342
- break;
343
- case 'number':
344
- case 'boolean':
345
- case 'date':
346
- case 'bigint':
347
- case 'regexp':
348
- this.safeHeader('Content-Type', 'text/plain; charset=utf-8');
349
- break;
350
- case 'buffer':
351
- this.safeHeader('Content-Type', 'application/octet-stream; charset=utf-8');
352
- break;
353
- case 'object':
354
- this.safeHeader('Content-Type', 'application/json; charset=utf-8');
355
- break;
356
- }
357
- }
358
- this.#endResponse(content);
359
- }
360
- /**
361
- * Stream the body to the response and handles cleaning up the stream
362
- */
363
- streamBody(body, errorCallback) {
364
- return new Promise((resolve) => {
365
- let finished = false;
366
- /*
367
- * Listen for errors on the stream and properly destroy
368
- * stream
369
- */
370
- body.on('error', (error) => {
371
- /* c8 ignore next 3 */
372
- if (finished) {
373
- return;
374
- }
375
- finished = true;
376
- destroy(body);
377
- this.type('text');
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
- }
385
- }
386
- else {
387
- this.response.destroy();
388
- }
389
- resolve();
390
- });
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
- */
403
- onFinished(this.response, () => {
404
- finished = true;
405
- destroy(body);
406
- });
407
- /*
408
- * Pipe stream
409
- */
410
- this.relayHeaders();
411
- body.pipe(this.response);
412
- });
413
- }
414
- /**
415
- * Downloads a file by streaming it to the response
416
- */
417
- async streamFileForDownload(filePath, generateEtag, errorCallback) {
418
- try {
419
- const stats = await stat(filePath);
420
- if (!stats || !stats.isFile()) {
421
- throw new TypeError('response.download only accepts path to a file');
422
- }
423
- /*
424
- * Set appropriate headers
425
- */
426
- this.header('Last-Modified', stats.mtime.toUTCString());
427
- this.type(extname(filePath));
428
- /*
429
- * Set the etag when instructed.
430
- */
431
- if (generateEtag) {
432
- this.setEtag(stats, true);
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
- */
443
- if (this.request.method === 'HEAD') {
444
- this.#endResponse(null, generateEtag && this.fresh() ? 304 : 200);
445
- return;
446
- }
447
- /*
448
- * Regardless of request method, if we are using etags and
449
- * cache is fresh, then we must respond with 304
450
- */
451
- if (generateEtag && this.fresh()) {
452
- this.#endResponse(null, 304);
453
- return;
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
- */
460
- this.header('Content-length', stats.size);
461
- /*
462
- * Finally stream the file
463
- */
464
- return this.streamBody(createReadStream(filePath), errorCallback);
465
- }
466
- catch (error) {
467
- this.type('text');
468
- this.removeHeader('Etag');
469
- if (typeof errorCallback === 'function') {
470
- this.#endResponse(...errorCallback(error));
471
- }
472
- else {
473
- this.#endResponse(error.code === 'ENOENT' ? 'File not found' : 'Cannot process file', error.code === 'ENOENT' ? 404 : 500);
474
- }
475
- }
476
- }
477
- /**
478
- * Writes headers with the Node.js res object using the
479
- * response.setHeader method
480
- */
481
- relayHeaders() {
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) {
495
- this.response.writeHead(statusCode || this.response.statusCode, this.#headers);
496
- return this;
497
- }
498
- /**
499
- * Returns the existing value for a given HTTP response
500
- * header.
501
- */
502
- getHeader(key) {
503
- const value = this.#headers[key.toLowerCase()];
504
- return value === undefined ? this.response.getHeader(key) : value;
505
- }
506
- /**
507
- * Get response headers
508
- */
509
- getHeaders() {
510
- return {
511
- ...this.response.getHeaders(),
512
- ...this.#headers,
513
- };
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
- */
526
- header(key, value) {
527
- if (value === null || value === undefined) {
528
- return this;
529
- }
530
- this.#headers[key.toLowerCase()] = this.#castHeaderValue(value);
531
- return this;
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
- */
544
- append(key, value) {
545
- if (value === null || value === undefined) {
546
- return this;
547
- }
548
- key = key.toLowerCase();
549
- let existingHeader = this.getHeader(key);
550
- let casted = this.#castHeaderValue(value);
551
- /**
552
- * If there isn't any header, then setHeader right
553
- * away
554
- */
555
- if (!existingHeader) {
556
- this.#headers[key] = casted;
557
- return this;
558
- }
559
- existingHeader = this.#castHeaderValue(existingHeader);
560
- casted = Array.isArray(existingHeader)
561
- ? existingHeader.concat(casted)
562
- : [existingHeader].concat(casted);
563
- this.#headers[key] = casted;
564
- return this;
565
- }
566
- /**
567
- * Adds HTTP response header, when it doesn't exists already.
568
- */
569
- safeHeader(key, value) {
570
- if (!this.getHeader(key)) {
571
- this.header(key, value);
572
- }
573
- return this;
574
- }
575
- /**
576
- * Removes the existing response header from being sent.
577
- */
578
- removeHeader(key) {
579
- key = key.toLowerCase();
580
- this.response.removeHeader(key);
581
- if (this.#headers[key]) {
582
- delete this.#headers[key.toLowerCase()];
583
- }
584
- return this;
585
- }
586
- /**
587
- * Returns the status code for the response
588
- */
589
- getStatus() {
590
- return this.response.statusCode;
591
- }
592
- /**
593
- * Set HTTP status code
594
- */
595
- status(code) {
596
- this.#hasExplicitStatus = true;
597
- this.response.statusCode = code;
598
- return this;
599
- }
600
- /**
601
- * Set's status code only when it's not explictly
602
- * set
603
- */
604
- safeStatus(code) {
605
- if (this.#hasExplicitStatus) {
606
- return this;
607
- }
608
- this.response.statusCode = code;
609
- return this;
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
- */
623
- type(type, charset) {
624
- type = charset ? `${type}; charset=${charset}` : type;
625
- this.header('Content-Type', mime.contentType(type));
626
- return this;
627
- }
628
- /**
629
- * Set the Vary HTTP header
630
- */
631
- vary(field) {
632
- vary(this.response, field);
633
- return this;
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
- */
641
- setEtag(body, weak = false) {
642
- this.header('Etag', etag(body, { weak }));
643
- return this;
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
- */
670
- fresh() {
671
- if (this.request.method && !CACHEABLE_HTTP_METHODS.includes(this.request.method)) {
672
- return false;
673
- }
674
- const status = this.response.statusCode;
675
- if ((status >= 200 && status < 300) || status === 304) {
676
- return fresh(this.request.headers, this.#headers);
677
- }
678
- return false;
679
- }
680
- /**
681
- * Returns the response body. Returns null when response
682
- * body is a stream
683
- */
684
- getBody() {
685
- if (this.lazyBody.content) {
686
- return this.lazyBody.content[0];
687
- }
688
- return null;
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
- */
697
- send(body, generateEtag = this.#config.etag) {
698
- this.lazyBody.content = [body, generateEtag];
699
- }
700
- /**
701
- * Alias of [[send]]
702
- */
703
- json(body, generateEtag = this.#config.etag) {
704
- return this.send(body, generateEtag);
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
- */
718
- jsonp(body, callbackName = this.#config.jsonpCallbackName, generateEtag = this.#config.etag) {
719
- this.lazyBody.content = [body, generateEtag, callbackName];
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
- */
745
- stream(body, errorCallback) {
746
- if (typeof body.pipe !== 'function' || !body.readable || typeof body.read !== 'function') {
747
- throw new TypeError('response.stream accepts a readable stream only');
748
- }
749
- this.lazyBody.stream = [body, errorCallback];
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
- */
776
- download(filePath, generateEtag = this.#config.etag, errorCallback) {
777
- this.lazyBody.fileToStream = [filePath, generateEtag, errorCallback];
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
- */
785
- attachment(filePath, name, disposition, generateEtag, errorCallback) {
786
- name = name || filePath;
787
- this.header('Content-Disposition', contentDisposition(name, { type: disposition }));
788
- return this.download(filePath, generateEtag, errorCallback);
789
- }
790
- /**
791
- * Set the location header.
792
- *
793
- * @example
794
- * ```js
795
- * response.location('/login')
796
- * ```
797
- */
798
- location(url) {
799
- this.header('Location', url);
800
- return this;
801
- }
802
- redirect(path, forwardQueryString = false, statusCode = 302) {
803
- const handler = new Redirect(this.request, this, this.#router, this.#qs);
804
- if (forwardQueryString) {
805
- handler.withQs();
806
- }
807
- if (path === 'back') {
808
- return handler.status(statusCode).back();
809
- }
810
- if (path) {
811
- return handler.status(statusCode).toPath(path);
812
- }
813
- return handler;
814
- }
815
- /**
816
- * Abort the request with custom body and a status code. 400 is
817
- * used when status is not defined
818
- */
819
- abort(body, status) {
820
- throw E_HTTP_REQUEST_ABORTED.invoke(body, status || 400);
821
- }
822
- /**
823
- * Abort the request with custom body and a status code when
824
- * passed condition returns `true`
825
- */
826
- abortIf(condition, body, status) {
827
- if (condition) {
828
- this.abort(body, status);
829
- }
830
- }
831
- /**
832
- * Abort the request with custom body and a status code when
833
- * passed condition returns `false`
834
- */
835
- abortUnless(condition, body, status) {
836
- if (!condition) {
837
- this.abort(body, status);
838
- }
839
- }
840
- /**
841
- * Set signed cookie as the response header. The inline options overrides
842
- * all options from the config.
843
- */
844
- cookie(key, value, options) {
845
- options = Object.assign({}, this.#config.cookie, options);
846
- const serialized = this.#cookieSerializer.sign(key, value, options);
847
- if (!serialized) {
848
- return this;
849
- }
850
- this.append('set-cookie', serialized);
851
- return this;
852
- }
853
- /**
854
- * Set encrypted cookie as the response header. The inline options overrides
855
- * all options from the config.
856
- */
857
- encryptedCookie(key, value, options) {
858
- options = Object.assign({}, this.#config.cookie, options);
859
- const serialized = this.#cookieSerializer.encrypt(key, value, options);
860
- if (!serialized) {
861
- return this;
862
- }
863
- this.append('set-cookie', serialized);
864
- return this;
865
- }
866
- /**
867
- * Set unsigned cookie as the response header. The inline options overrides
868
- * all options from the config.
869
- */
870
- plainCookie(key, value, options) {
871
- options = Object.assign({}, this.#config.cookie, options);
872
- const serialized = this.#cookieSerializer.encode(key, value, options);
873
- if (!serialized) {
874
- return this;
875
- }
876
- this.append('set-cookie', serialized);
877
- return this;
878
- }
879
- /**
880
- * Clear existing cookie.
881
- */
882
- clearCookie(key, options) {
883
- options = Object.assign({}, this.#config.cookie, options);
884
- options.expires = new Date(1);
885
- options.maxAge = -1;
886
- const serialized = this.#cookieSerializer.encode(key, '', { ...options, encode: false });
887
- this.append('set-cookie', serialized);
888
- return this;
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
- */
896
- finish() {
897
- if (!this.isPending) {
898
- return;
899
- }
900
- if (this.content) {
901
- this.writeBody(...this.content);
902
- return;
903
- }
904
- if (this.lazyBody.stream) {
905
- this.streamBody(...this.lazyBody.stream);
906
- return;
907
- }
908
- if (this.lazyBody.fileToStream) {
909
- this.streamFileForDownload(...this.lazyBody.fileToStream);
910
- return;
911
- }
912
- this.#endResponse();
913
- }
914
- /**
915
- * Shorthand method to finish request with "100" status code
916
- */
917
- continue() {
918
- this.status(100);
919
- return this.send(null, false);
920
- }
921
- /**
922
- * Shorthand method to finish request with "101" status code
923
- */
924
- switchingProtocols() {
925
- this.status(101);
926
- return this.send(null, false);
927
- }
928
- /**
929
- * Shorthand method to finish request with "200" status code
930
- */
931
- ok(body, generateEtag) {
932
- this.status(200);
933
- return this.send(body, generateEtag);
934
- }
935
- /**
936
- * Shorthand method to finish request with "201" status code
937
- */
938
- created(body, generateEtag) {
939
- this.status(201);
940
- return this.send(body, generateEtag);
941
- }
942
- /**
943
- * Shorthand method to finish request with "202" status code
944
- */
945
- accepted(body, generateEtag) {
946
- this.status(202);
947
- return this.send(body, generateEtag);
948
- }
949
- /**
950
- * Shorthand method to finish request with "203" status code
951
- */
952
- nonAuthoritativeInformation(body, generateEtag) {
953
- this.status(203);
954
- return this.send(body, generateEtag);
955
- }
956
- /**
957
- * Shorthand method to finish request with "204" status code
958
- */
959
- noContent() {
960
- this.status(204);
961
- return this.send(null, false);
962
- }
963
- /**
964
- * Shorthand method to finish request with "205" status code
965
- */
966
- resetContent() {
967
- this.status(205);
968
- return this.send(null, false);
969
- }
970
- /**
971
- * Shorthand method to finish request with "206" status code
972
- */
973
- partialContent(body, generateEtag) {
974
- this.status(206);
975
- return this.send(body, generateEtag);
976
- }
977
- /**
978
- * Shorthand method to finish request with "300" status code
979
- */
980
- multipleChoices(body, generateEtag) {
981
- this.status(300);
982
- return this.send(body, generateEtag);
983
- }
984
- /**
985
- * Shorthand method to finish request with "301" status code
986
- */
987
- movedPermanently(body, generateEtag) {
988
- this.status(301);
989
- return this.send(body, generateEtag);
990
- }
991
- /**
992
- * Shorthand method to finish request with "302" status code
993
- */
994
- movedTemporarily(body, generateEtag) {
995
- this.status(302);
996
- return this.send(body, generateEtag);
997
- }
998
- /**
999
- * Shorthand method to finish request with "303" status code
1000
- */
1001
- seeOther(body, generateEtag) {
1002
- this.status(303);
1003
- return this.send(body, generateEtag);
1004
- }
1005
- /**
1006
- * Shorthand method to finish request with "304" status code
1007
- */
1008
- notModified(body, generateEtag) {
1009
- this.status(304);
1010
- return this.send(body, generateEtag);
1011
- }
1012
- /**
1013
- * Shorthand method to finish request with "305" status code
1014
- */
1015
- useProxy(body, generateEtag) {
1016
- this.status(305);
1017
- return this.send(body, generateEtag);
1018
- }
1019
- /**
1020
- * Shorthand method to finish request with "307" status code
1021
- */
1022
- temporaryRedirect(body, generateEtag) {
1023
- this.status(307);
1024
- return this.send(body, generateEtag);
1025
- }
1026
- /**
1027
- * Shorthand method to finish request with "400" status code
1028
- */
1029
- badRequest(body, generateEtag) {
1030
- this.status(400);
1031
- return this.send(body, generateEtag);
1032
- }
1033
- /**
1034
- * Shorthand method to finish request with "401" status code
1035
- */
1036
- unauthorized(body, generateEtag) {
1037
- this.status(401);
1038
- return this.send(body, generateEtag);
1039
- }
1040
- /**
1041
- * Shorthand method to finish request with "402" status code
1042
- */
1043
- paymentRequired(body, generateEtag) {
1044
- this.status(402);
1045
- return this.send(body, generateEtag);
1046
- }
1047
- /**
1048
- * Shorthand method to finish request with "403" status code
1049
- */
1050
- forbidden(body, generateEtag) {
1051
- this.status(403);
1052
- return this.send(body, generateEtag);
1053
- }
1054
- /**
1055
- * Shorthand method to finish request with "404" status code
1056
- */
1057
- notFound(body, generateEtag) {
1058
- this.status(404);
1059
- return this.send(body, generateEtag);
1060
- }
1061
- /**
1062
- * Shorthand method to finish request with "405" status code
1063
- */
1064
- methodNotAllowed(body, generateEtag) {
1065
- this.status(405);
1066
- return this.send(body, generateEtag);
1067
- }
1068
- /**
1069
- * Shorthand method to finish request with "406" status code
1070
- */
1071
- notAcceptable(body, generateEtag) {
1072
- this.status(406);
1073
- return this.send(body, generateEtag);
1074
- }
1075
- /**
1076
- * Shorthand method to finish request with "407" status code
1077
- */
1078
- proxyAuthenticationRequired(body, generateEtag) {
1079
- this.status(407);
1080
- return this.send(body, generateEtag);
1081
- }
1082
- /**
1083
- * Shorthand method to finish request with "408" status code
1084
- */
1085
- requestTimeout(body, generateEtag) {
1086
- this.status(408);
1087
- return this.send(body, generateEtag);
1088
- }
1089
- /**
1090
- * Shorthand method to finish request with "409" status code
1091
- */
1092
- conflict(body, generateEtag) {
1093
- this.status(409);
1094
- return this.send(body, generateEtag);
1095
- }
1096
- /**
1097
- * Shorthand method to finish request with "401" status code
1098
- */
1099
- gone(body, generateEtag) {
1100
- this.status(410);
1101
- return this.send(body, generateEtag);
1102
- }
1103
- /**
1104
- * Shorthand method to finish request with "411" status code
1105
- */
1106
- lengthRequired(body, generateEtag) {
1107
- this.status(411);
1108
- return this.send(body, generateEtag);
1109
- }
1110
- /**
1111
- * Shorthand method to finish request with "412" status code
1112
- */
1113
- preconditionFailed(body, generateEtag) {
1114
- this.status(412);
1115
- return this.send(body, generateEtag);
1116
- }
1117
- /**
1118
- * Shorthand method to finish request with "413" status code
1119
- */
1120
- requestEntityTooLarge(body, generateEtag) {
1121
- this.status(413);
1122
- return this.send(body, generateEtag);
1123
- }
1124
- /**
1125
- * Shorthand method to finish request with "414" status code
1126
- */
1127
- requestUriTooLong(body, generateEtag) {
1128
- this.status(414);
1129
- return this.send(body, generateEtag);
1130
- }
1131
- /**
1132
- * Shorthand method to finish request with "415" status code
1133
- */
1134
- unsupportedMediaType(body, generateEtag) {
1135
- this.status(415);
1136
- return this.send(body, generateEtag);
1137
- }
1138
- /**
1139
- * Shorthand method to finish request with "416" status code
1140
- */
1141
- requestedRangeNotSatisfiable(body, generateEtag) {
1142
- this.status(416);
1143
- return this.send(body, generateEtag);
1144
- }
1145
- /**
1146
- * Shorthand method to finish request with "417" status code
1147
- */
1148
- expectationFailed(body, generateEtag) {
1149
- this.status(417);
1150
- return this.send(body, generateEtag);
1151
- }
1152
- /**
1153
- * Shorthand method to finish request with "422" status code
1154
- */
1155
- unprocessableEntity(body, generateEtag) {
1156
- this.status(422);
1157
- return this.send(body, generateEtag);
1158
- }
1159
- /**
1160
- * Shorthand method to finish request with "429" status code
1161
- */
1162
- tooManyRequests(body, generateEtag) {
1163
- this.status(429);
1164
- return this.send(body, generateEtag);
1165
- }
1166
- /**
1167
- * Shorthand method to finish request with "500" status code
1168
- */
1169
- internalServerError(body, generateEtag) {
1170
- this.status(500);
1171
- return this.send(body, generateEtag);
1172
- }
1173
- /**
1174
- * Shorthand method to finish request with "501" status code
1175
- */
1176
- notImplemented(body, generateEtag) {
1177
- this.status(501);
1178
- return this.send(body, generateEtag);
1179
- }
1180
- /**
1181
- * Shorthand method to finish request with "502" status code
1182
- */
1183
- badGateway(body, generateEtag) {
1184
- this.status(502);
1185
- return this.send(body, generateEtag);
1186
- }
1187
- /**
1188
- * Shorthand method to finish request with "503" status code
1189
- */
1190
- serviceUnavailable(body, generateEtag) {
1191
- this.status(503);
1192
- return this.send(body, generateEtag);
1193
- }
1194
- /**
1195
- * Shorthand method to finish request with "504" status code
1196
- */
1197
- gatewayTimeout(body, generateEtag) {
1198
- this.status(504);
1199
- return this.send(body, generateEtag);
1200
- }
1201
- /**
1202
- * Shorthand method to finish request with "505" status code
1203
- */
1204
- httpVersionNotSupported(body, generateEtag) {
1205
- this.status(505);
1206
- return this.send(body, generateEtag);
1207
- }
1208
- }