diff --git a/lib/interceptor/response-error.js b/lib/interceptor/response-error.js index a8105aa1437..0db8234c9bd 100644 --- a/lib/interceptor/response-error.js +++ b/lib/interceptor/response-error.js @@ -5,41 +5,49 @@ const DecoratorHandler = require('../handler/decorator-handler') const { ResponseError } = require('../core/errors') class ResponseErrorHandler extends DecoratorHandler { - #statusCode - #contentType - #decoder - #headers - #body - - constructor (_opts, { handler }) { + #contentType = '' + #isParseableRespnse = false + #statusCode = null + #decoder = null + #headers = null + #body = null + #opts = null + + constructor (opts, { handler }) { super(handler) + this.#opts = opts } - #checkContentType (contentType) { - return (this.#contentType ?? '').indexOf(contentType) === 0 + #isParsableContentType (contentType) { + return ( + this.#contentType.indexOf('application/json') !== -1 || + this.#contentType.indexO('text/plain') !== -1 + ) } onRequestStart (controller, context) { - this.#statusCode = 0 - this.#contentType = null - this.#decoder = null - this.#headers = null - this.#body = '' - return super.onRequestStart(controller, context) } onResponseStart (controller, statusCode, headers, statusMessage) { - this.#statusCode = statusCode - this.#headers = headers - this.#contentType = headers['content-type'] - if (this.#statusCode < 400) { - return super.onResponseStart(controller, statusCode, headers, statusMessage) + return super.onResponseStart( + controller, + statusCode, + headers, + statusMessage + ) } - if (this.#checkContentType('application/json') || this.#checkContentType('text/plain')) { + this.#statusCode = statusCode + this.#headers = headers + this.#contentType = headers['content-type'] + this.#isParseableRespnse = this.#isParsableContentType(this.#contentType) + if (this.#opts.shouldParseBody && this.#isParseableRespnse) { + this.#body = '' this.#decoder = new TextDecoder('utf-8') + } else { + this.#body = [] // pushing chunks instead } } @@ -48,18 +56,28 @@ class ResponseErrorHandler extends DecoratorHandler { return super.onResponseData(controller, chunk) } - this.#body += this.#decoder?.decode(chunk, { stream: true }) ?? '' + if (Array.isArray(this.#body)) { + this.#body.push(chunk) + } else { + this.#body += this.#decoder?.decode(chunk, { stream: true }) ?? '' + } } onResponseEnd (controller, trailers) { if (this.#statusCode >= 400) { - this.#body += this.#decoder?.decode(undefined, { stream: false }) ?? '' - - if (this.#checkContentType('application/json')) { - try { - this.#body = JSON.parse(this.#body) - } catch { - // Do nothing... + if (Array.isArray(this.#body)) this.#body = Buffer.concat(this.#body) + else { + this.#body += this.#decoder?.decode(undefined, { stream: false }) ?? '' + if ( + this.#isParseableRespnse && + // TODO: Story content-type as part of the handler + this.#contentType.indexOf('application/json') !== -1 + ) { + try { + this.#body = JSON.parse(this.#body) + } catch { + // Do nothing... + } } } @@ -87,8 +105,19 @@ class ResponseErrorHandler extends DecoratorHandler { } module.exports = () => { - return (dispatch) => { + return dispatch => { return function Intercept (opts, handler) { + if ( + opts?.shouldParseBody != null && + typeof opts.shouldParseBody !== 'boolean' + ) { + throw new TypeError('opts.shouldParseBody should be a boolean') + } + + opts = { + shouldParseBody: opts?.shouldParseBody ?? true + } + return dispatch(opts, new ResponseErrorHandler(opts, { handler })) } }