Skip to content

Commit

Permalink
Fixed bug #4727 : toFormData Blob issue on node>v17; (#4728)
Browse files Browse the repository at this point in the history
* Fixed bug #4727;
Added node 18.x to the CI;
Added hotfix for `ERR_OSSL_EVP_UNSUPPORTED` issue with karma running on node >=17.x;
Added `cross-env` to allow running build and test scripts on Windows platforms;

* Added conditional setting of `--openssl-legacy-provider` option for node versions >=17.x;

* Refactored ssl-hotfix & test script;

* Fixed and refactored default max body length test due to ECONNRESET failure;

* Added test for converting the data uri to a Blob;
Fixed bug with parsing mime type for Blob;

Co-authored-by: Jay <jasonsaayman@gmail.com>
  • Loading branch information
DigitalBrainJS and jasonsaayman committed May 20, 2022
1 parent de973f0 commit 467025b
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 35 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Expand Up @@ -12,7 +12,7 @@ jobs:

strategy:
matrix:
node-version: [12.x, 14.x, 16.x]
node-version: [12.x, 14.x, 16.x, 18.x]

steps:
- uses: actions/checkout@v2
Expand Down
22 changes: 22 additions & 0 deletions bin/ssl_hotfix.js
@@ -0,0 +1,22 @@
const {spawn} = require('child_process');

const args = process.argv.slice(2);

console.log(`Running ${args.join(' ')} on ${process.version}\n`);

const match = /v(\d+)/.exec(process.version);

const isHotfixNeeded = match && match[1] > 16;

isHotfixNeeded && console.warn('Setting --openssl-legacy-provider as ssl hotfix');

const test = spawn('cross-env',
isHotfixNeeded ? ['NODE_OPTIONS=--openssl-legacy-provider', ...args] : args, {
shell: true,
stdio: 'inherit'
}
);

test.on('exit', function (code) {
process.exit(code)
})
2 changes: 1 addition & 1 deletion lib/helpers/fromDataURI.js
Expand Up @@ -23,7 +23,7 @@ module.exports = function fromDataURI(uri, asBlob, options) {
}

if (protocol === 'data') {
uri = uri.slice(protocol.length);
uri = protocol.length ? uri.slice(protocol.length + 1) : uri;

var match = DATA_URL_PATTERN.exec(uri);

Expand Down
39 changes: 25 additions & 14 deletions lib/helpers/toFormData.js
@@ -1,6 +1,7 @@
'use strict';

var utils = require('../utils');
var AxiosError = require('../core/AxiosError');
var envFormData = require('../env/classes/FormData');

function isVisitable(thing) {
Expand All @@ -20,20 +21,6 @@ function renderKey(path, key, dots) {
}).join(dots ? '.' : '');
}

function convertValue(value) {
if (value === null) return '';

if (utils.isDate(value)) {
return value.toISOString();
}

if (utils.isArrayBuffer(value) || utils.isTypedArray(value)) {
return typeof Blob === 'function' ? new Blob([value]) : Buffer.from(value);
}

return value;
}

function isFlatArray(arr) {
return utils.isArray(arr) && !arr.some(isVisitable);
}
Expand All @@ -42,6 +29,10 @@ var predicates = utils.toFlatObject(utils, {}, null, function filter(prop) {
return /^is[A-Z]/.test(prop);
});

function isSpecCompliant(thing) {
return thing && utils.isFunction(thing.append) && thing[Symbol.toStringTag] === 'FormData' && thing[Symbol.iterator];
}

/**
* Convert a data object to FormData
* @param {Object} obj
Expand Down Expand Up @@ -73,11 +64,31 @@ function toFormData(obj, formData, options) {
var visitor = options.visitor || defaultVisitor;
var dots = options.dots;
var indexes = options.indexes;
var _Blob = options.Blob || typeof Blob !== 'undefined' && Blob;
var useBlob = _Blob && isSpecCompliant(formData);

if (!utils.isFunction(visitor)) {
throw new TypeError('visitor must be a function');
}

function convertValue(value) {
if (value === null) return '';

if (utils.isDate(value)) {
return value.toISOString();
}

if (!useBlob && utils.isBlob(value)) {
throw new AxiosError('Blob is not supported. Use a Buffer instead.');
}

if (utils.isArrayBuffer(value) || utils.isTypedArray(value)) {
return useBlob && typeof Blob === 'function' ? new Blob([value]) : Buffer.from(value);
}

return value;
}

/**
*
* @param {*} value
Expand Down
28 changes: 28 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Expand Up @@ -5,10 +5,10 @@
"main": "index.js",
"types": "index.d.ts",
"scripts": {
"test": "grunt test && dtslint",
"test": "node bin/ssl_hotfix.js grunt test && node bin/ssl_hotfix.js dtslint",
"start": "node ./sandbox/server.js",
"preversion": "grunt version && npm test",
"build": "NODE_ENV=production grunt build",
"build": "cross-env NODE_ENV=production grunt build",
"examples": "node ./examples/server.js",
"coveralls": "cat coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js",
"fix": "eslint --fix lib/**/*.js"
Expand Down Expand Up @@ -39,6 +39,7 @@
"abortcontroller-polyfill": "^1.7.3",
"body-parser": "^1.20.0",
"coveralls": "^3.1.1",
"cross-env": "^7.0.3",
"dtslint": "^4.2.1",
"es6-promise": "^4.2.8",
"express": "^4.18.1",
Expand Down
49 changes: 33 additions & 16 deletions test/unit/adapters/http.js
Expand Up @@ -15,6 +15,9 @@ var formidable = require('formidable');
var express = require('express');
var multer = require('multer');
var bodyParser = require('body-parser');
const isBlobSupported = typeof Blob !== 'undefined';

var noop = ()=> {};

describe('supports http with nodejs', function () {

Expand Down Expand Up @@ -572,27 +575,23 @@ describe('supports http with nodejs', function () {
var data = Array(2 * followRedirectsMaxBodyDefaults).join('ж');

server = http.createServer(function (req, res) {
res.setHeader('Content-Type', 'text/html; charset=UTF-8');
res.end();
}).listen(4444, function () {
var success = false, failure = false, error;
// consume the req stream
req.on('data', noop);
// and wait for the end before responding, otherwise an ECONNRESET error will be thrown
req.on('end', ()=> {
res.end('OK');
});
}).listen(4444, function (err) {
if (err) {
return done(err);
}
// send using the default -1 (unlimited axios maxBodyLength)
axios.post('http://localhost:4444/', {
data: data
}).then(function (res) {
success = true;
}).catch(function (err) {
error = err;
failure = true;
});


setTimeout(function () {
assert.equal(success, true, 'request should have succeeded');
assert.equal(failure, false, 'request should not fail');
assert.equal(error, undefined, 'There should not be any error');
assert.equal(res.data, 'OK', 'should handle response');
done();
}, 100);
}).catch(done);
});
});

Expand Down Expand Up @@ -1485,6 +1484,24 @@ describe('supports http with nodejs', function () {
}).catch(done);
});

it('should support requesting data URL as a Blob (if supported by the environment)', function (done) {

if (!isBlobSupported) {
this.skip();
return;
}

const buffer = Buffer.from('123');

const dataURI = 'data:application/octet-stream;base64,' + buffer.toString('base64');

axios.get(dataURI, {responseType: 'blob'}).then(async ({data})=> {
assert.strictEqual(data.type, 'application/octet-stream');
assert.deepStrictEqual(await data.text(), '123');
done();
}).catch(done);
});

it('should support requesting data URL as a String (text)', function (done) {
const buffer = Buffer.from('123', 'utf-8');

Expand Down
2 changes: 1 addition & 1 deletion test/unit/helpers/fromDataURI.js
Expand Up @@ -7,6 +7,6 @@ describe('helpers::fromDataURI', function () {

const dataURI = 'data:application/octet-stream;base64,' + buffer.toString('base64');

assert.deepStrictEqual(fromDataURI(dataURI), buffer);
assert.deepStrictEqual(fromDataURI(dataURI, false), buffer);
});
});

0 comments on commit 467025b

Please sign in to comment.