From 1cb9070cce492bdd0982a19c85c4fda00a81e861 Mon Sep 17 00:00:00 2001 From: Konstantin Vyatkin Date: Wed, 10 Jun 2020 07:16:51 -0400 Subject: [PATCH] [Spec] Should check body _source_ on redirect (#866) * correct stream tests * bump Node.JS min to 10.17 * lint --- package.json | 3 +-- src/body.js | 4 ++++ src/index.js | 4 ++-- test/main.js | 37 +++++++++++-------------------------- test/request.js | 6 +++--- test/response.js | 13 +++++-------- 6 files changed, 26 insertions(+), 41 deletions(-) diff --git a/package.json b/package.json index ad7277e..31c5f61 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ ], "types": "./@types/index.d.ts", "engines": { - "node": ">=10.16" + "node": ">=10.17" }, "scripts": { "build": "rollup -c", @@ -60,7 +60,6 @@ "mocha": "^7.2.0", "p-timeout": "^3.2.0", "parted": "^0.1.1", - "resumer": "0.0.0", "rollup": "^2.15.0", "string-to-arraybuffer": "^1.0.2", "tsd": "^0.11.0", diff --git a/src/body.js b/src/body.js index ad1c040..9ad8d07 100644 --- a/src/body.js +++ b/src/body.js @@ -208,6 +208,10 @@ async function consumeBody(data) { if (body.readableEnded === true || body._readableState.ended === true) { try { + if (accum.every(c => typeof c === 'string')) { + return Buffer.from(accum.join('')); + } + return Buffer.concat(accum, accumBytes); } catch (error) { throw new FetchError(`Could not create Buffer from response body for ${data.url}: ${error.message}`, 'system', error); diff --git a/src/index.js b/src/index.js index f1132c3..e6d1920 100644 --- a/src/index.js +++ b/src/index.js @@ -12,7 +12,7 @@ import zlib from 'zlib'; import Stream, {PassThrough, pipeline as pump} from 'stream'; import dataUriToBuffer from 'data-uri-to-buffer'; -import {writeToStream, getTotalBytes} from './body.js'; +import {writeToStream} from './body.js'; import Response from './response.js'; import Headers, {fromRawHeaders} from './headers.js'; import Request, {getNodeRequestOptions} from './request.js'; @@ -153,7 +153,7 @@ export default async function fetch(url, options_) { }; // HTTP-redirect fetch step 9 - if (response_.statusCode !== 303 && request.body && getTotalBytes(request) === null) { + if (response_.statusCode !== 303 && request.body && options_.body instanceof Stream.Readable) { reject(new FetchError('Cannot follow redirect with body being a readable stream', 'unsupported-redirect')); finalize(); return; diff --git a/test/main.js b/test/main.js index f341242..66b6379 100644 --- a/test/main.js +++ b/test/main.js @@ -1,4 +1,5 @@ // Test tools +/* eslint-disable node/no-unsupported-features/node-builtins */ import zlib from 'zlib'; import crypto from 'crypto'; import http from 'http'; @@ -10,7 +11,6 @@ import chai from 'chai'; import chaiPromised from 'chai-as-promised'; import chaiIterator from 'chai-iterator'; import chaiString from 'chai-string'; -import resumer from 'resumer'; import FormData from 'form-data'; import stringToArrayBuffer from 'string-to-arraybuffer'; import delay from 'delay'; @@ -404,7 +404,7 @@ describe('node-fetch', () => { const url = `${base}redirect/307`; const options = { method: 'PATCH', - body: resumer().queue('a=1').end() + body: stream.Readable.from('tada') }; return expect(fetch(url, options)).to.eventually.be.rejected .and.be.an.instanceOf(FetchError) @@ -1250,13 +1250,10 @@ describe('node-fetch', () => { }); it('should allow POST request with readable stream as body', () => { - let body = resumer().queue('a=1').end(); - body = body.pipe(new stream.PassThrough()); - const url = `${base}inspect`; const options = { method: 'POST', - body + body: stream.Readable.from('a=1') }; return fetch(url, options).then(res => { return res.json(); @@ -1971,27 +1968,16 @@ describe('node-fetch', () => { // Issue #414 it('should reject if attempt to accumulate body stream throws', () => { - let body = resumer().queue('a=1').end(); - body = body.pipe(new stream.PassThrough()); - const res = new Response(body); - const bufferConcat = Buffer.concat; - const restoreBufferConcat = () => { - Buffer.concat = bufferConcat; - }; + const res = new Response(stream.Readable.from((async function * () { + yield Buffer.from('tada'); + await new Promise(resolve => setTimeout(resolve, 200)); + yield {tada: 'yes'}; + })())); - Buffer.concat = () => { - throw new Error('embedded error'); - }; - - const textPromise = res.text(); - // Ensure that `Buffer.concat` is always restored: - textPromise.then(restoreBufferConcat, restoreBufferConcat); - - return expect(textPromise).to.eventually.be.rejected + return expect(res.text()).to.eventually.be.rejected .and.be.an.instanceOf(FetchError) .and.include({type: 'system'}) - .and.have.property('message').that.includes('Could not create Buffer') - .and.that.includes('embedded error'); + .and.have.property('message').that.include('Could not create Buffer'); }); it('supports supplying a lookup function to the agent', () => { @@ -2053,8 +2039,7 @@ describe('node-fetch', () => { const url = `${base}hello`; const bodyContent = 'a=1'; - let streamBody = resumer().queue(bodyContent).end(); - streamBody = streamBody.pipe(new stream.PassThrough()); + const streamBody = stream.Readable.from(bodyContent); const streamRequest = new Request(url, { method: 'POST', body: streamBody, diff --git a/test/request.js b/test/request.js index 6444a87..502b86a 100644 --- a/test/request.js +++ b/test/request.js @@ -1,10 +1,11 @@ +/* eslint-disable node/no-unsupported-features/node-builtins */ + import stream from 'stream'; import http from 'http'; import AbortController from 'abort-controller'; import chai from 'chai'; import FormData from 'form-data'; import Blob from 'fetch-blob'; -import resumer from 'resumer'; import stringToArrayBuffer from 'string-to-arraybuffer'; import TestServer from './utils/server.js'; @@ -200,8 +201,7 @@ describe('Request', () => { it('should support clone() method', () => { const url = base; - let body = resumer().queue('a=1').end(); - body = body.pipe(new stream.PassThrough()); + const body = stream.Readable.from('a=1'); const agent = new http.Agent(); const {signal} = new AbortController(); const request = new Request(url, { diff --git a/test/response.js b/test/response.js index 1e523b8..7126eb9 100644 --- a/test/response.js +++ b/test/response.js @@ -1,6 +1,7 @@ +/* eslint-disable node/no-unsupported-features/node-builtins */ + import * as stream from 'stream'; import chai from 'chai'; -import resumer from 'resumer'; import stringToArrayBuffer from 'string-to-arraybuffer'; import Blob from 'fetch-blob'; import {Response} from '../src/index.js'; @@ -54,9 +55,7 @@ describe('Response', () => { }); it('should support empty options', () => { - let body = resumer().queue('a=1').end(); - body = body.pipe(new stream.PassThrough()); - const res = new Response(body); + const res = new Response(stream.Readable.from('a=1')); return res.text().then(result => { expect(result).to.equal('a=1'); }); @@ -107,8 +106,7 @@ describe('Response', () => { }); it('should support clone() method', () => { - let body = resumer().queue('a=1').end(); - body = body.pipe(new stream.PassThrough()); + const body = stream.Readable.from('a=1'); const res = new Response(body, { headers: { a: '1' @@ -131,8 +129,7 @@ describe('Response', () => { }); it('should support stream as body', () => { - let body = resumer().queue('a=1').end(); - body = body.pipe(new stream.PassThrough()); + const body = stream.Readable.from('a=1'); const res = new Response(body); return res.text().then(result => { expect(result).to.equal('a=1');