aboutsummaryrefslogtreecommitdiffstats
path: root/vanilla/node_modules/undici/lib/interceptor/response-error.js
blob: a8105aa1437feeec76d5d21c74af9ab7028232f6 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
'use strict'

// const { parseHeaders } = require('../core/util')
const DecoratorHandler = require('../handler/decorator-handler')
const { ResponseError } = require('../core/errors')

class ResponseErrorHandler extends DecoratorHandler {
  #statusCode
  #contentType
  #decoder
  #headers
  #body

  constructor (_opts, { handler }) {
    super(handler)
  }

  #checkContentType (contentType) {
    return (this.#contentType ?? '').indexOf(contentType) === 0
  }

  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)
    }

    if (this.#checkContentType('application/json') || this.#checkContentType('text/plain')) {
      this.#decoder = new TextDecoder('utf-8')
    }
  }

  onResponseData (controller, chunk) {
    if (this.#statusCode < 400) {
      return super.onResponseData(controller, chunk)
    }

    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...
        }
      }

      let err
      const stackTraceLimit = Error.stackTraceLimit
      Error.stackTraceLimit = 0
      try {
        err = new ResponseError('Response Error', this.#statusCode, {
          body: this.#body,
          headers: this.#headers
        })
      } finally {
        Error.stackTraceLimit = stackTraceLimit
      }

      super.onResponseError(controller, err)
    } else {
      super.onResponseEnd(controller, trailers)
    }
  }

  onResponseError (controller, err) {
    super.onResponseError(controller, err)
  }
}

module.exports = () => {
  return (dispatch) => {
    return function Intercept (opts, handler) {
      return dispatch(opts, new ResponseErrorHandler(opts, { handler }))
    }
  }
}