@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
package/src/request.ts ADDED
@@ -0,0 +1,982 @@
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 fresh from 'fresh'
11
+ import typeIs from 'type-is'
12
+ import accepts from 'accepts'
13
+ import { isIP } from 'node:net'
14
+ import is from '@sindresorhus/is'
15
+ import proxyaddr from 'proxy-addr'
16
+ import { safeEqual } from '@poppinss/utils'
17
+ import Macroable from '@poppinss/macroable'
18
+ import lodash from '@poppinss/utils/lodash'
19
+ import { createId } from '@paralleldrive/cuid2'
20
+ import { parse, UrlWithStringQuery } from 'node:url'
21
+ import type { Encryption } from '@adonisjs/encryption'
22
+ import { ServerResponse, IncomingMessage, IncomingHttpHeaders } from 'node:http'
23
+
24
+ import type { Qs } from './qs.js'
25
+ import { trustProxy } from './helpers.js'
26
+ import { CookieParser } from './cookies/parser.js'
27
+ import { RequestConfig } from './types/request.js'
28
+ import type { HttpContext } from './http_context/main.js'
29
+
30
+ /**
31
+ * HTTP Request class exposes the interface to consistently read values
32
+ * related to a given HTTP request. The class is wrapper over
33
+ * [IncomingMessage](https://nodejs.org/api/http.html#http_class_http_incomingmessage)
34
+ * and has extended API.
35
+ *
36
+ * You can access the original [IncomingMessage](https://nodejs.org/api/http.html#http_class_http_incomingmessage)
37
+ * using `request.request` property.
38
+ */
39
+ export class Request extends Macroable {
40
+ /**
41
+ * Query string parser
42
+ */
43
+ #qsParser: Qs
44
+
45
+ /**
46
+ * Encryption module to verify signed URLs and unsign/decrypt
47
+ * cookies
48
+ */
49
+ #encryption: Encryption
50
+
51
+ /**
52
+ * Request config
53
+ */
54
+ #config: RequestConfig
55
+
56
+ /**
57
+ * Request body set using `setBody` method
58
+ */
59
+ #requestBody: Record<string, any> = {}
60
+
61
+ /**
62
+ * A merged copy of `request body` and `querystring`
63
+ */
64
+ #requestData: Record<string, any> = {}
65
+
66
+ /**
67
+ * Original merged copy of `request body` and `querystring`.
68
+ * Further mutation to this object are not allowed
69
+ */
70
+ #originalRequestData: Record<string, any> = {}
71
+
72
+ /**
73
+ * Parsed query string
74
+ */
75
+ #requestQs: Record<string, any> = {}
76
+
77
+ /**
78
+ * Raw request body as text
79
+ */
80
+ #rawRequestBody?: string
81
+
82
+ /**
83
+ * Cached copy of `accepts` fn to do content
84
+ * negotiation.
85
+ */
86
+ #lazyAccepts?: any
87
+
88
+ /**
89
+ * Copy of lazily parsed signed and plain cookies.
90
+ */
91
+ #cookieParser?: CookieParser
92
+
93
+ /**
94
+ * Parses copy of the URL with query string as a string and not
95
+ * object. This is done to build URL's with query string without
96
+ * stringifying the object
97
+ */
98
+ parsedUrl: UrlWithStringQuery
99
+
100
+ /**
101
+ * The ctx will be set by the context itself. It creates a circular
102
+ * reference
103
+ */
104
+ ctx?: HttpContext
105
+
106
+ constructor(
107
+ public request: IncomingMessage,
108
+ public response: ServerResponse,
109
+ encryption: Encryption,
110
+ config: RequestConfig,
111
+ qsParser: Qs
112
+ ) {
113
+ super()
114
+
115
+ this.#qsParser = qsParser
116
+ this.#config = config
117
+ this.#encryption = encryption
118
+ this.parsedUrl = parse(this.request.url!, false)
119
+ this.#parseQueryString()
120
+ }
121
+
122
+ /**
123
+ * Parses the query string
124
+ */
125
+ #parseQueryString() {
126
+ if (this.parsedUrl.query) {
127
+ this.updateQs(this.#qsParser.parse(this.parsedUrl.query))
128
+ this.#originalRequestData = { ...this.#requestData }
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Initiates the cookie parser lazily
134
+ */
135
+ #initiateCookieParser() {
136
+ if (!this.#cookieParser) {
137
+ this.#cookieParser = new CookieParser(this.header('cookie')!, this.#encryption)
138
+ }
139
+ }
140
+
141
+ /**
142
+ * Lazily initiates the `accepts` module to make sure to parse
143
+ * the request headers only when one of the content-negotiation
144
+ * methods are used.
145
+ */
146
+ #initiateAccepts() {
147
+ this.#lazyAccepts = this.#lazyAccepts || accepts(this.request)
148
+ }
149
+
150
+ /**
151
+ * Returns the request id from the `x-request-id` header. The
152
+ * header is untouched, if it already exists.
153
+ */
154
+ id(): string | undefined {
155
+ let requestId = this.header('x-request-id')
156
+ if (!requestId && this.#config.generateRequestId) {
157
+ requestId = createId()
158
+ this.request.headers['x-request-id'] = requestId
159
+ }
160
+
161
+ return requestId
162
+ }
163
+
164
+ /**
165
+ * Set initial request body. A copy of the input will be maintained as the original
166
+ * request body. Since the request body and query string is subject to mutations, we
167
+ * keep one original reference to flash old data (whenever required).
168
+ *
169
+ * This method is supposed to be invoked by the body parser and must be called only
170
+ * once. For further mutations make use of `updateBody` method.
171
+ */
172
+ setInitialBody(body: Record<string, any>) {
173
+ if (this.#originalRequestData && Object.isFrozen(this.#originalRequestData)) {
174
+ throw new Error('Cannot re-set initial body. Use "request.updateBody" instead')
175
+ }
176
+
177
+ this.updateBody(body)
178
+
179
+ /*
180
+ * Freeze the original object
181
+ */
182
+ this.#originalRequestData = Object.freeze(lodash.cloneDeep(this.#requestData))
183
+ }
184
+
185
+ /**
186
+ * Update the request body with new data object. The `all` property
187
+ * will be re-computed by merging the query string and request
188
+ * body.
189
+ */
190
+ updateBody(body: Record<string, any>) {
191
+ this.#requestBody = body
192
+ this.#requestData = { ...this.#requestBody, ...this.#requestQs }
193
+ }
194
+
195
+ /**
196
+ * Update the request raw body. Bodyparser sets this when unable to parse
197
+ * the request body or when request is multipart/form-data.
198
+ */
199
+ updateRawBody(rawBody: string) {
200
+ this.#rawRequestBody = rawBody
201
+ }
202
+
203
+ /**
204
+ * Update the query string with the new data object. The `all` property
205
+ * will be re-computed by merging the query and the request body.
206
+ */
207
+ updateQs(data: Record<string, any>) {
208
+ this.#requestQs = data
209
+ this.#requestData = { ...this.#requestBody, ...this.#requestQs }
210
+ }
211
+
212
+ /**
213
+ * Returns route params
214
+ */
215
+ params(): Record<string, any> {
216
+ return this.ctx?.params || {}
217
+ }
218
+
219
+ /**
220
+ * Returns the query string object by reference
221
+ */
222
+ qs(): Record<string, any> {
223
+ return this.#requestQs
224
+ }
225
+
226
+ /**
227
+ * Returns reference to the request body
228
+ */
229
+ body(): Record<string, any> {
230
+ return this.#requestBody
231
+ }
232
+
233
+ /**
234
+ * Returns reference to the merged copy of request body
235
+ * and query string
236
+ */
237
+ all(): Record<string, any> {
238
+ return this.#requestData
239
+ }
240
+
241
+ /**
242
+ * Returns reference to the merged copy of original request
243
+ * query string and body
244
+ */
245
+ original(): Record<string, any> {
246
+ return this.#originalRequestData
247
+ }
248
+
249
+ /**
250
+ * Returns the request raw body (if exists), or returns `null`.
251
+ *
252
+ * Ideally you must be dealing with the parsed body accessed using [[input]], [[all]] or
253
+ * [[post]] methods. The `raw` body is always a string.
254
+ */
255
+ raw(): string | null {
256
+ return this.#rawRequestBody || null
257
+ }
258
+
259
+ /**
260
+ * Returns value for a given key from the request body or query string.
261
+ * The `defaultValue` is used when original value is `undefined`.
262
+ *
263
+ * @example
264
+ * ```js
265
+ * request.input('username')
266
+ *
267
+ * // with default value
268
+ * request.input('username', 'virk')
269
+ * ```
270
+ */
271
+ input(key: string, defaultValue?: any): any {
272
+ return lodash.get(this.#requestData, key, defaultValue)
273
+ }
274
+
275
+ /**
276
+ * Returns value for a given key from route params
277
+ *
278
+ * @example
279
+ * ```js
280
+ * request.param('id')
281
+ *
282
+ * // with default value
283
+ * request.param('id', 1)
284
+ * ```
285
+ */
286
+ param(key: string, defaultValue?: any): any {
287
+ return lodash.get(this.params(), key, defaultValue)
288
+ }
289
+
290
+ /**
291
+ * Get everything from the request body except the given keys.
292
+ *
293
+ * @example
294
+ * ```js
295
+ * request.except(['_csrf'])
296
+ * ```
297
+ */
298
+ except(keys: string[]): Record<string, any> {
299
+ return lodash.omit(this.#requestData, keys)
300
+ }
301
+
302
+ /**
303
+ * Get value for specified keys.
304
+ *
305
+ * @example
306
+ * ```js
307
+ * request.only(['username', 'age'])
308
+ * ```
309
+ */
310
+ only<T extends string>(keys: T[]): { [K in T]: any } {
311
+ return lodash.pick(this.#requestData, keys) as { [K in T]: any }
312
+ }
313
+
314
+ /**
315
+ * Returns the HTTP request method. This is the original
316
+ * request method. For spoofed request method, make
317
+ * use of [[method]].
318
+ *
319
+ * @example
320
+ * ```js
321
+ * request.intended()
322
+ * ```
323
+ */
324
+ intended(): string {
325
+ return this.request.method!
326
+ }
327
+
328
+ /**
329
+ * Returns the request HTTP method by taking method spoofing into account.
330
+ *
331
+ * Method spoofing works when all of the following are true.
332
+ *
333
+ * 1. `app.http.allowMethodSpoofing` config value is true.
334
+ * 2. request query string has `_method`.
335
+ * 3. The [[intended]] request method is `POST`.
336
+ *
337
+ * @example
338
+ * ```js
339
+ * request.method()
340
+ * ```
341
+ */
342
+ method(): string {
343
+ if (this.#config.allowMethodSpoofing && this.intended() === 'POST') {
344
+ return this.input('_method', this.intended()).toUpperCase()
345
+ }
346
+ return this.intended()
347
+ }
348
+
349
+ /**
350
+ * Returns a copy of headers as an object
351
+ */
352
+ headers(): IncomingHttpHeaders {
353
+ return this.request.headers
354
+ }
355
+
356
+ /**
357
+ * Returns value for a given header key. The default value is
358
+ * used when original value is `undefined`.
359
+ */
360
+ header(key: string, defaultValue?: any): string | undefined {
361
+ key = key.toLowerCase()
362
+ const headers = this.headers()
363
+
364
+ switch (key) {
365
+ case 'referer':
366
+ case 'referrer':
367
+ return headers.referrer || headers.referer || defaultValue
368
+ default:
369
+ return headers[key] || defaultValue
370
+ }
371
+ }
372
+
373
+ /**
374
+ * Returns the ip address of the user. This method is optimize to fetch
375
+ * ip address even when running your AdonisJs app behind a proxy.
376
+ *
377
+ * You can also define your own custom function to compute the ip address by
378
+ * defining `app.http.getIp` as a function inside the config file.
379
+ *
380
+ * ```js
381
+ * {
382
+ * http: {
383
+ * getIp (request) {
384
+ * // I am using nginx as a proxy server and want to trust 'x-real-ip'
385
+ * return request.header('x-real-ip')
386
+ * }
387
+ * }
388
+ * }
389
+ * ```
390
+ *
391
+ * You can control the behavior of trusting the proxy values by defining it
392
+ * inside the `config/app.js` file.
393
+ *
394
+ * ```js
395
+ * {
396
+ * http: {
397
+ * trustProxy: '127.0.0.1'
398
+ * }
399
+ * }
400
+ * ```
401
+ *
402
+ * The value of trustProxy is passed directly to [proxy-addr](https://www.npmjs.com/package/proxy-addr)
403
+ */
404
+ ip(): string {
405
+ const ipFn = this.#config.getIp
406
+ if (typeof ipFn === 'function') {
407
+ return ipFn(this)
408
+ }
409
+
410
+ return proxyaddr(this.request, this.#config.trustProxy)
411
+ }
412
+
413
+ /**
414
+ * Returns an array of ip addresses from most to least trusted one.
415
+ * This method is optimize to fetch ip address even when running
416
+ * your AdonisJs app behind a proxy.
417
+ *
418
+ * You can control the behavior of trusting the proxy values by defining it
419
+ * inside the `config/app.js` file.
420
+ *
421
+ * ```js
422
+ * {
423
+ * http: {
424
+ * trustProxy: '127.0.0.1'
425
+ * }
426
+ * }
427
+ * ```
428
+ *
429
+ * The value of trustProxy is passed directly to [proxy-addr](https://www.npmjs.com/package/proxy-addr)
430
+ */
431
+ ips(): string[] {
432
+ return proxyaddr.all(this.request, this.#config.trustProxy)
433
+ }
434
+
435
+ /**
436
+ * Returns the request protocol by checking for the URL protocol or
437
+ * `X-Forwarded-Proto` header.
438
+ *
439
+ * If the `trust` is evaluated to `false`, then URL protocol is returned,
440
+ * otherwise `X-Forwarded-Proto` header is used (if exists).
441
+ *
442
+ * You can control the behavior of trusting the proxy values by defining it
443
+ * inside the `config/app.js` file.
444
+ *
445
+ * ```js
446
+ * {
447
+ * http: {
448
+ * trustProxy: '127.0.0.1'
449
+ * }
450
+ * }
451
+ * ```
452
+ *
453
+ * The value of trustProxy is passed directly to [proxy-addr](https://www.npmjs.com/package/proxy-addr)
454
+ */
455
+ protocol(): string {
456
+ if ('encrypted' in this.request.socket) {
457
+ return 'https'
458
+ }
459
+
460
+ if (!trustProxy(this.request.socket.remoteAddress!, this.#config.trustProxy)) {
461
+ return this.parsedUrl.protocol || 'http'
462
+ }
463
+
464
+ const forwardedProtocol = this.header('X-Forwarded-Proto')
465
+ return forwardedProtocol ? forwardedProtocol.split(/\s*,\s*/)[0] : 'http'
466
+ }
467
+
468
+ /**
469
+ * Returns a boolean telling if request is served over `https`
470
+ * or not. Check [[protocol]] method to know how protocol is
471
+ * fetched.
472
+ */
473
+ secure(): boolean {
474
+ return this.protocol() === 'https'
475
+ }
476
+
477
+ /**
478
+ * Returns the request host. If proxy headers are trusted, then
479
+ * `X-Forwarded-Host` is given priority over the `Host` header.
480
+ *
481
+ * You can control the behavior of trusting the proxy values by defining it
482
+ * inside the `config/app.js` file.
483
+ *
484
+ * ```js
485
+ * {
486
+ * http: {
487
+ * trustProxy: '127.0.0.1'
488
+ * }
489
+ * }
490
+ * ```
491
+ *
492
+ * The value of trustProxy is passed directly to [proxy-addr](https://www.npmjs.com/package/proxy-addr)
493
+ */
494
+ host(): string | null {
495
+ let host = this.header('host')
496
+
497
+ /*
498
+ * Use X-Fowarded-Host when we trust the proxy header and it
499
+ * exists
500
+ */
501
+ if (trustProxy(this.request.socket.remoteAddress!, this.#config.trustProxy)) {
502
+ host = this.header('X-Forwarded-Host') || host
503
+ }
504
+
505
+ if (!host) {
506
+ return null
507
+ }
508
+
509
+ return host
510
+ }
511
+
512
+ /**
513
+ * Returns the request hostname. If proxy headers are trusted, then
514
+ * `X-Forwarded-Host` is given priority over the `Host` header.
515
+ *
516
+ * You can control the behavior of trusting the proxy values by defining it
517
+ * inside the `config/app.js` file.
518
+ *
519
+ * ```js
520
+ * {
521
+ * http: {
522
+ * trustProxy: '127.0.0.1'
523
+ * }
524
+ * }
525
+ * ```
526
+ *
527
+ * The value of trustProxy is passed directly to [proxy-addr](https://www.npmjs.com/package/proxy-addr)
528
+ */
529
+ hostname(): string | null {
530
+ const host = this.host()
531
+
532
+ if (!host) {
533
+ return null
534
+ }
535
+
536
+ /*
537
+ * Support for IPv6
538
+ * https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2
539
+ * https://github.com/nodejs/node/pull/5314
540
+ */
541
+ const offset = host[0] === '[' ? host.indexOf(']') + 1 : 0
542
+ const index = host.indexOf(':', offset)
543
+ return index !== -1 ? host.substring(0, index) : host
544
+ }
545
+
546
+ /**
547
+ * Returns an array of subdomains for the given host. An empty array is
548
+ * returned if [[hostname]] is `null` or is an IP address.
549
+ *
550
+ * Also `www` is not considered as a subdomain
551
+ */
552
+ subdomains(): string[] {
553
+ const hostname = this.hostname()
554
+
555
+ /*
556
+ * Return empty array when hostname is missing or it's
557
+ * an IP address
558
+ */
559
+ if (!hostname || isIP(hostname)) {
560
+ return []
561
+ }
562
+
563
+ const offset = this.#config.subdomainOffset
564
+ const subdomains = hostname.split('.').reverse().slice(offset)
565
+
566
+ /*
567
+ * Remove www from the subdomains list
568
+ */
569
+ if (subdomains[subdomains.length - 1] === 'www') {
570
+ subdomains.splice(subdomains.length - 1, 1)
571
+ }
572
+
573
+ return subdomains
574
+ }
575
+
576
+ /**
577
+ * Returns a boolean telling, if request `X-Requested-With === 'xmlhttprequest'`
578
+ * or not.
579
+ */
580
+ ajax(): boolean {
581
+ const xRequestedWith = this.header('X-Requested-With', '')
582
+ return xRequestedWith!.toLowerCase() === 'xmlhttprequest'
583
+ }
584
+
585
+ /**
586
+ * Returns a boolean telling, if request has `X-Pjax` header
587
+ * set or not
588
+ */
589
+ pjax(): boolean {
590
+ return !!this.header('X-Pjax')
591
+ }
592
+
593
+ /**
594
+ * Returns the request relative URL.
595
+ *
596
+ * @example
597
+ * ```js
598
+ * request.url()
599
+ *
600
+ * // include query string
601
+ * request.url(true)
602
+ * ```
603
+ */
604
+ url(includeQueryString?: boolean): string {
605
+ const pathname = this.parsedUrl.pathname!
606
+ return includeQueryString && this.parsedUrl.query
607
+ ? `${pathname}?${this.parsedUrl.query}`
608
+ : pathname
609
+ }
610
+
611
+ /**
612
+ * Returns the complete HTTP url by combining
613
+ * [[protocol]]://[[hostname]]/[[url]]
614
+ *
615
+ * @example
616
+ * ```js
617
+ * request.completeUrl()
618
+ *
619
+ * // include query string
620
+ * request.completeUrl(true)
621
+ * ```
622
+ */
623
+ completeUrl(includeQueryString?: boolean): string {
624
+ const protocol = this.protocol()
625
+ const hostname = this.host()
626
+ return `${protocol}://${hostname}${this.url(includeQueryString)}`
627
+ }
628
+
629
+ /**
630
+ * Find if the current HTTP request is for the given route or the routes
631
+ */
632
+ matchesRoute(routeIdentifier: string | string[]): boolean {
633
+ /**
634
+ * The context is missing inside the HTTP server hooks.
635
+ */
636
+ if (!this.ctx || !this.ctx.route) {
637
+ return false
638
+ }
639
+
640
+ const route = this.ctx.route
641
+
642
+ /**
643
+ * Search the identifier(s) against the route "pattern", "name" and the route handler
644
+ */
645
+ return !!(Array.isArray(routeIdentifier) ? routeIdentifier : [routeIdentifier]).find(
646
+ (identifier) => {
647
+ if (route.pattern === identifier || route.name === identifier) {
648
+ return true
649
+ }
650
+
651
+ if (typeof route.handler === 'function') {
652
+ return false
653
+ }
654
+
655
+ return route.handler.reference === identifier
656
+ }
657
+ )
658
+ }
659
+
660
+ /**
661
+ * Returns the best matching content type of the request by
662
+ * matching against the given types.
663
+ *
664
+ * The content type is picked from the `content-type` header and request
665
+ * must have body.
666
+ *
667
+ * The method response highly depends upon the types array values. Described below:
668
+ *
669
+ * | Type(s) | Return value |
670
+ * |----------|---------------|
671
+ * | ['json'] | json |
672
+ * | ['application/*'] | application/json |
673
+ * | ['vnd+json'] | application/json |
674
+ *
675
+ * @example
676
+ * ```js
677
+ * const bodyType = request.is(['json', 'xml'])
678
+ *
679
+ * if (bodyType === 'json') {
680
+ * // process JSON
681
+ * }
682
+ *
683
+ * if (bodyType === 'xml') {
684
+ * // process XML
685
+ * }
686
+ * ```
687
+ */
688
+ is(types: string[]): string | null {
689
+ return typeIs(this.request, types) || null
690
+ }
691
+
692
+ /**
693
+ * Returns the best type using `Accept` header and
694
+ * by matching it against the given types.
695
+ *
696
+ * If nothing is matched, then `null` will be returned
697
+ *
698
+ * Make sure to check [accepts](https://www.npmjs.com/package/accepts) package
699
+ * docs too.
700
+ *
701
+ * @example
702
+ * ```js
703
+ * switch (request.accepts(['json', 'html'])) {
704
+ * case 'json':
705
+ * return response.json(user)
706
+ * case 'html':
707
+ * return view.render('user', { user })
708
+ * default:
709
+ * // decide yourself
710
+ * }
711
+ * ```
712
+ */
713
+ accepts<T extends string>(types: T[]): T | null {
714
+ this.#initiateAccepts()
715
+ return this.#lazyAccepts.type(types) || null
716
+ }
717
+
718
+ /**
719
+ * Return the types that the request accepts, in the order of the
720
+ * client's preference (most preferred first).
721
+ *
722
+ * Make sure to check [accepts](https://www.npmjs.com/package/accepts) package
723
+ * docs too.
724
+ */
725
+ types(): string[] {
726
+ this.#initiateAccepts()
727
+ return this.#lazyAccepts.types()
728
+ }
729
+
730
+ /**
731
+ * Returns the best language using `Accept-language` header
732
+ * and by matching it against the given languages.
733
+ *
734
+ * If nothing is matched, then `null` will be returned
735
+ *
736
+ * Make sure to check [accepts](https://www.npmjs.com/package/accepts) package
737
+ * docs too.
738
+ *
739
+ * @example
740
+ * ```js
741
+ * switch (request.language(['fr', 'de'])) {
742
+ * case 'fr':
743
+ * return view.render('about', { lang: 'fr' })
744
+ * case 'de':
745
+ * return view.render('about', { lang: 'de' })
746
+ * default:
747
+ * return view.render('about', { lang: 'en' })
748
+ * }
749
+ * ```
750
+ */
751
+ language<T extends string>(languages: T[]): T | null {
752
+ this.#initiateAccepts()
753
+ return this.#lazyAccepts.language(languages) || null
754
+ }
755
+
756
+ /**
757
+ * Return the languages that the request accepts, in the order of the
758
+ * client's preference (most preferred first).
759
+ *
760
+ * Make sure to check [accepts](https://www.npmjs.com/package/accepts) package
761
+ * docs too.
762
+ */
763
+ languages(): string[] {
764
+ this.#initiateAccepts()
765
+ return this.#lazyAccepts.languages()
766
+ }
767
+
768
+ /**
769
+ * Returns the best charset using `Accept-charset` header
770
+ * and by matching it against the given charsets.
771
+ *
772
+ * If nothing is matched, then `null` will be returned
773
+ *
774
+ * Make sure to check [accepts](https://www.npmjs.com/package/accepts) package
775
+ * docs too.
776
+ *
777
+ * @example
778
+ * ```js
779
+ * switch (request.charset(['utf-8', 'ISO-8859-1'])) {
780
+ * case 'utf-8':
781
+ * // make utf-8 friendly response
782
+ * case 'ISO-8859-1':
783
+ * // make ISO-8859-1 friendly response
784
+ * }
785
+ * ```
786
+ */
787
+ charset<T extends string>(charsets: T[]): T | null {
788
+ this.#initiateAccepts()
789
+ return this.#lazyAccepts.charset(charsets) || null
790
+ }
791
+
792
+ /**
793
+ * Return the charsets that the request accepts, in the order of the
794
+ * client's preference (most preferred first).
795
+ *
796
+ * Make sure to check [accepts](https://www.npmjs.com/package/accepts) package
797
+ * docs too.
798
+ */
799
+ charsets(): string[] {
800
+ this.#initiateAccepts()
801
+ return this.#lazyAccepts.charsets()
802
+ }
803
+
804
+ /**
805
+ * Returns the best encoding using `Accept-encoding` header
806
+ * and by matching it against the given encodings.
807
+ *
808
+ * If nothing is matched, then `null` will be returned
809
+ *
810
+ * Make sure to check [accepts](https://www.npmjs.com/package/accepts) package
811
+ * docs too.
812
+ */
813
+ encoding<T extends string>(encodings: T[]): T | null {
814
+ this.#initiateAccepts()
815
+ return this.#lazyAccepts.encoding(encodings) || null
816
+ }
817
+
818
+ /**
819
+ * Return the charsets that the request accepts, in the order of the
820
+ * client's preference (most preferred first).
821
+ *
822
+ * Make sure to check [accepts](https://www.npmjs.com/package/accepts) package
823
+ * docs too.
824
+ */
825
+ encodings(): string[] {
826
+ this.#initiateAccepts()
827
+ return this.#lazyAccepts.encodings()
828
+ }
829
+
830
+ /**
831
+ * Returns a boolean telling if request has body
832
+ */
833
+ hasBody(): boolean {
834
+ return typeIs.hasBody(this.request)
835
+ }
836
+
837
+ /**
838
+ * Returns a boolean telling if the new response etag evaluates same
839
+ * as the request header `if-none-match`. In case of `true`, the
840
+ * server must return `304` response, telling the browser to
841
+ * use the client cache.
842
+ *
843
+ * You won't have to deal with this method directly, since AdonisJs will
844
+ * handle this for you when `http.etag = true` inside `config/app.js` file.
845
+ *
846
+ * However, this is how you can use it manually.
847
+ *
848
+ * ```js
849
+ * const responseBody = view.render('some-view')
850
+ *
851
+ * // sets the HTTP etag header for response
852
+ * response.setEtag(responseBody)
853
+ *
854
+ * if (request.fresh()) {
855
+ * response.sendStatus(304)
856
+ * } else {
857
+ * response.send(responseBody)
858
+ * }
859
+ * ```
860
+ */
861
+ fresh(): boolean {
862
+ if (['GET', 'HEAD'].indexOf(this.intended()) === -1) {
863
+ return false
864
+ }
865
+
866
+ const status = this.response.statusCode
867
+ if ((status >= 200 && status < 300) || status === 304) {
868
+ return fresh(this.headers(), this.response.getHeaders())
869
+ }
870
+
871
+ return false
872
+ }
873
+
874
+ /**
875
+ * Opposite of [[fresh]]
876
+ */
877
+ stale(): boolean {
878
+ return !this.fresh()
879
+ }
880
+
881
+ /**
882
+ * Returns all parsed and signed cookies. Signed cookies ensures
883
+ * that their value isn't tampered.
884
+ */
885
+ cookiesList() {
886
+ this.#initiateCookieParser()
887
+ return this.#cookieParser!.list()
888
+ }
889
+
890
+ /**
891
+ * Returns value for a given key from signed cookies. Optional
892
+ * defaultValue is returned when actual value is undefined.
893
+ */
894
+ cookie(key: string, defaultValue?: string): any {
895
+ this.#initiateCookieParser()
896
+ return this.#cookieParser!.unsign(key) || defaultValue
897
+ }
898
+
899
+ /**
900
+ * Returns value for a given key from signed cookies. Optional
901
+ * defaultValue is returned when actual value is undefined.
902
+ */
903
+ encryptedCookie(key: string, defaultValue?: string): any {
904
+ this.#initiateCookieParser()
905
+ return this.#cookieParser!.decrypt(key) || defaultValue
906
+ }
907
+
908
+ /**
909
+ * Returns value for a given key from unsigned cookies. Optional
910
+ * defaultValue is returned when actual value is undefined.
911
+ */
912
+ plainCookie(key: string, options?: { defaultValue?: string; encoded?: boolean }): any
913
+ plainCookie(key: string, defaultValue?: string, encoded?: boolean): any
914
+ plainCookie(
915
+ key: string,
916
+ defaultValueOrOptions?: string | { defaultValue?: string; encoded?: boolean },
917
+ encoded?: boolean
918
+ ): any {
919
+ this.#initiateCookieParser()
920
+
921
+ if (is.object(defaultValueOrOptions)) {
922
+ return (
923
+ this.#cookieParser!.decode(key, defaultValueOrOptions?.encoded) ||
924
+ defaultValueOrOptions.defaultValue
925
+ )
926
+ }
927
+
928
+ return this.#cookieParser!.decode(key, encoded) || defaultValueOrOptions
929
+ }
930
+
931
+ /**
932
+ * Returns a boolean telling if a signed url as a valid signature
933
+ * or not.
934
+ */
935
+ hasValidSignature(purpose?: string) {
936
+ const { signature, ...rest } = this.qs()
937
+ if (!signature) {
938
+ return false
939
+ }
940
+
941
+ /*
942
+ * Return false when signature fails
943
+ */
944
+ const signedUrl = this.#encryption.verifier.unsign(signature, purpose)
945
+ if (!signedUrl) {
946
+ return false
947
+ }
948
+
949
+ const queryString = this.#qsParser.stringify(rest)
950
+
951
+ return queryString
952
+ ? safeEqual(signedUrl, `${this.url()}?${queryString}`)
953
+ : safeEqual(signedUrl, this.url())
954
+ }
955
+
956
+ /**
957
+ * Serializes request to JSON format
958
+ */
959
+ serialize() {
960
+ return {
961
+ id: this.id(),
962
+ url: this.url(),
963
+ query: this.parsedUrl.query,
964
+ body: this.all(),
965
+ params: this.params(),
966
+ headers: this.headers(),
967
+ method: this.method(),
968
+ protocol: this.protocol(),
969
+ cookies: this.cookiesList(),
970
+ hostname: this.hostname(),
971
+ ip: this.ip(),
972
+ subdomains: this.ctx?.subdomains || {},
973
+ }
974
+ }
975
+
976
+ /**
977
+ * toJSON copy of the request
978
+ */
979
+ toJSON() {
980
+ return this.serialize()
981
+ }
982
+ }