@adonisjs/http-server 6.8.2-3 → 6.8.2-5

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