dump.js 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. 'use strict'
  2. const util = require('../core/util')
  3. const { InvalidArgumentError, RequestAbortedError } = require('../core/errors')
  4. const DecoratorHandler = require('../handler/decorator-handler')
  5. class DumpHandler extends DecoratorHandler {
  6. #maxSize = 1024 * 1024
  7. #abort = null
  8. #dumped = false
  9. #aborted = false
  10. #size = 0
  11. #reason = null
  12. #handler = null
  13. constructor ({ maxSize }, handler) {
  14. super(handler)
  15. if (maxSize != null && (!Number.isFinite(maxSize) || maxSize < 1)) {
  16. throw new InvalidArgumentError('maxSize must be a number greater than 0')
  17. }
  18. this.#maxSize = maxSize ?? this.#maxSize
  19. this.#handler = handler
  20. }
  21. onConnect (abort) {
  22. this.#abort = abort
  23. this.#handler.onConnect(this.#customAbort.bind(this))
  24. }
  25. #customAbort (reason) {
  26. this.#aborted = true
  27. this.#reason = reason
  28. }
  29. // TODO: will require adjustment after new hooks are out
  30. onHeaders (statusCode, rawHeaders, resume, statusMessage) {
  31. const headers = util.parseHeaders(rawHeaders)
  32. const contentLength = headers['content-length']
  33. if (contentLength != null && contentLength > this.#maxSize) {
  34. throw new RequestAbortedError(
  35. `Response size (${contentLength}) larger than maxSize (${
  36. this.#maxSize
  37. })`
  38. )
  39. }
  40. if (this.#aborted) {
  41. return true
  42. }
  43. return this.#handler.onHeaders(
  44. statusCode,
  45. rawHeaders,
  46. resume,
  47. statusMessage
  48. )
  49. }
  50. onError (err) {
  51. if (this.#dumped) {
  52. return
  53. }
  54. err = this.#reason ?? err
  55. this.#handler.onError(err)
  56. }
  57. onData (chunk) {
  58. this.#size = this.#size + chunk.length
  59. if (this.#size >= this.#maxSize) {
  60. this.#dumped = true
  61. if (this.#aborted) {
  62. this.#handler.onError(this.#reason)
  63. } else {
  64. this.#handler.onComplete([])
  65. }
  66. }
  67. return true
  68. }
  69. onComplete (trailers) {
  70. if (this.#dumped) {
  71. return
  72. }
  73. if (this.#aborted) {
  74. this.#handler.onError(this.reason)
  75. return
  76. }
  77. this.#handler.onComplete(trailers)
  78. }
  79. }
  80. function createDumpInterceptor (
  81. { maxSize: defaultMaxSize } = {
  82. maxSize: 1024 * 1024
  83. }
  84. ) {
  85. return dispatch => {
  86. return function Intercept (opts, handler) {
  87. const { dumpMaxSize = defaultMaxSize } =
  88. opts
  89. const dumpHandler = new DumpHandler(
  90. { maxSize: dumpMaxSize },
  91. handler
  92. )
  93. return dispatch(opts, dumpHandler)
  94. }
  95. }
  96. }
  97. module.exports = createDumpInterceptor