Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(adapter-fetch): Add support for handling binary data #332

Merged
merged 4 commits into from May 16, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 3 additions & 1 deletion packages/@pollyjs/adapter-fetch/package.json
Expand Up @@ -42,7 +42,9 @@
"dependencies": {
"@pollyjs/adapter": "^4.2.1",
"@pollyjs/utils": "^4.1.0",
"detect-node": "^2.0.4"
"buffer": "^5.6.0",
"detect-node": "^2.0.4",
"to-arraybuffer": "^1.0.1"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to-arraybuffer uses require('buffer').Buffer so I'm confused how this all is built in our current build pipeline - is it some how "browserifying" the node implementation and also bundling the npm "buffer" package?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok so looked more into it to see what was actually being built. We current have rollup-plugin-node-builtins setup which has support for using Buffer via buffer-es6.

If I imported the buffer via import { Buffer } from 'buffer' it only inlines the polyfill for the UMD build but when I used from 'buffer/', it inlined it for all 3 builds (CJS, ES, and UMD). Since this is only meant for the browser, it would make sense for it to be be polyfilled in all 3.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Explains a lot, was very confused - thanks for looking into it

},
"devDependencies": {
"@pollyjs/core": "^4.2.1",
Expand Down
24 changes: 18 additions & 6 deletions packages/@pollyjs/adapter-fetch/src/index.js
@@ -1,7 +1,10 @@
import Adapter from '@pollyjs/adapter';
import isNode from 'detect-node';
import { Buffer } from 'buffer/';
import bufferToArrayBuffer from 'to-arraybuffer';

import serializeHeaders from './utils/serializer-headers';
import isBufferUtf8Representable from './utils/is-buffer-utf8-representable';

const { defineProperty } = Object;
const IS_STUBBED = Symbol();
Expand Down Expand Up @@ -164,10 +167,14 @@ export default class FetchAdapter extends Adapter {
}
]);

const buffer = Buffer.from(await response.arrayBuffer());
const isBinaryBuffer = !isBufferUtf8Representable(buffer);

return {
statusCode: response.status,
headers: serializeHeaders(response.headers),
body: await response.text()
body: buffer.toString(isBinaryBuffer ? 'hex' : 'utf8'),
isBinary: isBinaryBuffer
};
}

Expand Down Expand Up @@ -199,11 +206,16 @@ export default class FetchAdapter extends Adapter {
}

const { absoluteUrl, response: pollyResponse } = pollyRequest;
const { statusCode } = pollyResponse;
const responseBody =
statusCode === 204 && pollyResponse.body === ''
? null
: pollyResponse.body;
const { statusCode, body, isBinary } = pollyResponse;

let responseBody = body;

if (statusCode === 204 && responseBody === '') {
responseBody = null;
} else if (isBinary) {
responseBody = bufferToArrayBuffer(Buffer.from(body, 'hex'));
}

const response = new Response(responseBody, {
status: statusCode,
headers: pollyResponse.headers
Expand Down
@@ -0,0 +1,12 @@
import { Buffer } from 'buffer/';

/**
* Determine if the given buffer is utf8.
* @param {Buffer} buffer
*/
export default function isBufferUtf8Representable(buffer) {
const utfEncodedBuffer = buffer.toString('utf8');
const reconstructedBuffer = Buffer.from(utfEncodedBuffer, 'utf8');

return reconstructedBuffer.equals(buffer);
}
34 changes: 34 additions & 0 deletions packages/@pollyjs/adapter-fetch/tests/integration/adapter-test.js
Expand Up @@ -5,6 +5,7 @@ import adapterTests from '@pollyjs-tests/integration/adapter-tests';
import adapterPollyTests from '@pollyjs-tests/integration/adapter-polly-tests';
import adapterBrowserTests from '@pollyjs-tests/integration/adapter-browser-tests';
import adapterIdentifierTests from '@pollyjs-tests/integration/adapter-identifier-tests';
import { Buffer } from 'buffer/';

import FetchAdapter from '../../src';
import pollyConfig from '../utils/polly-config';
Expand Down Expand Up @@ -103,6 +104,39 @@ describe('Integration | Fetch Adapter', function() {
expect(error.message).to.contain('The user aborted a request.');
});

it('should be able to download binary content', async function() {
this.timeout(10000);

const fetch = async () =>
Buffer.from(
await this.fetch('https://placekitten.com/32/32').then(res =>
offirgolan marked this conversation as resolved.
Show resolved Hide resolved
res.arrayBuffer()
)
);

this.polly.disconnectFrom(FetchAdapter);

const nativeResponseBuffer = await fetch();

this.polly.connectTo(FetchAdapter);

const recordedResponseBuffer = await fetch();

const { recordingName, config } = this.polly;

await this.polly.stop();
this.polly = new Polly(recordingName, config);
this.polly.replay();

const replayedResponseBuffer = await fetch();

expect(nativeResponseBuffer.equals(recordedResponseBuffer)).to.equal(true);
expect(recordedResponseBuffer.equals(replayedResponseBuffer)).to.equal(
true
);
expect(nativeResponseBuffer.equals(replayedResponseBuffer)).to.equal(true);
});

describe('Request', function() {
it('should support Request objects', async function() {
const { server } = this.polly;
Expand Down
10 changes: 9 additions & 1 deletion yarn.lock
Expand Up @@ -3914,6 +3914,14 @@ buffer@^4.3.0:
ieee754 "^1.1.4"
isarray "^1.0.0"

buffer@^5.6.0:
version "5.6.0"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.6.0.tgz#a31749dc7d81d84db08abf937b6b8c4033f62786"
integrity sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==
dependencies:
base64-js "^1.0.2"
ieee754 "^1.1.4"

builtin-modules@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.1.0.tgz#aad97c15131eb76b65b50ef208e7584cd76a7484"
Expand Down Expand Up @@ -13542,7 +13550,7 @@ to-array@0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/to-array/-/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890"

to-arraybuffer@^1.0.0:
to-arraybuffer@^1.0.0, to-arraybuffer@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43"

Expand Down