Skip to content

Commit

Permalink
feat: support setting connectTimeout in Electron (#770)
Browse files Browse the repository at this point in the history
Typically, Socket#setTimeout(0) will clear the timer set before. However, in some platforms (Electron 3.x~4.x), the timer will not be cleared. So we introduce a variable here.
  • Loading branch information
luin authored Jan 4, 2019
1 parent ec1e852 commit 2d591b7
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 39 deletions.
12 changes: 12 additions & 0 deletions lib/redis.js
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,18 @@ Redis.prototype.connect = function (callback) {
stream.on('data', eventHandler.dataHandler(_this));

if (options.connectTimeout) {
/*
* Typically, Socket#setTimeout(0) will clear the timer
* set before. However, in some platforms (Electron 3.x~4.x),
* the timer will not be cleared. So we introduce a variable here.
*
* See https://github.com/electron/electron/issues/14915
*/
var connectTimeoutCleared = false;
stream.setTimeout(options.connectTimeout, function () {
if (connectTimeoutCleared) {
return;
}
stream.setTimeout(0);
stream.destroy();

Expand All @@ -296,6 +307,7 @@ Redis.prototype.connect = function (callback) {
eventHandler.errorHandler(_this)(err);
});
stream.once(CONNECT_EVENT, function () {
connectTimeoutCleared = true;
stream.setTimeout(0);
});
}
Expand Down
115 changes: 76 additions & 39 deletions test/functional/connection.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
'use strict';

const {Socket} = require('net')

describe('connection', function () {
it('should emit "connect" when connected', function (done) {
var redis = new Redis();
Expand Down Expand Up @@ -45,51 +47,86 @@ describe('connection', function () {
});
});

it('should close the connection when timeout', function (done) {
var redis = new Redis(6379, '192.0.0.0', {
connectTimeout: 1,
retryStrategy: null
});
var pending = 2;
redis.on('error', function (err) {
expect(err.message).to.eql('connect ETIMEDOUT');
if (!--pending) {
done();
}
});
redis.get('foo', function (err) {
expect(err.message).to.match(/Connection is closed/);
if (!--pending) {
redis.disconnect();
done();
}
describe('connectTimeout', () => {
it('should close the connection when timeout', function (done) {
var redis = new Redis(6379, '192.0.0.0', {
connectTimeout: 1,
retryStrategy: null
});
var pending = 2;
redis.on('error', function (err) {
expect(err.message).to.eql('connect ETIMEDOUT');
if (!--pending) {
done();
}
});
redis.get('foo', function (err) {
expect(err.message).to.match(/Connection is closed/);
if (!--pending) {
redis.disconnect();
done();
}
});
});
});

it('should clear the timeout when connected', function (done) {
var redis = new Redis({ connectTimeout: 10000 });
setImmediate(function () {
stub(redis.stream, 'setTimeout', function (timeout) {
it('should clear the timeout when connected', function (done) {
const connectTimeout = 10000
var redis = new Redis({connectTimeout});
var set = false
stub(Socket.prototype, 'setTimeout', function (timeout) {
if (timeout === connectTimeout) {
set = true;
return;
}
expect(set).to.eql(true);
expect(timeout).to.eql(0);
redis.stream.setTimeout.restore();
Socket.prototype.setTimeout.restore();
redis.disconnect();
done();
});
});
});

it('should ignore timeout if connect', function (done) {
var redis = new Redis({
port: 6379,
connectTimeout: 500,
retryStrategy: null
});
let isReady = false
let timedoutCalled = false
stub(Socket.prototype, 'setTimeout', function (timeout, callback) {
if (timeout === 0) {
if (!isReady) {
isReady = true
} else {
timedoutCalled = true
}
return
}

setTimeout(() => {
callback()
expect(timedoutCalled).to.eql(false)
Socket.prototype.setTimeout.restore();
redis.disconnect();
done();
}, timeout)
});
});
})

describe('#connect', function () {
it('should return a promise', function (done) {
var pending = 2;
var redis = new Redis({ lazyConnect: true });
var redis = new Redis({lazyConnect: true});
redis.connect().then(function () {
redis.disconnect();
if (!--pending) {
done();
}
});

var redis2 = new Redis(6390, { lazyConnect: true, retryStrategy: null });
var redis2 = new Redis(6390, {lazyConnect: true, retryStrategy: null});
redis2.connect().catch(function () {
if (!--pending) {
redis2.disconnect();
Expand Down Expand Up @@ -125,7 +162,7 @@ describe('connection', function () {
});

it('should resolve when the status become ready', function (done) {
var redis = new Redis({ lazyConnect: true });
var redis = new Redis({lazyConnect: true});
redis.connect().then(function () {
expect(redis.status).to.eql('ready');
redis.disconnect()
Expand Down Expand Up @@ -230,7 +267,7 @@ describe('connection', function () {

describe('connectionName', function () {
it('should name the connection if options.connectionName is not null', function (done) {
var redis = new Redis({ connectionName: 'niceName' });
var redis = new Redis({connectionName: 'niceName'});
redis.once('ready', function () {
redis.client('getname', function (err, res) {
expect(res).to.eql('niceName');
Expand All @@ -242,7 +279,7 @@ describe('connection', function () {
});

it('should set the name before any subscribe command if reconnected', function (done) {
var redis = new Redis({ connectionName: 'niceName' });
var redis = new Redis({connectionName: 'niceName'});
redis.once('ready', function () {
redis.subscribe('l', function () {
redis.disconnect(true);
Expand All @@ -261,7 +298,7 @@ describe('connection', function () {
describe('readOnly', function () {
it('should send readonly command before other commands', function (done) {
var called = false;
var redis = new Redis({ port: 30001, readOnly: true, showFriendlyErrorStack: true });
var redis = new Redis({port: 30001, readOnly: true, showFriendlyErrorStack: true});
var node = new MockServer(30001, function (argv) {
if (argv[0] === 'readonly') {
called = true;
Expand All @@ -279,8 +316,8 @@ describe('connection', function () {

describe('autoResendUnfulfilledCommands', function () {
it('should resend unfulfilled commands to the correct db when reconnected', function (done) {
var redis = new Redis({ db: 3 });
var pub = new Redis({ db: 3 });
var redis = new Redis({db: 3});
var pub = new Redis({db: 3});
redis.once('ready', function () {
var pending = 2;
redis.blpop('l', 0, function (err, res) {
Expand Down Expand Up @@ -308,14 +345,14 @@ describe('connection', function () {
});

it('should resend previous subscribes before sending unfulfilled commands', function (done) {
var redis = new Redis({ db: 4 });
var pub = new Redis({ db: 4 });
var redis = new Redis({db: 4});
var pub = new Redis({db: 4});
redis.once('ready', function () {
pub.pubsub('channels', function(err, channelsBefore){
redis.subscribe('l', function() {
pub.pubsub('channels', function (err, channelsBefore) {
redis.subscribe('l', function () {
redis.disconnect(true);
redis.unsubscribe('l', function() {
pub.pubsub('channels', function(err, channels){
redis.unsubscribe('l', function () {
pub.pubsub('channels', function (err, channels) {
expect(channels.length).to.eql(channelsBefore.length);
redis.disconnect()
done();
Expand Down

0 comments on commit 2d591b7

Please sign in to comment.