dump.js 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. 'use strict'
  2. const { InvalidArgumentError, RequestAbortedError } = require('../core/errors')
  3. const DecoratorHandler = require('../handler/decorator-handler')
  4. class DumpHandler extends DecoratorHandler {
  5. #maxSize = 1024 * 1024
  6. #dumped = false
  7. #size = 0
  8. aborted = false
  9. reason = false
  10. constructor ({ maxSize, signal }, handler) {
  11. if (maxSize != null && (!Number.isFinite(maxSize) || maxSize < 1)) {
  12. throw new InvalidArgumentError('maxSize must be a number greater than 0')
  13. }
  14. super(handler)
  15. this.#maxSize = maxSize ?? this.#maxSize
  16. // this.#handler = handler
  17. }
  18. #abort (reason) {
  19. this.aborted = true
  20. this.reason = reason
  21. }
  22. onRequestStart (controller, context) {
  23. controller.abort = this.#abort.bind(this)
  24. return super.onRequestStart(controller, context)
  25. }
  26. onResponseStart (controller, statusCode, headers, statusMessage) {
  27. const contentLength = headers['content-length']
  28. if (contentLength != null && contentLength > this.#maxSize) {
  29. throw new RequestAbortedError(
  30. `Response size (${contentLength}) larger than maxSize (${
  31. this.#maxSize
  32. })`
  33. )
  34. }
  35. if (this.aborted === true) {
  36. return true
  37. }
  38. return super.onResponseStart(controller, statusCode, headers, statusMessage)
  39. }
  40. onResponseError (controller, err) {
  41. super.onResponseError(controller, this.aborted === true ? this.reason : err)
  42. }
  43. onResponseData (controller, chunk) {
  44. this.#size = this.#size + chunk.length
  45. if (this.#size > this.#maxSize) {
  46. throw new RequestAbortedError(
  47. `Response size (${this.#size}) larger than maxSize (${this.#maxSize})`
  48. )
  49. }
  50. if (this.#size === this.#maxSize) {
  51. this.#dumped = true
  52. }
  53. return true
  54. }
  55. onResponseEnd (controller, trailers) {
  56. if (this.aborted === true) {
  57. super.onResponseError(controller, this.reason)
  58. return
  59. }
  60. super.onResponseEnd(controller, this.#dumped ? {} : trailers)
  61. }
  62. }
  63. function createDumpInterceptor (
  64. { maxSize: defaultMaxSize } = {
  65. maxSize: 1024 * 1024
  66. }
  67. ) {
  68. return dispatch => {
  69. return function Intercept (opts, handler) {
  70. const { dumpMaxSize = defaultMaxSize } = opts
  71. const dumpHandler = new DumpHandler({ maxSize: dumpMaxSize, signal: opts.signal }, handler)
  72. return dispatch(opts, dumpHandler)
  73. }
  74. }
  75. }
  76. module.exports = createDumpInterceptor