diff --git a/lib/client.js b/lib/client.js index 1706675..a47da62 100644 --- a/lib/client.js +++ b/lib/client.js @@ -621,6 +621,15 @@ Client.prototype.search = function(base, options, controls, callback) { if (typeof(callback) !== 'function') throw new TypeError('callback (function) required'); + if (options.attributes) { + if (Array.isArray(options.attributes)) { + // noop + } else if (typeof(options.attributes) === 'string') { + options.attributes = [options.attributes]; + } else { + throw new TypeError('options.attributes must be an Array of Strings'); + } + } var req = new SearchRequest({ baseObject: typeof(base) === 'string' ? dn.parse(base) : base, scope: options.scope || 'base', @@ -633,6 +642,8 @@ Client.prototype.search = function(base, options, controls, callback) { controls: controls }); + + if (!this.connection) return callback(new ConnectionError('no connection')); @@ -698,18 +709,24 @@ Client.prototype._send = function(message, expect, callback, connection) { var self = this; - function closedConn() { - if (conn) - conn.destroy(); + function closeConn(err) { + var err = err || new ConnectionError('no connection'); + + // This is lame, but we want to send the original error back, whereas + // this will trigger a connection event + process.nextTick(function() { + if (conn) + conn.destroy(); + }); if (typeof(callback) === 'function') - return callback(new ConnectionError('no connection')); + return callback(err); - return callback.emit('error', new ConnectionError('no connection')); + return callback.emit('error', err); } if (!conn) - return closedConn(); + return closeConn(); // Now set up the callback in the messages table message.messageID = conn.ldap.nextMessageID; @@ -784,8 +801,7 @@ Client.prototype._send = function(message, expect, callback, connection) { try { return conn.write(message.toBer(), _writeCb); } catch (e) { - conn.end(); - return closedConn(); + return closeConn(e); } }; diff --git a/tst/laundry.test.js b/tst/laundry.test.js index d8aafc6..32d64ba 100644 --- a/tst/laundry.test.js +++ b/tst/laundry.test.js @@ -9,11 +9,36 @@ var ldap = require('../lib/index'); ///--- Globals var SOCKET = '/tmp/.' + uuid(); +var SUFFIX = 'dc=' + uuid(); var client; var server; + +///--- Helper + +function search(t, options, callback) { + client.search(SUFFIX, options, function(err, res) { + t.ifError(err); + t.ok(res); + var found = false; + res.on('searchEntry', function(entry) { + t.ok(entry); + found = true; + }); + res.on('end', function() { + t.ok(found); + if (callback) + return callback(); + + t.end(); + }); + }); +} + + + ///--- Tests test('setup', function(t) { @@ -24,32 +49,30 @@ test('setup', function(t) { socketPath: SOCKET }); t.ok(client); - // client.log4js.setLevel('Debug'); t.end(); }); + + server.search(SUFFIX, function(req, res, next) { + var entry = { + dn: 'cn=foo, ' + SUFFIX, + attributes: { + objectclass: ['person', 'top'], + cn: 'Pogo Stick', + sn: 'Stick', + givenname: 'ogo', + mail: uuid() + '@pogostick.org' + } + }; + + if (req.filter.matches(entry.attributes)) + res.send(entry); + + res.end(); + }); }); test('Evolution search filter (GH-3)', function(t) { - var suffix = 'dc=' + uuid(); - var entry = { - dn: 'cn=foo, ' + suffix, - attributes: { - objectclass: ['person', 'top'], - cn: 'Pogo Stick', - sn: 'Stick', - givenname: 'ogo', - mail: uuid() + '@pogostick.org' - } - }; - - server.search(suffix, function(req, res, next) { - console.log(req.filter.filters[0].type); - if (req.filter.matches(entry.attributes)) - res.send(entry); - res.end(); - }); - // This is what Evolution sends, when searching for a contact 'ogo'. Wow. var filter = '(|(cn=ogo*)(givenname=ogo*)(sn=ogo*)(mail=ogo*)(member=ogo*)' + @@ -69,19 +92,17 @@ test('Evolution search filter (GH-3)', function(t) { '(anniversary=ogo*)(birthdate=ogo*)(mailer=ogo*)(fileas=ogo*)' + '(category=ogo*)(calcaluri=ogo*)(calfburl=ogo*)(icscalendar=ogo*))'; - client.search(suffix, filter, function(err, res) { - t.ifError(err); - t.ok(res); - var found = false; - res.on('searchEntry', function(entry) { - t.ok(entry); - found = true; - }); - res.on('end', function() { - t.ok(found); - t.end(); - }); - }); + return search(t, filter); +}); + + +test('GH-49 Client errors on bad attributes', function(t) { + var searchOpts = { + filter: 'cn=*ogo*', + scope: 'one', + attributes: 'dn' + }; + return search(t, searchOpts); });