Skip to content
This repository was archived by the owner on Mar 5, 2025. It is now read-only.

Commit 2b3fb3a

Browse files
authored
Nikos/5835/websocket provider keeps important error message back (#5884)
* Remove internal listener * Clear ws connection manually * Emit error for connectFailed instead of close * Add test * Add error reason and code, emit close, too * Add description in error * Remove connection listeners only on node * Update changelog * Check for client instead of connection for removing listener * Remove console
1 parent ef23642 commit 2b3fb3a

File tree

4 files changed

+68
-4
lines changed

4 files changed

+68
-4
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -655,5 +655,8 @@ Released with 1.0.0-beta.37 code base.
655655
- Add optional `hexFormat` param to `getTransaction` and `getBlock` that accepts the value `'hex'` (#5845)
656656
- `utils.toNumber` and `utils.hexToNumber` can now return the large unsafe numbers as `BigInt`, if `true` was passed to a new optional parameter called `bigIntOnOverflow` (#5845)
657657
- Updated @types/bn.js dependency to 5.1.1 in web3, web3-core and web3-eth-contract as reason mentioned in #5640 (#5885)
658+
- Add description to error for failed connection on websocket (#5884)
659+
660+
658661
### Security
659662
- Updated dependencies (#5885)

packages/web3-core-helpers/src/errors.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ module.exports = {
7373
if (event) {
7474
error.code = event.code;
7575
error.reason = event.reason;
76+
if(event.description) {
77+
error.description = event.description;
78+
}
7679
}
7780

7881
return error;

packages/web3-providers-ws/src/index.js

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ var WebsocketProvider = function WebsocketProvider(url, options) {
6363
this.responseQueue = new Map();
6464
this.reconnectAttempts = 0;
6565
this.reconnecting = false;
66-
66+
this.connectFailedDescription = null;
6767
// The w3cwebsocket implementation does not support Basic Auth
6868
// username/password in the URL. So generate the basic auth header, and
6969
// pass through with any additional headers supplied in constructor
@@ -160,6 +160,47 @@ WebsocketProvider.prototype._onConnect = function () {
160160
}
161161
};
162162

163+
WebsocketProvider.prototype._onConnectFailed = function (event) {
164+
this.connectFailedDescription = event.toString().split('\n')[0];
165+
var _this = this;
166+
if (this.connectFailedDescription) {
167+
event.description = this.connectFailedDescription;
168+
this.connectFailedDescription = null; // clean the message, so it won't be used in the next connection
169+
}
170+
171+
event.code = 1006;
172+
event.reason = 'connection failed';
173+
174+
if (this.reconnectOptions.auto && (![1000, 1001].includes(event.code) || event.wasClean === false)) {
175+
this.reconnect();
176+
177+
return;
178+
}
179+
180+
this.emit(this.ERROR, event);
181+
if (this.requestQueue.size > 0) {
182+
this.requestQueue.forEach(function (request, key) {
183+
request.callback(errors.ConnectionNotOpenError(event));
184+
_this.requestQueue.delete(key);
185+
});
186+
}
187+
188+
if (this.responseQueue.size > 0) {
189+
this.responseQueue.forEach(function (request, key) {
190+
request.callback(errors.InvalidConnection('on WS', event));
191+
_this.responseQueue.delete(key);
192+
});
193+
}
194+
195+
//clean connection on our own
196+
if(this.connection._connection){
197+
this.connection._connection.removeAllListeners();
198+
}
199+
this.connection._client.removeAllListeners();
200+
this.connection._readyState = 3; // set readyState to CLOSED
201+
202+
this.emit(this.CLOSE, event);
203+
}
163204
/**
164205
* Listener for the `close` event of the underlying WebSocket object
165206
*
@@ -176,8 +217,7 @@ WebsocketProvider.prototype._onClose = function (event) {
176217
return;
177218
}
178219

179-
this.emit(this.CLOSE, event);
180-
220+
this.emit(this.CLOSE, event);
181221
if (this.requestQueue.size > 0) {
182222
this.requestQueue.forEach(function (request, key) {
183223
request.callback(errors.ConnectionNotOpenError(event));
@@ -207,7 +247,11 @@ WebsocketProvider.prototype._addSocketListeners = function () {
207247
this.connection.addEventListener('message', this._onMessage.bind(this));
208248
this.connection.addEventListener('open', this._onConnect.bind(this));
209249
this.connection.addEventListener('close', this._onClose.bind(this));
210-
};
250+
if(this.connection._client){
251+
this.connection._client.removeAllListeners('connectFailed'); //Override the internal listeners, so they don't trigger a `close` event. We want to trigger `_onClose` manually with a description.
252+
this.connection._client.on('connectFailed',this._onConnectFailed.bind(this));
253+
}
254+
}
211255

212256
/**
213257
* Will remove all socket listeners
@@ -220,6 +264,8 @@ WebsocketProvider.prototype._removeSocketListeners = function () {
220264
this.connection.removeEventListener('message', this._onMessage);
221265
this.connection.removeEventListener('open', this._onConnect);
222266
this.connection.removeEventListener('close', this._onClose);
267+
if(this.connection._connection)
268+
this.connection._client.removeListener('connectFailed',this._onConnectFailed);
223269
};
224270

225271
/**

test/websocket.ganache.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,18 @@ describe('WebsocketProvider (ganache)', function () {
184184
web3.currentProvider.disconnect(1000);
185185
})
186186
})
187+
188+
it('"end" handler fires with close event object if Web3 disconnects', async function(){
189+
this.timeout(5000);
190+
server = ganache.server({ server: { ws: false, http: true } });
191+
await server.listen(port);
192+
193+
const web3 = new Web3(new Web3.providers.WebsocketProvider(host + port));
194+
195+
web3.currentProvider.on('error',(err)=>{
196+
assert(err.description.includes('Server responded with a non-101 status'));
197+
})
198+
})
187199

188200
// Here, the first error (try/caught) is fired by the request queue checker in
189201
// the onClose handler. The second error is fired by the readyState check in .send

0 commit comments

Comments
 (0)