Skip to content

Commit c178ca1

Browse files
authored
Correctly parse JSON contentType in responseError interceptor (#3892)
Signed-off-by: Matteo Collina <[email protected]>
1 parent 513e213 commit c178ca1

File tree

2 files changed

+92
-2
lines changed

2 files changed

+92
-2
lines changed

lib/interceptor/response-error.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ class ResponseErrorHandler extends DecoratorHandler {
2727
return this.#handler.onConnect(abort)
2828
}
2929

30+
#checkContentType (contentType) {
31+
return this.#contentType.indexOf(contentType) === 0
32+
}
33+
3034
onHeaders (statusCode, rawHeaders, resume, statusMessage, headers = parseHeaders(rawHeaders)) {
3135
this.#statusCode = statusCode
3236
this.#headers = headers
@@ -36,7 +40,7 @@ class ResponseErrorHandler extends DecoratorHandler {
3640
return this.#handler.onHeaders(statusCode, rawHeaders, resume, statusMessage, headers)
3741
}
3842

39-
if (this.#contentType === 'application/json' || this.#contentType === 'text/plain') {
43+
if (this.#checkContentType('application/json') || this.#checkContentType('text/plain')) {
4044
this.#decoder = new TextDecoder('utf-8')
4145
}
4246
}
@@ -53,7 +57,7 @@ class ResponseErrorHandler extends DecoratorHandler {
5357
if (this.#statusCode >= 400) {
5458
this.#body += this.#decoder?.decode(undefined, { stream: false }) ?? ''
5559

56-
if (this.#contentType === 'application/json') {
60+
if (this.#checkContentType('application/json')) {
5761
try {
5862
this.#body = JSON.parse(this.#body)
5963
} catch {

test/interceptors/response-error.js

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,89 @@ test('should not throw error for ok response', async () => {
8282
assert.equal(response.statusCode, 200)
8383
assert.equal(await response.body.text(), 'hello')
8484
})
85+
86+
test('should throw error for error response, parsing JSON', async () => {
87+
const server = createServer()
88+
89+
server.on('request', (req, res) => {
90+
res.writeHead(400, { 'content-type': 'application/json; charset=utf-8' })
91+
res.end(JSON.stringify({ message: 'Bad Request' }))
92+
})
93+
94+
server.listen(0)
95+
96+
await once(server, 'listening')
97+
98+
const client = new Client(
99+
`http://localhost:${server.address().port}`
100+
).compose(responseError())
101+
102+
after(async () => {
103+
await client.close()
104+
server.close()
105+
106+
await once(server, 'close')
107+
})
108+
109+
let error
110+
try {
111+
await client.request({
112+
method: 'GET',
113+
path: '/',
114+
headers: {
115+
'content-type': 'text/plain'
116+
}
117+
})
118+
} catch (err) {
119+
error = err
120+
}
121+
122+
assert.equal(error.statusCode, 400)
123+
assert.equal(error.message, 'Response Error')
124+
assert.deepStrictEqual(error.body, {
125+
message: 'Bad Request'
126+
})
127+
})
128+
129+
test('should throw error for error response, parsing JSON without charset', async () => {
130+
const server = createServer()
131+
132+
server.on('request', (req, res) => {
133+
res.writeHead(400, { 'content-type': 'application/json' })
134+
res.end(JSON.stringify({ message: 'Bad Request' }))
135+
})
136+
137+
server.listen(0)
138+
139+
await once(server, 'listening')
140+
141+
const client = new Client(
142+
`http://localhost:${server.address().port}`
143+
).compose(responseError())
144+
145+
after(async () => {
146+
await client.close()
147+
server.close()
148+
149+
await once(server, 'close')
150+
})
151+
152+
let error
153+
try {
154+
await client.request({
155+
method: 'GET',
156+
path: '/',
157+
headers: {
158+
'content-type': 'text/plain'
159+
}
160+
})
161+
} catch (err) {
162+
error = err
163+
}
164+
165+
assert.equal(error.statusCode, 400)
166+
assert.equal(error.message, 'Response Error')
167+
assert.deepStrictEqual(error.body, {
168+
message: 'Bad Request'
169+
})
170+
})

0 commit comments

Comments
 (0)