Skip to content

Commit

Permalink
Fixed unit tests & removed dead code for web3-providers-http (#5228) (#…
Browse files Browse the repository at this point in the history
…5264)

* Fixed unit tests & removed dead code for web3-providers-http (#5228)

* Fixed unit tests & removed dead code for web3-providers-http

* Use chai-as-promised to resolve or reject promise

* Fixed bug that timeout option doesn't work properly

* change log update

Co-authored-by: Ayanami <[email protected]>
  • Loading branch information
jdevcs and Ayanami authored Jul 19, 2022
1 parent fc7bfcd commit 84ac9b7
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 89 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,7 @@ Released with 1.0.0-beta.37 code base.
- Add optional hex formatting parameter for getTransactionrReceipt (#5153)
- Fix transactionRoot -> transactionsRoot in BlockHeader (#5083)
- Fix Promise in Accounts.signTransaction() throwing errors that cannot be caught (#4724)
- Fixed unit tests & removed dead code for web3-providers-http (#5228)

### Security
- Updated `got` lib version and fixed other libs using npm audit fix (#5178) (#5254)
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -109,13 +109,15 @@
"buffer": "^4.9.2",
"bundlesize": "^0.18.0",
"chai": "^4.2.0",
"chai-as-promised": "^7.1.1",
"clean-webpack-plugin": "^3.0.0",
"core-js": "^3.6.5",
"crypto-js": "^3.3.0",
"decache": "^4.6.0",
"dependency-check": "^4.1.0",
"ethereumjs-util": "^7.1.0",
"ethers": "^5.4.4",
"fetch-mock": "^9.11.0",
"ganache-cli": "^6.12.0",
"jshint": "^2.12.0",
"karma": "^6.3.19",
Expand Down
57 changes: 23 additions & 34 deletions packages/web3-providers-http/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@
*/

var errors = require('web3-core-helpers').errors;
var fetch = require('cross-fetch');
var http = require('http');
var https = require('https');

// Apply missing polyfill for IE
require('cross-fetch/polyfill');
require('es6-promise').polyfill();
require('abortcontroller-polyfill/dist/polyfill-patch-fetch');

Expand Down Expand Up @@ -65,21 +65,22 @@ var HttpProvider = function HttpProvider(host, options) {
*/
HttpProvider.prototype.send = function (payload, callback) {
var options = {
method: 'POST',
body: JSON.stringify(payload)
method: 'POST',
body: JSON.stringify(payload)
};
var headers = {};
var controller;

if (typeof AbortController !== 'undefined') {
controller = new AbortController();
} else if (typeof AbortController === 'undefined' && typeof window !== 'undefined' && typeof window.AbortController !== 'undefined') {
} else if (typeof window !== 'undefined' && typeof window.AbortController !== 'undefined') {
// Some chrome version doesn't recognize new AbortController(); so we are using it from window instead
// https://stackoverflow.com/questions/55718778/why-abortcontroller-is-not-defined
controller = new window.AbortController();
} else {
// Disable user defined timeout
this.timeout = 0;
}

if (typeof controller !== 'undefined') {
options.signal = controller.signal;
}

// the current runtime is node
Expand All @@ -99,8 +100,8 @@ HttpProvider.prototype.send = function (payload, callback) {
}
}

if(this.headers) {
this.headers.forEach(function(header) {
if (this.headers) {
this.headers.forEach(function (header) {
headers[header.name] = header.value;
});
}
Expand All @@ -112,55 +113,43 @@ HttpProvider.prototype.send = function (payload, callback) {

// As the Fetch API supports the credentials as following options 'include', 'omit', 'same-origin'
// https://developer.mozilla.org/en-US/docs/Web/API/fetch#credentials
// To avoid breaking change in 1.x we override this value based on boolean option.
// To avoid breaking change in 1.x we override this value based on boolean option.
if(this.withCredentials) {
options.credentials = 'include';
} else {
options.credentials = 'omit';
}

options.headers = headers;

if (this.timeout > 0) {
this.timeoutId = setTimeout(function() {
if (this.timeout > 0 && typeof controller !== 'undefined') {
this.timeoutId = setTimeout(function () {
controller.abort();
}, this.timeout);
}

// Prevent global leak of connected
var _this = this;

var success = function(response) {
var success = function (response) {
if (this.timeoutId !== undefined) {
clearTimeout(this.timeoutId);
}
var result = response;
var error = null;

try {
// Response is a stream data so should be awaited for json response
result.json().then(function(data) {
result = data;
_this.connected = true;
callback(error, result);
});
} catch (e) {
_this.connected = false;
callback(errors.InvalidResponse(result));
}

// Response is a stream data so should be awaited for json response
response.json().then(function (data) {
callback(null, data);
}).catch(function (error) {
callback(errors.InvalidResponse(response));
});
};

var failed = function(error) {
var failed = function (error) {
if (this.timeoutId !== undefined) {
clearTimeout(this.timeoutId);
}

if (error.name === 'AbortError') {
_this.connected = false;
callback(errors.ConnectionTimeout(this.timeout));
}

_this.connected = false;
callback(errors.InvalidConnection(this.host));
}

Expand Down
163 changes: 108 additions & 55 deletions test/httpprovider.js
Original file line number Diff line number Diff line change
@@ -1,50 +1,52 @@
var chai = require('chai');
var chaiAsPromised = require('chai-as-promised');
chai.use(chaiAsPromised);
var assert = chai.assert;
var expect = chai.expect;
var http = require('http');
var https = require('https');
var SandboxedModule = require('sandboxed-module');
var Web3 = require('../packages/web3');
var HttpProvider = require('../packages/web3-providers-http');
var fetchMock = require('fetch-mock');

SandboxedModule.registerBuiltInSourceTransformer('istanbul');
function isObject(object) {
return object != null && typeof object === 'object';
}

class Response {
constructor(url, headers) {
this.url = url;
var header = new Map();
headers.map(function(h) {
header.set(`${h[0].toLowerCase()}`, `${h[1]}`);
});
this.headers = header;
this.ok = true;
this.status = 200;
this.statusText = 'OK';
}
};

function Mock(url, options) {
const response = new Response(url, Object.entries(options.headers));
return new Promise(function(resolve, reject) {
resolve(response);
});
};

var HttpProvider = SandboxedModule.require('../packages/web3-providers-http', {
requires: {
'cross-fetch': Mock,
},
singleOnly: true
});
function deepEqual(object1, object2) {
const keys1 = Object.keys(object1);
const keys2 = Object.keys(object2);
if (keys1.length !== keys2.length) {
return false;
}
for (const key of keys1) {
const val1 = object1[key];
const val2 = object2[key];
const areObjects = isObject(val1) && isObject(val2);
if (areObjects && !deepEqual(val1, val2) || !areObjects && val1 !== val2) {
return false;
}
}
return true;
}

describe('web3-providers-http', function () {
describe('prepareRequest', function () {
it('should set request header', async function () {
it('should set request header', function () {
var options = {headers: [{name: 'Access-Control-Allow-Origin', value: '*'}]}
var provider = new HttpProvider('http://localhost:8545', options);

var origin = 'Access-Control-Allow-Origin';
assert.equal(provider.headers, options.headers);
assert.equal(provider.httpAgent instanceof http.Agent, true);
});

it('should have https agent', function () {
var provider = new HttpProvider('https://localhost');

assert.equal(provider.httpsAgent instanceof https.Agent, true);
});

it('should use the passed custom http agent', async function () {
it('should use the passed custom http agent', function () {
var agent = new http.Agent();
var options = {agent: {http: agent}};
var provider = new HttpProvider('http://localhost:8545', options);
Expand All @@ -55,7 +57,7 @@ describe('web3-providers-http', function () {
assert.equal(provider.agent, options.agent);
});

it('should use the passed custom https agent', async function () {
it('should use the passed custom https agent', function () {
var agent = new https.Agent();
var options = {agent: {https: agent}};
var provider = new HttpProvider('http://localhost:8545', options);
Expand All @@ -65,34 +67,85 @@ describe('web3-providers-http', function () {
assert.equal(provider.httpsAgent, undefined);
assert.equal(provider.agent, options.agent);
});
});

/**
Deprecated with xhr2-cookies since this is non-standard
describe('send', function () {
it('should fail with invalid remote node connection', async function () {
var provider = new HttpProvider('http://localhost:8545');
var web3 = new Web3(provider);

it('should use the passed baseUrl', function () {
agent = new https.Agent();
options = {agent: {https: agent, baseUrl: 'base'}};
provider = new HttpProvider('http://localhost:8545', options);
result = provider._prepareRequest();
await expect(web3.eth.getChainId()).to.be.rejectedWith(Error, "CONNECTION ERROR: Couldn't connect to node http://localhost:8545.");
});

assert.equal(typeof result, 'object');
assert.equal(result.agents.httpsAgent, agent);
assert.equal(result.agents.baseUrl, 'base');
assert.equal(provider.httpAgent, undefined);
assert.equal(provider.httpsAgent, undefined);
assert.equal(provider.agent, options.agent);
it('should fail for non-json format response', async function () {
var provider = new HttpProvider('/fetchMock');
var web3 = new Web3(provider);

fetchMock.mock('/fetchMock', 'Testing non-json format response');

await expect(web3.eth.getChainId()).to.be.rejectedWith(Error, /Invalid JSON RPC response/);
fetchMock.restore();
});
**/
});

describe('send', function () {
it('should send basic async request', function (done) {
var provider = new HttpProvider();
it('should timeout by delayed response', async function () {
var provider = new HttpProvider('/fetchMock', { timeout: 500 });
var web3 = new Web3(provider);

fetchMock.mock('/fetchMock', 'Testing non-json format response', { delay: 1000 });

await expect(web3.eth.getChainId()).to.be.rejectedWith(Error, 'CONNECTION TIMEOUT: timeout of 500 ms achived');
fetchMock.restore();
});

it('should send basic async request', async function () {
var provider = new HttpProvider('/fetchMock');

var reqObject = {
'jsonrpc': '2.0',
'id': 0,
'method': 'eth_chainId',
'params': []
};

var resObject = {
'jsonrpc': '2.0',
'id': 0,
'result': '0x1'
};

fetchMock.mock((url, opts) => {
const reqCount = JSON.parse(opts.body).id;
reqObject = JSON.stringify((() => {
const obj = reqObject;
obj.id = reqCount;
return obj;
})());
resObject = (() => {
const obj = resObject;
obj.id = reqCount;
return obj;
})();
const matcher = {
url: '/fetchMock',
method: 'POST',
credentials: 'omit',
headers: {
'Content-Type': 'application/json'
},
body: reqObject
};
return url === matcher.url
&& opts.method === matcher.method
&& opts.credentials === matcher.credentials
&& deepEqual(opts.headers, matcher.headers)
&& opts.body === matcher.body;
}, resObject);

var web3 = new Web3(provider);

provider.send({}, function (err, result) {
assert.equal(typeof result, 'undefined');
done();
});
var chainId = await web3.eth.getChainId();
assert.equal(chainId, 1);
fetchMock.restore();
});
});
});

0 comments on commit 84ac9b7

Please sign in to comment.