diff --git a/src/gaxios.ts b/src/gaxios.ts index a8ef4c7f..2df23b4c 100644 --- a/src/gaxios.ts +++ b/src/gaxios.ts @@ -44,6 +44,8 @@ interface FetchCompliance { fetch: typeof fetch; } +const HTTP_STATUS_NO_CONTENT = 204; + export class Gaxios implements FetchCompliance { protected agentCache = new Map< string | URL, @@ -254,6 +256,10 @@ export class Gaxios implements FetchCompliance { opts: GaxiosOptionsPrepared, res: Response, ): Promise> { + if (res.status === HTTP_STATUS_NO_CONTENT) { + return ''; + } + if ( opts.maxContentLength && res.headers.has('content-length') && @@ -270,8 +276,14 @@ export class Gaxios implements FetchCompliance { switch (opts.responseType) { case 'stream': return res.body; - case 'json': - return res.json(); + case 'json': { + const data = await res.text(); + try { + return JSON.parse(data); + } catch { + return data; + } + } case 'arraybuffer': return res.arrayBuffer(); case 'blob': @@ -605,7 +617,7 @@ export class Gaxios implements FetchCompliance { * This implementation follows the spec: https://www.ietf.org/rfc/rfc2387.txt. However, recursive * multipart/related requests are not currently supported. * - * @param {GaxioMultipartOptions[]} multipartOptions the pieces to turn into a multipart/related body. + * @param {GaxiosMultipartOptions[]} multipartOptions the pieces to turn into a multipart/related body. * @param {string} boundary the boundary string to be placed between each part. */ private async *getMultipartRequest( diff --git a/test/test.getch.ts b/test/test.getch.ts index b05cf101..3db1a3d4 100644 --- a/test/test.getch.ts +++ b/test/test.getch.ts @@ -1214,6 +1214,45 @@ describe('🎏 data handling', () => { scope.done(); } }); + + it('should handle "204 No Content" responses when response type is "json"', async () => { + const scope = nock(url) + .matchHeader('content-type', 'application/json') + .put('/') + .reply(204, '', {'Content-Type': 'application/json'}); + const res = await request({ + url, + method: 'PUT', + data: {}, + headers: new Headers({ + 'content-type': 'application/json', + accept: 'application/json', + }), + responseType: 'json', + }); + scope.done(); + assert.deepStrictEqual(res.data, ''); + }); + + it('should not throw an error in case of invalid json and "json" response type', async () => { + const invalidJsonText = '{foo: 1}', + scope = nock(url) + .matchHeader('content-type', 'application/json') + .put('/') + .reply(200, invalidJsonText, {'Content-Type': 'application/json'}); + const res = await request({ + url, + method: 'PUT', + data: {}, + headers: new Headers({ + 'content-type': 'application/json', + accept: 'application/json', + }), + responseType: 'json', + }); + scope.done(); + assert.deepStrictEqual(res.data, invalidJsonText); + }); }); describe('🍂 defaults & instances', () => {