Skip to content
This repository was archived by the owner on Mar 10, 2020. It is now read-only.

Commit 4251c88

Browse files
author
Alan Shaw
authored
fix: error reporting for non-JSON responses (#1016)
Better error reporting by detecting `Content-Type` of the response and not attempting to parse JSON for non-`application/json` responses. resolves #912 resolves #1000 closes #1001 License: MIT Signed-off-by: Alan Shaw <alan.shaw@protocol.ai>
1 parent a28b009 commit 4251c88

File tree

3 files changed

+94
-3
lines changed

3 files changed

+94
-3
lines changed

Diff for: src/utils/send-request.js

+19-2
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,26 @@ const log = require('debug')('ipfs-http-client:request')
1313

1414
// -- Internal
1515

16+
function hasJSONHeaders (res) {
17+
return res.headers['content-type'] &&
18+
res.headers['content-type'].indexOf('application/json') === 0
19+
}
20+
1621
function parseError (res, cb) {
1722
const error = new Error(`Server responded with ${res.statusCode}`)
23+
error.statusCode = res.statusCode
24+
25+
if (!hasJSONHeaders(res)) {
26+
return streamToValue(res, (err, data) => { // eslint-disable-line handle-callback-err
27+
// the `err` here refers to errors in stream processing, which
28+
// we ignore here, since we already have a valid `error` response
29+
// from the server above that we have to report to the caller.
30+
if (data && data.length) {
31+
error.message = data.toString()
32+
}
33+
cb(error)
34+
})
35+
}
1836

1937
streamToJsonValue(res, (err, payload) => {
2038
if (err) {
@@ -34,8 +52,7 @@ function onRes (buffer, cb) {
3452
return (res) => {
3553
const stream = Boolean(res.headers['x-stream-output'])
3654
const chunkedObjects = Boolean(res.headers['x-chunked-output'])
37-
const isJson = res.headers['content-type'] &&
38-
res.headers['content-type'].indexOf('application/json') === 0
55+
const isJson = hasJSONHeaders(res)
3956

4057
if (res.req) {
4158
log(res.req.method, `${res.req.getHeaders().host}${res.req.path}`, res.statusCode, res.statusMessage)

Diff for: src/utils/stream-to-json-value.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ function streamToJsonValue (res, cb) {
2424
try {
2525
res = JSON.parse(data)
2626
} catch (err) {
27-
return cb(err)
27+
return cb(new Error(`Invalid JSON: ${data}`))
2828
}
2929

3030
cb(null, res)

Diff for: test/request-api.spec.js

+74
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,77 @@ describe('trailer headers', () => {
6565
})
6666
})
6767
})
68+
69+
describe('error handling', () => {
70+
it('should handle plain text error response', function (done) {
71+
if (!isNode) return this.skip()
72+
73+
const server = require('http').createServer((req, res) => {
74+
// Consume the entire request, before responding.
75+
req.on('data', () => {})
76+
req.on('end', () => {
77+
// Write a text/plain response with a 403 (forbidden) status
78+
res.writeHead(403, { 'Content-Type': 'text/plain' })
79+
res.write('ipfs method not allowed')
80+
res.end()
81+
})
82+
})
83+
84+
server.listen(6001, () => {
85+
ipfsClient('/ip4/127.0.0.1/tcp/6001').config.replace('test/fixtures/r-config.json', (err) => {
86+
expect(err).to.exist()
87+
expect(err.statusCode).to.equal(403)
88+
expect(err.message).to.equal('ipfs method not allowed')
89+
server.close(done)
90+
})
91+
})
92+
})
93+
94+
it('should handle JSON error response', function (done) {
95+
if (!isNode) return this.skip()
96+
97+
const server = require('http').createServer((req, res) => {
98+
// Consume the entire request, before responding.
99+
req.on('data', () => {})
100+
req.on('end', () => {
101+
// Write a application/json response with a 400 (bad request) header
102+
res.writeHead(400, { 'Content-Type': 'application/json' })
103+
res.write(JSON.stringify({ Message: 'client error', Code: 1 }))
104+
res.end()
105+
})
106+
})
107+
108+
server.listen(6001, () => {
109+
ipfsClient('/ip4/127.0.0.1/tcp/6001').config.replace('test/fixtures/r-config.json', (err) => {
110+
expect(err).to.exist()
111+
expect(err.statusCode).to.equal(400)
112+
expect(err.message).to.equal('client error')
113+
expect(err.code).to.equal(1)
114+
server.close(done)
115+
})
116+
})
117+
})
118+
119+
it('should handle JSON error response with invalid JSON', function (done) {
120+
if (!isNode) return this.skip()
121+
122+
const server = require('http').createServer((req, res) => {
123+
// Consume the entire request, before responding.
124+
req.on('data', () => {})
125+
req.on('end', () => {
126+
// Write a application/json response with a 400 (bad request) header
127+
res.writeHead(400, { 'Content-Type': 'application/json' })
128+
res.write('{ Message: ')
129+
res.end()
130+
})
131+
})
132+
133+
server.listen(6001, () => {
134+
ipfsClient('/ip4/127.0.0.1/tcp/6001').config.replace('test/fixtures/r-config.json', (err) => {
135+
expect(err).to.exist()
136+
expect(err.message).to.include('Invalid JSON')
137+
server.close(done)
138+
})
139+
})
140+
})
141+
})

0 commit comments

Comments
 (0)