'use strict' const { InvalidArgumentError, RequestAbortedError } = require('../core/errors') const DecoratorHandler = require('../handler/decorator-handler') class DumpHandler extends DecoratorHandler { #maxSize = 1024 * 1024 #dumped = false #size = 0 #controller = null aborted = false reason = false constructor ({ maxSize, signal }, handler) { if (maxSize != null && (!Number.isFinite(maxSize) || maxSize < 1)) { throw new InvalidArgumentError('maxSize must be a number greater than 0') } super(handler) this.#maxSize = maxSize ?? this.#maxSize // this.#handler = handler } #abort (reason) { this.aborted = true this.reason = reason } onRequestStart (controller, context) { controller.abort = this.#abort.bind(this) this.#controller = controller return super.onRequestStart(controller, context) } onResponseStart (controller, statusCode, headers, statusMessage) { const contentLength = headers['content-length'] if (contentLength != null && contentLength > this.#maxSize) { throw new RequestAbortedError( `Response size (${contentLength}) larger than maxSize (${ this.#maxSize })` ) } if (this.aborted === true) { return true } return super.onResponseStart(controller, statusCode, headers, statusMessage) } onResponseError (controller, err) { if (this.#dumped) { return } err = this.#controller.reason ?? err super.onResponseError(controller, err) } onResponseData (controller, chunk) { this.#size = this.#size + chunk.length if (this.#size >= this.#maxSize) { this.#dumped = true if (this.aborted === true) { super.onResponseError(controller, this.reason) } else { super.onResponseEnd(controller, {}) } } return true } onResponseEnd (controller, trailers) { if (this.#dumped) { return } if (this.#controller.aborted === true) { super.onResponseError(controller, this.reason) return } super.onResponseEnd(controller, trailers) } } function createDumpInterceptor ( { maxSize: defaultMaxSize } = { maxSize: 1024 * 1024 } ) { return dispatch => { return function Intercept (opts, handler) { const { dumpMaxSize = defaultMaxSize } = opts const dumpHandler = new DumpHandler({ maxSize: dumpMaxSize, signal: opts.signal }, handler) return dispatch(opts, dumpHandler) } } } module.exports = createDumpInterceptor