From 31f0e0a0c692c36c50d4e23a704c046bd5ed9fa4 Mon Sep 17 00:00:00 2001 From: Cory Reed Date: Thu, 21 Nov 2019 10:50:05 -0800 Subject: [PATCH] fix(adapter-node-http): Correctly handle uploading binary data (#257) --- .../@pollyjs/adapter-node-http/package.json | 2 + .../adapter-node-http/rollup.config.test.js | 2 +- .../@pollyjs/adapter-node-http/src/index.js | 16 ++++---- .../tests/integration/adapter-test.js | 37 ++++++++++++++++++- yarn.lock | 9 +++++ 5 files changed, 55 insertions(+), 11 deletions(-) diff --git a/packages/@pollyjs/adapter-node-http/package.json b/packages/@pollyjs/adapter-node-http/package.json index cca72f8a..f967c8de 100644 --- a/packages/@pollyjs/adapter-node-http/package.json +++ b/packages/@pollyjs/adapter-node-http/package.json @@ -51,6 +51,8 @@ "devDependencies": { "@pollyjs/core": "^2.6.3", "@pollyjs/persister-fs": "^2.6.3", + "form-data": "^2.5.1", + "get-stream": "^5.1.0", "node-fetch": "^2.6.0", "rollup": "^1.14.6" } diff --git a/packages/@pollyjs/adapter-node-http/rollup.config.test.js b/packages/@pollyjs/adapter-node-http/rollup.config.test.js index 5e7c1347..546724be 100644 --- a/packages/@pollyjs/adapter-node-http/rollup.config.test.js +++ b/packages/@pollyjs/adapter-node-http/rollup.config.test.js @@ -3,7 +3,7 @@ import createJestTestConfig from '../../../scripts/rollup/jest.test.config'; import { external } from './rollup.config.shared'; -const testExternal = [...external, 'crypto']; +const testExternal = [...external, 'crypto', 'fs', 'path']; export default [ createNodeTestConfig({ external: testExternal }), diff --git a/packages/@pollyjs/adapter-node-http/src/index.js b/packages/@pollyjs/adapter-node-http/src/index.js index 7f1063ad..baca62f5 100644 --- a/packages/@pollyjs/adapter-node-http/src/index.js +++ b/packages/@pollyjs/adapter-node-http/src/index.js @@ -70,17 +70,15 @@ export default class HttpAdapter extends Adapter { ...REQUEST_ARGUMENTS.get(req) ); const url = getUrlFromOptions(parsedArguments.options); - const contentType = (headers['content-type'] || '').toString(); - const isMultiPart = contentType.includes('multipart'); if (body) { - if (isMultiPart && Buffer.isBuffer(body)) { - // Nock can return a hex-encoded body multipart/form-data - if (!isUtf8Representable(body)) { - body = Buffer.from(body, 'hex'); - } - - body = body.toString(); + if ( + typeof body === 'string' && + !isUtf8Representable(Buffer.from(body, 'hex')) + ) { + // Nock internally converts a binary buffer into its hexadecimal + // representation so convert it back to a buffer. + body = Buffer.from(body, 'hex'); } else if (isJSONContent(headers)) { // Nock will parse json content into an object. We have our own way // of dealing with json content so convert it back to a string. diff --git a/packages/@pollyjs/adapter-node-http/tests/integration/adapter-test.js b/packages/@pollyjs/adapter-node-http/tests/integration/adapter-test.js index 327c3b83..4f68df85 100644 --- a/packages/@pollyjs/adapter-node-http/tests/integration/adapter-test.js +++ b/packages/@pollyjs/adapter-node-http/tests/integration/adapter-test.js @@ -1,6 +1,10 @@ +import fs from 'fs'; import http from 'http'; import https from 'https'; +import path from 'path'; +import FormData from 'form-data'; +import getStream from 'get-stream'; import adapterIdentifierTests from '@pollyjs-tests/integration/adapter-identifier-tests'; import setupFetchRecord from '@pollyjs-tests/helpers/setup-fetch-record'; import adapterTests from '@pollyjs-tests/integration/adapter-tests'; @@ -69,7 +73,7 @@ describe('Integration | Node Http Adapter', function() { function commonTests(transport) { const { protocol } = transport.globalAgent; - it('should handle posting a buffer', async function() { + it('should be able to upload a binary data', async function() { const { server } = this.polly; const url = `${protocol}//example.com`; const body = Buffer.from('Node HTTP Adapter', 'base64'); @@ -85,9 +89,40 @@ function commonTests(transport) { expect(requests).to.have.lengthOf(2); expect(requests[0].id).to.equal(requests[1].id); + expect(requests[0].body.toString('base64')).to.equal( + body.toString('base64') + ); expect(requests[0].identifiers.body).to.equal(body.toString('hex')); }); + it('should be able to upload form data', async function() { + const url = `${protocol}//example.com/upload`; + const { server } = this.polly; + const formData = new FormData(); + let request; + + server.post(url).intercept((req, res) => { + request = req; + res.send(201); + }); + + formData.append( + 'upload', + fs.createReadStream(path.resolve(__dirname, '../../package.json')) + ); + const body = await getStream(formData); + + await nativeRequest(transport, url, { + body, + headers: formData.getHeaders(), + method: 'POST' + }); + + expect(request).to.exist; + expect(typeof request.body).to.equal('string'); + expect(request.body).to.include('@pollyjs/adapter-node-http'); + }); + it('should be able to download binary content', async function() { const url = `${protocol}//via.placeholder.com/150/92c952`; const { server } = this.polly; diff --git a/yarn.lock b/yarn.lock index 54e182bb..ddbf7ca1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6834,6 +6834,15 @@ forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" +form-data@^2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4" + integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + form-data@~2.3.2: version "2.3.3" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"