Skip to content

Commit c7cd5da

Browse files
committed
Improve client reconnect behavior
A client configured to reconnect should do so on socket close unless unbind was called explicitly. This covers cases where the connection undergoes clean but unexpected termination.
1 parent 3edf9de commit c7cd5da

File tree

3 files changed

+81
-43
lines changed

3 files changed

+81
-43
lines changed

lib/client/client.js

+9-1
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,10 @@ Client.prototype.unbind = function unbind(callback) {
678678
if (typeof (callback) !== 'function')
679679
throw new TypeError('callback must be a function');
680680

681+
// When the socket closes, it is useful to know whether it was due to a
682+
// user-initiated unbind or something else.
683+
this.unbound = true;
684+
681685
if (!this.socket)
682686
return callback();
683687

@@ -1023,9 +1027,13 @@ Client.prototype._onClose = function _onClose(had_err) {
10231027
});
10241028
delete socket.ldap.parser;
10251029
delete socket.ldap;
1026-
if (had_err && this.reconnect) {
1030+
1031+
// Automatically fire reconnect logic if the socket was closed for any reason
1032+
// other than a user-initiated unbind.
1033+
if (this.reconnect && !this.unbound) {
10271034
this._connect();
10281035
}
1036+
this.unbound = false;
10291037
return false;
10301038
};
10311039

test/client.test.js

+71-42
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,14 @@ var SOCKET = '/tmp/.' + uuid();
1515

1616
var SUFFIX = 'dc=test';
1717

18+
var LOG = new Logger({
19+
name: 'ldapjs_unit_test',
20+
stream: process.stderr,
21+
level: (process.env.LOG_LEVEL || 'info'),
22+
serializers: Logger.stdSerializers,
23+
src: true
24+
});
25+
1826
var ldap;
1927
var Attribute;
2028
var Change;
@@ -148,13 +156,7 @@ test('setup', function (t) {
148156
client = ldap.createClient({
149157
connectTimeout: parseInt(process.env.LDAP_CONNECT_TIMEOUT || 0, 10),
150158
socketPath: SOCKET,
151-
log: new Logger({
152-
name: 'ldapjs_unit_test',
153-
stream: process.stderr,
154-
level: (process.env.LOG_LEVEL || 'info'),
155-
serializers: Logger.stdSerializers,
156-
src: true
157-
})
159+
log: LOG
158160
});
159161
t.ok(client);
160162
t.end();
@@ -622,13 +624,7 @@ test('setup action', function (t) {
622624
var setupClient = ldap.createClient({
623625
connectTimeout: parseInt(process.env.LDAP_CONNECT_TIMEOUT || 0, 10),
624626
socketPath: SOCKET,
625-
log: new Logger({
626-
name: 'ldapjs_unit_test',
627-
stream: process.stderr,
628-
level: (process.env.LOG_LEVEL || 'info'),
629-
serializers: Logger.stdSerializers,
630-
src: true
631-
})
627+
log: LOG
632628
});
633629
setupClient.on('setup', function (clt, cb) {
634630
clt.bind(BIND_DN, BIND_PW, function (err, res) {
@@ -652,13 +648,7 @@ test('setup reconnect', function (t) {
652648
connectTimeout: parseInt(process.env.LDAP_CONNECT_TIMEOUT || 0, 10),
653649
socketPath: SOCKET,
654650
reconnect: true,
655-
log: new Logger({
656-
name: 'ldapjs_unit_test',
657-
stream: process.stderr,
658-
level: (process.env.LOG_LEVEL || 'info'),
659-
serializers: Logger.stdSerializers,
660-
src: true
661-
})
651+
log: LOG
662652
});
663653
rClient.on('setup', function (clt, cb) {
664654
clt.bind(BIND_DN, BIND_PW, function (err, res) {
@@ -715,13 +705,7 @@ test('setup abort', function (t) {
715705
connectTimeout: parseInt(process.env.LDAP_CONNECT_TIMEOUT || 0, 10),
716706
socketPath: SOCKET,
717707
reconnect: true,
718-
log: new Logger({
719-
name: 'ldapjs_unit_test',
720-
stream: process.stderr,
721-
level: (process.env.LOG_LEVEL || 'info'),
722-
serializers: Logger.stdSerializers,
723-
src: true
724-
})
708+
log: LOG
725709
});
726710
var message = 'It\'s a trap!';
727711
setupClient.on('setup', function (clt, cb) {
@@ -743,13 +727,7 @@ test('abort reconnect', function (t) {
743727
connectTimeout: parseInt(process.env.LDAP_CONNECT_TIMEOUT || 0, 10),
744728
socketPath: '/dev/null',
745729
reconnect: true,
746-
log: new Logger({
747-
name: 'ldapjs_unit_test',
748-
stream: process.stderr,
749-
level: (process.env.LOG_LEVEL || 'info'),
750-
serializers: Logger.stdSerializers,
751-
src: true
752-
})
730+
log: LOG
753731
});
754732
var retryCount = 0;
755733
abortClient.on('connectError', function () {
@@ -777,13 +755,7 @@ test('reconnect max retries', function (t) {
777755
initialDelay: 10,
778756
maxDelay: 100
779757
},
780-
log: new Logger({
781-
name: 'ldapjs_unit_test',
782-
stream: process.stderr,
783-
level: (process.env.LOG_LEVEL || 'info'),
784-
serializers: Logger.stdSerializers,
785-
src: true
786-
})
758+
log: LOG
787759
});
788760
var count = 0;
789761
rClient.on('connectError', function () {
@@ -797,6 +769,63 @@ test('reconnect max retries', function (t) {
797769
});
798770

799771

772+
test('reconnect on server close', function (t) {
773+
var clt = ldap.createClient({
774+
socketPath: SOCKET,
775+
reconnect: true,
776+
log: LOG
777+
});
778+
clt.on('setup', function (sclt, cb) {
779+
sclt.bind(BIND_DN, BIND_PW, function (err, res) {
780+
t.ifError(err);
781+
cb(err);
782+
});
783+
});
784+
clt.once('connect', function () {
785+
t.ok(clt.socket);
786+
clt.once('connect', function () {
787+
t.ok(true, 'successful reconnect');
788+
clt.destroy();
789+
t.end();
790+
});
791+
792+
// Simulate server-side close
793+
clt.socket.destroy();
794+
});
795+
});
796+
797+
798+
test('no auto-reconnect on unbind', function (t) {
799+
var clt = ldap.createClient({
800+
socketPath: SOCKET,
801+
reconnect: true,
802+
log: LOG
803+
});
804+
clt.on('setup', function (sclt, cb) {
805+
sclt.bind(BIND_DN, BIND_PW, function (err, res) {
806+
t.ifError(err);
807+
cb(err);
808+
});
809+
});
810+
clt.once('connect', function () {
811+
clt.once('connect', function () {
812+
t.ifError(new Error('client should not reconnect'));
813+
});
814+
clt.once('close', function () {
815+
t.ok(true, 'initial close');
816+
setImmediate(function () {
817+
t.ok(!clt.connected, 'should not be connected');
818+
t.ok(!clt.connecting, 'should not be connecting');
819+
clt.destroy();
820+
t.end();
821+
});
822+
});
823+
824+
clt.unbind();
825+
});
826+
});
827+
828+
800829
test('abandon (GH-27)', function (t) {
801830
client.abandon(401876543, function (err) {
802831
t.ifError(err);

tools/jsl.node.conf

+1
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@
119119
+define process
120120
+define require
121121
+define setInterval
122+
+define setImmediate
122123
+define setTimeout
123124
+define Buffer
124125
+define JSON

0 commit comments

Comments
 (0)