Client refactor, with pooled client (minor lint cleanup as well)
This commit is contained in:
parent
5be9dedbf6
commit
59ea20ffa2
|
@ -0,0 +1,60 @@
|
||||||
|
var Logger = require('bunyan');
|
||||||
|
|
||||||
|
var ldap = require('../lib/index');
|
||||||
|
|
||||||
|
|
||||||
|
///
|
||||||
|
// Run the "inmemory.js" server in the same directory
|
||||||
|
///
|
||||||
|
|
||||||
|
function ifError(err) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err.stack);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var LOG = new Logger({
|
||||||
|
name: 'ldapjs',
|
||||||
|
stream: process.stderr,
|
||||||
|
level: (process.env.LOG_LEVEL || 'info'),
|
||||||
|
serializers: Logger.stdSerializers
|
||||||
|
});
|
||||||
|
var MAX_CONNS = process.env.LDAP_MAX_CONNS || 10;
|
||||||
|
|
||||||
|
var client = ldap.createClient({
|
||||||
|
url: 'ldap://localhost:1389',
|
||||||
|
maxConnections: MAX_CONNS,
|
||||||
|
log: LOG
|
||||||
|
});
|
||||||
|
|
||||||
|
client.bind('cn=root', 'secret', function (err) {
|
||||||
|
ifError(err);
|
||||||
|
|
||||||
|
client.add('o=smartdc', {o: 'smartdc'}, function (err) {
|
||||||
|
ifError(err);
|
||||||
|
|
||||||
|
var finished = 0;
|
||||||
|
for (var i = 0; i < MAX_CONNS; i++) {
|
||||||
|
client.search('o=smartdc', function (err, res) {
|
||||||
|
ifError(err);
|
||||||
|
res.on('end', function () {
|
||||||
|
if (++finished === (MAX_CONNS - 1)) {
|
||||||
|
console.error('Go kill the LDAP server and restart it')
|
||||||
|
setTimeout(function () {
|
||||||
|
console.log('readding suffix');
|
||||||
|
client.add('o=smartdc', {o: 'smartdc'}, function (err) {
|
||||||
|
ifError(err);
|
||||||
|
client.unbind(function () {
|
||||||
|
console.log('All done');
|
||||||
|
process.exit(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, 15000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
|
@ -6,15 +6,15 @@ var net = require('net');
|
||||||
var tls = require('tls');
|
var tls = require('tls');
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
|
|
||||||
var Attribute = require('./attribute');
|
var Attribute = require('../attribute');
|
||||||
var Change = require('./change');
|
var Change = require('../change');
|
||||||
var Control = require('./controls/index').Control;
|
var Control = require('../controls/index').Control;
|
||||||
var Protocol = require('./protocol');
|
var Protocol = require('../protocol');
|
||||||
var dn = require('./dn');
|
var dn = require('../dn');
|
||||||
var errors = require('./errors');
|
var errors = require('../errors');
|
||||||
var filters = require('./filters');
|
var filters = require('../filters');
|
||||||
var messages = require('./messages');
|
var messages = require('../messages');
|
||||||
var url = require('./url');
|
var url = require('../url');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -38,29 +38,27 @@ var SearchReference = messages.SearchReference;
|
||||||
var SearchResponse = messages.SearchResponse;
|
var SearchResponse = messages.SearchResponse;
|
||||||
var Parser = messages.Parser;
|
var Parser = messages.Parser;
|
||||||
|
|
||||||
|
|
||||||
var Filter = filters.Filter;
|
var Filter = filters.Filter;
|
||||||
var PresenceFilter = filters.PresenceFilter;
|
var PresenceFilter = filters.PresenceFilter;
|
||||||
|
|
||||||
|
var ConnectionError = errors.ConnectionError;
|
||||||
|
|
||||||
var CMP_EXPECT = [errors.LDAP_COMPARE_TRUE, errors.LDAP_COMPARE_FALSE];
|
var CMP_EXPECT = [errors.LDAP_COMPARE_TRUE, errors.LDAP_COMPARE_FALSE];
|
||||||
var MAX_MSGID = Math.pow(2, 31) - 1;
|
var MAX_MSGID = Math.pow(2, 31) - 1;
|
||||||
|
|
||||||
|
// node 0.6 got rid of FDs, so make up a client id for logging
|
||||||
|
var CLIENT_ID = 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
///--- Internal Helpers
|
///--- Internal Helpers
|
||||||
|
|
||||||
function xor() {
|
function nextClientId() {
|
||||||
var b = false;
|
if (++CLIENT_ID === MAX_MSGID)
|
||||||
for (var i = 0; i < arguments.length; i++) {
|
return 1;
|
||||||
if (arguments[i] && !b) {
|
|
||||||
b = true;
|
|
||||||
} else if (arguments[i] && b) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
return CLIENT_ID;
|
||||||
|
}
|
||||||
|
|
||||||
function validateControls(controls) {
|
function validateControls(controls) {
|
||||||
if (Array.isArray(controls)) {
|
if (Array.isArray(controls)) {
|
||||||
|
@ -78,15 +76,120 @@ function validateControls(controls) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function ConnectionError(message) {
|
function setupSocket(socket, opts) {
|
||||||
errors.LDAPError.call(this,
|
var log = opts.log;
|
||||||
'ConnectionError',
|
|
||||||
0x80, // LDAP_OTHER,
|
socket.ldap = {
|
||||||
message,
|
id: opts.url ? opts.url.href : opts.socketPath,
|
||||||
null,
|
messageID: 0,
|
||||||
ConnectionError);
|
messages: {},
|
||||||
|
getNextMessageID: function getNextMessageID() {
|
||||||
|
if (++socket.ldap.messageID >= MAX_MSGID)
|
||||||
|
socket.ldap.messageID = 1;
|
||||||
|
|
||||||
|
return socket.ldap.messageID;
|
||||||
|
},
|
||||||
|
parser: new Parser({
|
||||||
|
log: log
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
// This won't be set on TLS. So. Very. Annoying.
|
||||||
|
if (typeof (socket.setKeepAlive) !== 'function') {
|
||||||
|
socket.setKeepAlive = function setKeepAlive(enable, delay) {
|
||||||
|
return socket.socket ? socket.socket.setKeepAlive(enable, delay) : false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// On close we have to walk the outstanding messages and go invoke their
|
||||||
|
// callback with an error
|
||||||
|
socket.on('close', function onClose(had_err) {
|
||||||
|
if (log.trace())
|
||||||
|
log.trace('close event had_err=%s', had_err ? 'yes' : 'no');
|
||||||
|
|
||||||
|
opts.emit('close', had_err);
|
||||||
|
Object.keys(socket.ldap.messages).forEach(function (msgid) {
|
||||||
|
var err;
|
||||||
|
if (socket.unbindMessageID !== parseInt(msgid, 10)) {
|
||||||
|
err = new ConnectionError(socket.ldap.id + ' closed');
|
||||||
|
} else {
|
||||||
|
err = new UnbindResponse({
|
||||||
|
messageID: msgid
|
||||||
|
});
|
||||||
|
err.status = 'unbind';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof (socket.ldap.messages[msgid]) === 'function') {
|
||||||
|
var callback = socket.ldap.messages[msgid];
|
||||||
|
delete socket.ldap.messages[msgid];
|
||||||
|
return callback(err);
|
||||||
|
} else if (socket.ldap.messages[msgid]) {
|
||||||
|
if (err instanceof Error)
|
||||||
|
socket.ldap.messages[msgid].emit('error', err);
|
||||||
|
delete socket.ldap.messages[msgid];
|
||||||
|
}
|
||||||
|
|
||||||
|
delete socket.ldap.parser;
|
||||||
|
delete socket.ldap;
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('data', function onData(data) {
|
||||||
|
if (log.trace())
|
||||||
|
log.trace('data event: %s', util.inspect(data));
|
||||||
|
|
||||||
|
socket.ldap.parser.write(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('end', function onEnd() {
|
||||||
|
if (log.trace())
|
||||||
|
log.trace('end event');
|
||||||
|
|
||||||
|
opts.emit('end');
|
||||||
|
socket.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('error', function onError(err) {
|
||||||
|
if (log.trace())
|
||||||
|
log.trace({err: err}, 'error event: %s', new Error().stack);
|
||||||
|
|
||||||
|
if (opts.listeners('error').length)
|
||||||
|
opts.emit('error', err);
|
||||||
|
|
||||||
|
socket.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('timeout', function onTimeout() {
|
||||||
|
if (log.trace())
|
||||||
|
log.trace('timeout event');
|
||||||
|
|
||||||
|
opts.emit('socketTimeout');
|
||||||
|
socket.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
// The "router"
|
||||||
|
socket.ldap.parser.on('message', function onMessage(message) {
|
||||||
|
message.connection = socket;
|
||||||
|
var callback = socket.ldap.messages[message.messageID];
|
||||||
|
|
||||||
|
if (!callback) {
|
||||||
|
log.error({message: message.json}, 'unsolicited message');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return callback(message);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.ldap.parser.on('error', function onParseError(err) {
|
||||||
|
log.debug({err: err}, 'parser error event');
|
||||||
|
|
||||||
|
if (opts.listeners('error').length)
|
||||||
|
opts.emit('error', err);
|
||||||
|
|
||||||
|
socket.end();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
util.inherits(ConnectionError, errors.LDAPError);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -105,38 +208,24 @@ util.inherits(ConnectionError, errors.LDAPError);
|
||||||
* @throws {TypeError} on bad input.
|
* @throws {TypeError} on bad input.
|
||||||
*/
|
*/
|
||||||
function Client(options) {
|
function Client(options) {
|
||||||
if (!options || typeof (options) !== 'object')
|
assert.ok(options);
|
||||||
throw new TypeError('options (object) required');
|
|
||||||
if (options.url && typeof (options.url) !== 'string')
|
|
||||||
throw new TypeError('options.url (string) required');
|
|
||||||
if (options.socketPath && typeof (options.socketPath) !== 'string')
|
|
||||||
throw new TypeError('options.socketPath must be a string');
|
|
||||||
if (typeof (options.log) !== 'object')
|
|
||||||
throw new TypeError('options.log must be an object');
|
|
||||||
|
|
||||||
if (!xor(options.url, options.socketPath))
|
|
||||||
throw new TypeError('options.url ^ options.socketPath (String) required');
|
|
||||||
|
|
||||||
EventEmitter.call(this, options);
|
EventEmitter.call(this, options);
|
||||||
|
|
||||||
var parsedUrl;
|
var _url;
|
||||||
if (options.url)
|
if (options.url)
|
||||||
parsedUrl = url.parse(options.url);
|
_url = url.parse(options.url);
|
||||||
|
|
||||||
this.connection = null;
|
this.connectTimeout = parseInt((options.connectTimeout || 0), 10);
|
||||||
this.connectTimeout = options.connectTimeout || false;
|
this.host = _url ? _url.hostname : undefined;
|
||||||
this.connectOptions = {
|
this.log = options.log.child({clazz: 'Client'}, true);
|
||||||
port: parsedUrl ? parsedUrl.port : options.socketPath,
|
this.port = _url ? _url.port : false;
|
||||||
host: parsedUrl ? parsedUrl.hostname : undefined,
|
this.secure = _url ? _url.secure : false;
|
||||||
socketPath: options.socketPath || undefined
|
this.socketPath = options.socketPath || false;
|
||||||
};
|
this.timeout = parseInt((options.timeout || 0), 10);
|
||||||
this.log = options.log;
|
this.url = _url;
|
||||||
this.secure = parsedUrl ? parsedUrl.secure : false;
|
|
||||||
this.timeout = options.timeout || false;
|
|
||||||
this.url = parsedUrl || false;
|
|
||||||
|
|
||||||
// We'll emit a connect event when this is done
|
this.socket = this._connect();
|
||||||
this.connect();
|
|
||||||
}
|
}
|
||||||
util.inherits(Client, EventEmitter);
|
util.inherits(Client, EventEmitter);
|
||||||
module.exports = Client;
|
module.exports = Client;
|
||||||
|
@ -605,7 +694,7 @@ Client.prototype.unbind = function unbind(callback) {
|
||||||
if (typeof (callback) !== 'function')
|
if (typeof (callback) !== 'function')
|
||||||
throw new TypeError('callback must be a function');
|
throw new TypeError('callback must be a function');
|
||||||
|
|
||||||
if (!this.connection)
|
if (!this.socket)
|
||||||
return callback();
|
return callback();
|
||||||
|
|
||||||
var req = new UnbindRequest();
|
var req = new UnbindRequest();
|
||||||
|
@ -613,155 +702,47 @@ Client.prototype.unbind = function unbind(callback) {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Connects this client, either at construct time, or after an unbind has
|
///--- Private API
|
||||||
* been called. Under normal circumstances you don't need to call this method.
|
|
||||||
*
|
Client.prototype._connect = function _connect() {
|
||||||
* @param {Function} (optional) callback invoked when `connect` is emitted.
|
|
||||||
*/
|
|
||||||
Client.prototype.connect = function connect(callback) {
|
|
||||||
var c = null;
|
|
||||||
var log = this.log;
|
var log = this.log;
|
||||||
var opts = this.connectOptions;
|
|
||||||
var proto = this.secure ? tls : net;
|
var proto = this.secure ? tls : net;
|
||||||
var self = this;
|
var self = this;
|
||||||
|
var socket = null;
|
||||||
var timer = false;
|
var timer = false;
|
||||||
|
|
||||||
c = proto.connect(opts.port, opts.host, function () {
|
function onConnect() {
|
||||||
if (timer)
|
if (timer)
|
||||||
clearTimeout(timer);
|
clearTimeout(timer);
|
||||||
|
|
||||||
assert.ok(c.ldap);
|
assert.ok(socket.ldap);
|
||||||
|
|
||||||
c.ldap.id += c.fd ? (':' + c.fd) : '';
|
socket.ldap.id = nextClientId() + '__' + socket.ldap.id;
|
||||||
|
self.log = self.log.child({ldap_id: socket.ldap.id}, true);
|
||||||
|
|
||||||
if (log.trace())
|
if (log.trace())
|
||||||
log.trace('%s connect event', c.ldap.id);
|
log.trace('connect event');
|
||||||
|
|
||||||
self.connection = c;
|
self.socket = socket;
|
||||||
self.emit('connect', c);
|
self.emit('connect', socket);
|
||||||
|
}
|
||||||
|
|
||||||
return (typeof (callback) === 'function' ? callback(null, c) : false);
|
socket = proto.createConnection((this.port || this.socketPath),
|
||||||
});
|
(this.host ? this.host : onConnect),
|
||||||
|
(this.host ? onConnect : undefined));
|
||||||
|
|
||||||
|
setupSocket(socket, this);
|
||||||
|
|
||||||
if (this.connectTimeout) {
|
if (this.connectTimeout) {
|
||||||
timer = setTimeout(function () {
|
timer = setTimeout(function onConnectTimeout() {
|
||||||
c.destroy();
|
socket.destroy();
|
||||||
|
|
||||||
self.emit('connectTimeout', new ConnectionError('timeout'));
|
self.emit('connectTimeout');
|
||||||
}, this.connectTimeout);
|
}, this.connectTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof (c.setKeepAlive) !== 'function') {
|
return socket;
|
||||||
c.setKeepAlive = function setKeepAlive(enable, delay) {
|
|
||||||
return c.socket ? c.socket.setKeepAlive(enable, delay) : false;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
c.ldap = {
|
|
||||||
id: self.url ? self.url.href : opts.socketPath,
|
|
||||||
messageID: 0,
|
|
||||||
messages: {},
|
|
||||||
get nextMessageID() {
|
|
||||||
if (++c.ldap.messageID >= MAX_MSGID)
|
|
||||||
c.ldap.messageID = 1;
|
|
||||||
|
|
||||||
return c.ldap.messageID;
|
|
||||||
},
|
|
||||||
parser: new Parser({
|
|
||||||
log: self.log
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
c.on('end', function () {
|
|
||||||
if (log.trace())
|
|
||||||
log.trace('%s end event', c.ldap.id);
|
|
||||||
|
|
||||||
c.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
// On close we have to walk the outstanding messages and go invoke their
|
|
||||||
// callback with an error
|
|
||||||
c.on('close', function (had_err) {
|
|
||||||
if (log.trace())
|
|
||||||
log.trace('%s close event had_err=%s', c.ldap.id, had_err ? 'yes' : 'no');
|
|
||||||
|
|
||||||
Object.keys(c.ldap.messages).forEach(function (msgid) {
|
|
||||||
var err;
|
|
||||||
if (c.unbindMessageID !== parseInt(msgid, 10)) {
|
|
||||||
err = new ConnectionError(c.ldap.id + ' closed');
|
|
||||||
} else {
|
|
||||||
err = new UnbindResponse({
|
|
||||||
messageID: msgid
|
|
||||||
});
|
|
||||||
err.status = 'unbind';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof (c.ldap.messages[msgid]) === 'function') {
|
|
||||||
var callback = c.ldap.messages[msgid];
|
|
||||||
delete c.ldap.messages[msgid];
|
|
||||||
return callback(err);
|
|
||||||
} else if (c.ldap.messages[msgid]) {
|
|
||||||
if (err instanceof Error)
|
|
||||||
c.ldap.messages[msgid].emit('error', err);
|
|
||||||
delete c.ldap.messages[msgid];
|
|
||||||
}
|
|
||||||
|
|
||||||
delete c.ldap.parser;
|
|
||||||
delete c.ldap;
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
c.on('error', function (err) {
|
|
||||||
if (log.trace())
|
|
||||||
log.trace({err: err}, '%s error event', c.ldap.id);
|
|
||||||
|
|
||||||
if (self.listeners('error').length)
|
|
||||||
self.emit('error', err);
|
|
||||||
|
|
||||||
c.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
c.on('timeout', function () {
|
|
||||||
if (log.trace())
|
|
||||||
log.trace('%s timeout event=%s', c.ldap.id);
|
|
||||||
|
|
||||||
self.emit('timeout');
|
|
||||||
c.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
c.on('data', function (data) {
|
|
||||||
if (log.trace())
|
|
||||||
log.trace('%s data event: %s', c.ldap.id, util.inspect(data));
|
|
||||||
|
|
||||||
c.ldap.parser.write(data);
|
|
||||||
});
|
|
||||||
|
|
||||||
// The "router"
|
|
||||||
c.ldap.parser.on('message', function (message) {
|
|
||||||
message.connection = c;
|
|
||||||
var callback = c.ldap.messages[message.messageID];
|
|
||||||
|
|
||||||
if (!callback) {
|
|
||||||
log.error({message: message.json}, '%s: unsolicited message', c.ldap.id);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return callback(message);
|
|
||||||
});
|
|
||||||
|
|
||||||
c.ldap.parser.on('error', function (err) {
|
|
||||||
log.debug({err: err}, '%s parser error event', c.ldap.id, err);
|
|
||||||
|
|
||||||
if (self.listeners('error').length)
|
|
||||||
self.emit('error', err);
|
|
||||||
|
|
||||||
c.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
return c;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -771,98 +752,95 @@ Client.prototype._send = function _send(message, expect, emitter, callback) {
|
||||||
assert.ok(typeof (emitter) !== undefined);
|
assert.ok(typeof (emitter) !== undefined);
|
||||||
assert.ok(callback);
|
assert.ok(callback);
|
||||||
|
|
||||||
var conn = this.connection;
|
var conn = this.socket;
|
||||||
|
var log = this.log;
|
||||||
var self = this;
|
var self = this;
|
||||||
var timer = false;
|
var timer = false;
|
||||||
|
|
||||||
if (!conn)
|
if (!conn)
|
||||||
return callback(new ConnectionError('no socket'));
|
return callback(new ConnectionError('no socket'));
|
||||||
|
|
||||||
message.messageID = conn.ldap.nextMessageID;
|
function _done(event, obj) {
|
||||||
conn.ldap.messages[message.messageID] = function messageCallback(res) {
|
if (emitter)
|
||||||
|
return emitter.emit(event, obj);
|
||||||
|
|
||||||
|
if (event === 'error')
|
||||||
|
return callback(obj);
|
||||||
|
|
||||||
|
return callback(null, obj);
|
||||||
|
} // end function _done(event, obj)
|
||||||
|
|
||||||
|
function messageCallback(msg) {
|
||||||
if (timer)
|
if (timer)
|
||||||
clearTimeout(timer);
|
clearTimeout(timer);
|
||||||
|
|
||||||
|
if (log.debug())
|
||||||
|
log.debug({msg: msg ? msg.json : null}, 'response received');
|
||||||
|
|
||||||
if (expect === 'abandon')
|
if (expect === 'abandon')
|
||||||
return callback(null);
|
return _done('end', null);
|
||||||
|
|
||||||
if (self.log.debug())
|
if (msg instanceof SearchEntry || msg instanceof SearchReference) {
|
||||||
self.log.debug({res: res.json}, '%s: response received', conn.ldap.id);
|
var event = msg.constructor.name;
|
||||||
|
event = event[0].toLowerCase() + event.slice(1);
|
||||||
var err = null;
|
return _done(event, msg);
|
||||||
|
} else {
|
||||||
if (res instanceof LDAPResult) {
|
|
||||||
delete conn.ldap.messages[message.messageID];
|
delete conn.ldap.messages[message.messageID];
|
||||||
|
|
||||||
if (expect.indexOf(res.status) === -1) {
|
if (msg instanceof LDAPResult) {
|
||||||
err = errors.getError(res);
|
if (expect.indexOf(msg.status) === -1)
|
||||||
if (emitter)
|
return _done('error', errors.getError(msg));
|
||||||
return emitter.emit('error', err);
|
|
||||||
|
|
||||||
return callback(err);
|
return _done('end', msg);
|
||||||
|
} else if (msg instanceof Error) {
|
||||||
|
return _done('error', msg);
|
||||||
|
} else {
|
||||||
|
return _done('error', new errors.ProtocolError(msg.type));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (emitter)
|
|
||||||
return emitter.emit('end', res);
|
|
||||||
|
|
||||||
return callback(null, res);
|
|
||||||
} else if (res instanceof SearchEntry || res instanceof SearchReference) {
|
|
||||||
assert.ok(emitter);
|
|
||||||
var event = res.constructor.name;
|
|
||||||
event = event[0].toLowerCase() + event.slice(1);
|
|
||||||
return emitter.emit(event, res);
|
|
||||||
} else if (res instanceof Error) {
|
|
||||||
if (emitter)
|
|
||||||
return emitter.emit('error', res);
|
|
||||||
|
|
||||||
return callback(res);
|
|
||||||
}
|
}
|
||||||
|
} // end function messageCallback(msg)
|
||||||
|
|
||||||
delete conn.ldap.messages[message.messageID];
|
function onRequestTimeout() {
|
||||||
err = new errors.ProtocolError(res.type);
|
self.emit('timeout', message);
|
||||||
|
if (conn.ldap.messages[message.messageID]) {
|
||||||
|
conn.ldap.messages[message.messageID](new LDAPResult({
|
||||||
|
status: 80, // LDAP_OTHER
|
||||||
|
errorMessage: 'request timeout (client interrupt)'
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
} // end function onRequestTimeout()
|
||||||
|
|
||||||
if (emitter)
|
function writeCallback() {
|
||||||
return emitter.emit('error', err);
|
if (expect === 'abandon') {
|
||||||
|
return callback(null);
|
||||||
|
} else if (expect === 'unbind') {
|
||||||
|
conn.unbindMessageID = message.id;
|
||||||
|
conn.end();
|
||||||
|
} else if (emitter) {
|
||||||
|
return callback(null, emitter);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} // end writeCallback()
|
||||||
|
|
||||||
return callback(err);
|
// Start actually doing something...
|
||||||
};
|
message.messageID = conn.ldap.getNextMessageID();
|
||||||
|
conn.ldap.messages[message.messageID] = messageCallback;
|
||||||
|
|
||||||
// If there's a user specified timeout, pick that up
|
if (self.timeout) {
|
||||||
if (this.timeout) {
|
log.debug('Setting timeout to %d', self.timeout);
|
||||||
timer = setTimeout(function () {
|
timer = setTimeout(onRequestTimeout, self.timeout);
|
||||||
self.emit('timeout', message);
|
|
||||||
if (conn.ldap.messages[message.messageID]) {
|
|
||||||
conn.ldap.messages[message.messageID](new LDAPResult({
|
|
||||||
status: 80, // LDAP_OTHER
|
|
||||||
errorMessage: 'request timeout (client interrupt)'
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}, this.timeout);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (log.debug())
|
||||||
|
log.debug('sending request %j', message.json);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Finally send some data
|
return conn.write(message.toBer(), writeCallback);
|
||||||
if (this.log.debug())
|
|
||||||
this.log.debug({msg: message.json}, '%s: sending request', conn.ldap.id);
|
|
||||||
|
|
||||||
return conn.write(message.toBer(), function writeCallback() {
|
|
||||||
if (expect === 'abandon') {
|
|
||||||
return callback(null);
|
|
||||||
} else if (expect === 'unbind') {
|
|
||||||
conn.unbindMessageID = message.id;
|
|
||||||
conn.end();
|
|
||||||
} else if (emitter) {
|
|
||||||
return callback(null, emitter);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (timer)
|
if (timer)
|
||||||
clearTimeout(timer);
|
clearTimeout(timer);
|
||||||
|
|
||||||
conn.destroy();
|
log.debug({err: e}, 'Error writing message to socket');
|
||||||
delete self.connection;
|
|
||||||
return callback(e);
|
return callback(e);
|
||||||
}
|
}
|
||||||
};
|
};
|
|
@ -0,0 +1,64 @@
|
||||||
|
// Copyright 2012 Mark Cavage, Inc. All rights reserved.
|
||||||
|
|
||||||
|
var assert = require('assert');
|
||||||
|
|
||||||
|
var Logger = require('bunyan');
|
||||||
|
var Pool = require('generic-pool').Pool;
|
||||||
|
|
||||||
|
var Client = require('./client');
|
||||||
|
var ClientPool = require('./pool');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///--- Globals
|
||||||
|
|
||||||
|
var DEF_LOG = new Logger({
|
||||||
|
name: 'ldapjs',
|
||||||
|
component: 'client',
|
||||||
|
stream: process.stderr,
|
||||||
|
serializers: Logger.stdSerializers
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///--- Functions
|
||||||
|
|
||||||
|
function xor() {
|
||||||
|
var b = false;
|
||||||
|
for (var i = 0; i < arguments.length; i++) {
|
||||||
|
if (arguments[i] && !b) {
|
||||||
|
b = true;
|
||||||
|
} else if (arguments[i] && b) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///--- Exports
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
|
||||||
|
createClient: function createClient(options) {
|
||||||
|
if (typeof (options) !== 'object')
|
||||||
|
throw new TypeError('options (object) required');
|
||||||
|
if (options.url && typeof (options.url) !== 'string')
|
||||||
|
throw new TypeError('options.url (string) required');
|
||||||
|
if (options.socketPath && typeof (options.socketPath) !== 'string')
|
||||||
|
throw new TypeError('options.socketPath must be a string');
|
||||||
|
if (!xor(options.url, options.socketPath))
|
||||||
|
throw new TypeError('options.url ^ options.socketPath (String) required');
|
||||||
|
if (!options.log)
|
||||||
|
options.log = DEF_LOG;
|
||||||
|
if (typeof (options.log) !== 'object')
|
||||||
|
throw new TypeError('options.log must be an object');
|
||||||
|
|
||||||
|
if (options.maxConnections > 1)
|
||||||
|
return new ClientPool(options);
|
||||||
|
|
||||||
|
return new Client(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
|
@ -0,0 +1,263 @@
|
||||||
|
// Copyright 2012 Mark Cavage, Inc. All rights reserved.
|
||||||
|
|
||||||
|
var assert = require('assert');
|
||||||
|
var EventEmitter = require('events').EventEmitter;
|
||||||
|
var util = require('util');
|
||||||
|
|
||||||
|
var pooling = require('pooling');
|
||||||
|
|
||||||
|
var ConnectionError = require('../errors').ConnectionError;
|
||||||
|
var BindResponse = require('../messages').BindResponse;
|
||||||
|
|
||||||
|
var Client = require('./client');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///--- Globals
|
||||||
|
|
||||||
|
var STD_OPS = [
|
||||||
|
'add',
|
||||||
|
'del',
|
||||||
|
'modify',
|
||||||
|
'modifyDN'
|
||||||
|
];
|
||||||
|
|
||||||
|
var RETURN_VAL_OPS = [
|
||||||
|
'compare',
|
||||||
|
'exop'
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///--- Internal Functions
|
||||||
|
|
||||||
|
function createPool(options) {
|
||||||
|
assert.ok(options);
|
||||||
|
|
||||||
|
return pooling.createPool({
|
||||||
|
checkInterval: options.checkInterval,
|
||||||
|
log: options.log,
|
||||||
|
name: 'ldapjs_' + (options.url || options.socketPath),
|
||||||
|
max: options.maxConnections,
|
||||||
|
maxIdleTime: options.maxIdleTime,
|
||||||
|
|
||||||
|
create: function createConnection(callback) {
|
||||||
|
var client = new Client(options);
|
||||||
|
|
||||||
|
client.once('error', function (err) {
|
||||||
|
return callback(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
client.once('connect', function onConnect() {
|
||||||
|
client.removeAllListeners('error');
|
||||||
|
|
||||||
|
if (!options.bindDN || !options.bindCredentials)
|
||||||
|
return callback(null, client);
|
||||||
|
|
||||||
|
function bindCallback(err, res) {
|
||||||
|
if (err)
|
||||||
|
return callback(err, null);
|
||||||
|
|
||||||
|
return callback(null, client);
|
||||||
|
}
|
||||||
|
|
||||||
|
return client.bind(options.bindDN,
|
||||||
|
options.bindCredentials,
|
||||||
|
options.bindControls || [],
|
||||||
|
bindCallback);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
check: function check(client, callback) {
|
||||||
|
// just do a root dse search
|
||||||
|
client.search('', '(objectclass=*)', function (err, res) {
|
||||||
|
if (err)
|
||||||
|
return callback(err);
|
||||||
|
|
||||||
|
res.on('error', function (e) {
|
||||||
|
return callback(e);
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.on('end', function () {
|
||||||
|
return callback(null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy: function destroy(client) {
|
||||||
|
client.unbind(function () {});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///--- API
|
||||||
|
|
||||||
|
function ClientPool(options) {
|
||||||
|
assert.ok(options);
|
||||||
|
EventEmitter.call(this, options);
|
||||||
|
|
||||||
|
this.log = options.log.child({clazz: 'ClientPool'}, true);
|
||||||
|
this.options = {
|
||||||
|
bindDN: options.bindDN,
|
||||||
|
bindCredentials: options.bindCredentials,
|
||||||
|
bindControls: options.bindControls || [],
|
||||||
|
checkInterval: options.checkInterval,
|
||||||
|
connectTimeout: (options.connectTimeout || 0),
|
||||||
|
maxIdleTime: options.maxIdleTime,
|
||||||
|
maxConnections: options.maxConnections,
|
||||||
|
log: options.log,
|
||||||
|
socketPath: options.socketPath,
|
||||||
|
timeout: (options.timeout || 0),
|
||||||
|
url: options.url
|
||||||
|
};
|
||||||
|
this.pool = createPool(options);
|
||||||
|
}
|
||||||
|
util.inherits(ClientPool, EventEmitter);
|
||||||
|
module.exports = ClientPool;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
STD_OPS.forEach(function (op) {
|
||||||
|
ClientPool.prototype[op] = function clientProxy() {
|
||||||
|
var args = Array.prototype.slice.call(arguments);
|
||||||
|
var cb = args.pop();
|
||||||
|
if (typeof (cb) !== 'function')
|
||||||
|
throw new TypeError('callback (Function) required');
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
return this.pool.acquire(function onAcquire(err, client) {
|
||||||
|
if (err)
|
||||||
|
return cb(err);
|
||||||
|
|
||||||
|
args.push(function proxyCallback(err, res) {
|
||||||
|
self.pool.release(client);
|
||||||
|
return cb(err, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
return Client.prototype[op].apply(client, args);
|
||||||
|
} catch (e) {
|
||||||
|
self.pool.release(client);
|
||||||
|
return cb(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
RETURN_VAL_OPS.forEach(function (op) {
|
||||||
|
ClientPool.prototype[op] = function clientProxy() {
|
||||||
|
var args = Array.prototype.slice.call(arguments);
|
||||||
|
var cb = args.pop();
|
||||||
|
if (typeof (cb) !== 'function')
|
||||||
|
throw new TypeError('callback (Function) required');
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
return this.pool.acquire(function onAcquire(poolErr, client) {
|
||||||
|
if (poolErr)
|
||||||
|
return cb(poolErr);
|
||||||
|
|
||||||
|
args.push(function proxyCallback(err, val, res) {
|
||||||
|
self.pool.release(client);
|
||||||
|
return cb(err, val, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
return Client.prototype[op].apply(client, args);
|
||||||
|
} catch (e) {
|
||||||
|
self.pool.release(client);
|
||||||
|
return cb(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
ClientPool.prototype.search = function search(base, opts, controls, callback) {
|
||||||
|
if (typeof (controls) === 'function') {
|
||||||
|
callback = controls;
|
||||||
|
controls = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
return this.pool.acquire(function onAcquire(err, client) {
|
||||||
|
if (err)
|
||||||
|
return callback(err);
|
||||||
|
|
||||||
|
// This is largely in existence for search requests
|
||||||
|
client.timeout = self.timeout || client.timeout;
|
||||||
|
|
||||||
|
|
||||||
|
return client.search(base, opts, controls, function (err, res) {
|
||||||
|
function cleanup() {
|
||||||
|
self.pool.release(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
cleanup();
|
||||||
|
return callback(err, res);
|
||||||
|
}
|
||||||
|
res.on('error', cleanup);
|
||||||
|
res.on('end', cleanup);
|
||||||
|
|
||||||
|
return callback(null, res);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
ClientPool.prototype.abandon = function abandon(msgid, controls, callback) {
|
||||||
|
if (typeof (controls) === 'function') {
|
||||||
|
callback = controls;
|
||||||
|
controls = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.log.error({
|
||||||
|
messageID: msgid
|
||||||
|
}, 'Abandon is not supported with connection pooling. Ignoring.');
|
||||||
|
return callback(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
ClientPool.prototype.bind = function bind(dn, creds, controls, callback) {
|
||||||
|
if (typeof (controls) === 'function') {
|
||||||
|
callback = controls;
|
||||||
|
controls = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
self.options.bindDN = null;
|
||||||
|
self.options.bindCredentials = null;
|
||||||
|
self.options.bindControls = null;
|
||||||
|
|
||||||
|
return this.pool.shutdown(function () {
|
||||||
|
self.pool = createPool(self.options);
|
||||||
|
|
||||||
|
return self.pool.acquire(function onAcquire(err, client) {
|
||||||
|
if (err)
|
||||||
|
return callback(err);
|
||||||
|
|
||||||
|
return client.bind(dn, creds, controls, function (err, res) {
|
||||||
|
self.pool.release(client);
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
return callback(err, res);
|
||||||
|
|
||||||
|
self.options.bindDN = dn;
|
||||||
|
self.options.bindCredentials = creds;
|
||||||
|
self.options.bindControls = controls;
|
||||||
|
|
||||||
|
return callback(null, res);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
ClientPool.prototype.unbind = function unbind(callback) {
|
||||||
|
return this.pool.shutdown(callback);
|
||||||
|
};
|
|
@ -50,7 +50,10 @@ PagedResultsControl.prototype.parse = function parse(buffer) {
|
||||||
this._value = {};
|
this._value = {};
|
||||||
this._value.size = ber.readInt();
|
this._value.size = ber.readInt();
|
||||||
this._value.cookie = ber.readString(asn1.Ber.OctetString, true);
|
this._value.cookie = ber.readString(asn1.Ber.OctetString, true);
|
||||||
if(!this._value.cookie) this._value.cookie = new Buffer(0); //readString returns '' instead of a zero-length buffer
|
//readString returns '' instead of a zero-length buffer
|
||||||
|
if (!this._value.cookie)
|
||||||
|
this._value.cookie = new Buffer(0);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,10 +70,11 @@ PagedResultsControl.prototype._toBer = function (ber) {
|
||||||
var writer = new BerWriter();
|
var writer = new BerWriter();
|
||||||
writer.startSequence();
|
writer.startSequence();
|
||||||
writer.writeInt(this.value.size);
|
writer.writeInt(this.value.size);
|
||||||
if(this.value.cookie && this.value.cookie.length>0)
|
if (this.value.cookie && this.value.cookie.length > 0) {
|
||||||
writer.writeBuffer(this.value.cookie, asn1.Ber.OctetString);
|
writer.writeBuffer(this.value.cookie, asn1.Ber.OctetString);
|
||||||
else
|
} else {
|
||||||
writer.writeString(''); //writeBuffer rejects zero-length buffers
|
writer.writeString(''); //writeBuffer rejects zero-length buffers
|
||||||
|
}
|
||||||
writer.endSequence();
|
writer.endSequence();
|
||||||
|
|
||||||
ber.writeBuffer(writer.buffer, 0x04);
|
ber.writeBuffer(writer.buffer, 0x04);
|
||||||
|
|
|
@ -139,3 +139,16 @@ module.exports.getMessage = function (code) {
|
||||||
var errObj = ERRORS[code];
|
var errObj = ERRORS[code];
|
||||||
return (errObj && errObj.message ? errObj.message : '');
|
return (errObj && errObj.message ? errObj.message : '');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function ConnectionError(message) {
|
||||||
|
LDAPError.call(this,
|
||||||
|
'ConnectionError',
|
||||||
|
0x80, // LDAP_OTHER,
|
||||||
|
message,
|
||||||
|
null,
|
||||||
|
ConnectionError);
|
||||||
|
}
|
||||||
|
util.inherits(ConnectionError, LDAPError);
|
||||||
|
module.exports.ConnectionError = ConnectionError;
|
||||||
|
|
17
lib/index.js
17
lib/index.js
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
var Logger = require('bunyan');
|
var Logger = require('bunyan');
|
||||||
|
|
||||||
var Client = require('./client');
|
var client = require('./client');
|
||||||
var Attribute = require('./attribute');
|
var Attribute = require('./attribute');
|
||||||
var Change = require('./change');
|
var Change = require('./change');
|
||||||
var Protocol = require('./protocol');
|
var Protocol = require('./protocol');
|
||||||
|
@ -42,20 +42,7 @@ if (!String.prototype.endsWith) {
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|
||||||
Client: Client,
|
createClient: client.createClient,
|
||||||
createClient: function (options) {
|
|
||||||
if (typeof (options) !== 'object')
|
|
||||||
throw new TypeError('options (object) required');
|
|
||||||
|
|
||||||
if (!options.log) {
|
|
||||||
options.log = new Logger({
|
|
||||||
name: 'ldapjs',
|
|
||||||
component: 'client',
|
|
||||||
stream: process.stderr
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return new Client(options);
|
|
||||||
},
|
|
||||||
|
|
||||||
Server: Server,
|
Server: Server,
|
||||||
createServer: function (options) {
|
createServer: function (options) {
|
||||||
|
|
11
package.json
11
package.json
|
@ -25,13 +25,14 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"asn1": "0.1.11",
|
"asn1": "0.1.11",
|
||||||
"buffertools": "1.0.7",
|
"buffertools": "1.0.9",
|
||||||
"bunyan": "0.6.3",
|
"bunyan": "0.6.8",
|
||||||
"dtrace-provider": "0.0.6",
|
"dtrace-provider": "0.0.7",
|
||||||
"nopt": "1.0.10"
|
"nopt": "1.0.10",
|
||||||
|
"pooling": "0.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"tap": "0.2",
|
"tap": "0.2.4",
|
||||||
"node-uuid": "1.3.3"
|
"node-uuid": "1.3.3"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||||
|
|
||||||
|
var Logger = require('bunyan');
|
||||||
|
|
||||||
var test = require('tap').test;
|
var test = require('tap').test;
|
||||||
var uuid = require('node-uuid');
|
var uuid = require('node-uuid');
|
||||||
|
|
||||||
|
@ -129,7 +131,14 @@ test('setup', function (t) {
|
||||||
server.listen(SOCKET, function () {
|
server.listen(SOCKET, function () {
|
||||||
client = ldap.createClient({
|
client = ldap.createClient({
|
||||||
socketPath: SOCKET,
|
socketPath: SOCKET,
|
||||||
reconnect: false // turn this off for unit testing
|
maxConnections: process.env.LDAP_MAX_CONNS || 5,
|
||||||
|
log: new Logger({
|
||||||
|
name: 'ldapjs_unit_test',
|
||||||
|
stream: process.stderr,
|
||||||
|
level: (process.env.LOG_LEVEL || 'info'),
|
||||||
|
serializers: Logger.stdSerializers,
|
||||||
|
idleTimeoutMillis: 10
|
||||||
|
})
|
||||||
});
|
});
|
||||||
t.ok(client);
|
t.ok(client);
|
||||||
t.end();
|
t.end();
|
||||||
|
@ -138,16 +147,6 @@ test('setup', function (t) {
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
test('simple bind success', function (t) {
|
|
||||||
client.bind(BIND_DN, BIND_PW, function (err, res) {
|
|
||||||
t.ifError(err);
|
|
||||||
t.ok(res);
|
|
||||||
t.equal(res.status, 0);
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
test('simple bind failure', function (t) {
|
test('simple bind failure', function (t) {
|
||||||
client.bind(BIND_DN, uuid(), function (err, res) {
|
client.bind(BIND_DN, uuid(), function (err, res) {
|
||||||
t.ok(err);
|
t.ok(err);
|
||||||
|
@ -164,6 +163,16 @@ test('simple bind failure', function (t) {
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
test('simple bind success', function (t) {
|
||||||
|
client.bind(BIND_DN, BIND_PW, function (err, res) {
|
||||||
|
t.ifError(err);
|
||||||
|
t.ok(res);
|
||||||
|
t.equal(res.status, 0);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
test('add success', function (t) {
|
test('add success', function (t) {
|
||||||
var attrs = [
|
var attrs = [
|
||||||
new Attribute({
|
new Attribute({
|
||||||
|
@ -579,6 +588,7 @@ test('search timeout (GH-51)', function (t) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
test('unbind (GH-30)', function (t) {
|
test('unbind (GH-30)', function (t) {
|
||||||
client.unbind(function (err) {
|
client.unbind(function (err) {
|
||||||
t.ifError(err);
|
t.ifError(err);
|
||||||
|
|
|
@ -9,7 +9,7 @@ var getControl;
|
||||||
var PagedResultsControl;
|
var PagedResultsControl;
|
||||||
|
|
||||||
function bufferEqual(t, a, b) {
|
function bufferEqual(t, a, b) {
|
||||||
t.equal(a.toString('hex'), b.toString('hex'))
|
t.equal(a.toString('hex'), b.toString('hex'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -38,14 +38,14 @@ test('new with args', function (t) {
|
||||||
criticality: true,
|
criticality: true,
|
||||||
value: {
|
value: {
|
||||||
size: 1000,
|
size: 1000,
|
||||||
cookie: new Buffer([1,2,3])
|
cookie: new Buffer([1, 2, 3])
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
t.ok(c);
|
t.ok(c);
|
||||||
t.equal(c.type, '1.2.840.113556.1.4.319');
|
t.equal(c.type, '1.2.840.113556.1.4.319');
|
||||||
t.ok(c.criticality);
|
t.ok(c.criticality);
|
||||||
t.equal(c.value.size, 1000);
|
t.equal(c.value.size, 1000);
|
||||||
bufferEqual(t,c.value.cookie, new Buffer([1,2,3]));
|
bufferEqual(t, c.value.cookie, new Buffer([1, 2, 3]));
|
||||||
|
|
||||||
|
|
||||||
var writer = new BerWriter();
|
var writer = new BerWriter();
|
||||||
|
@ -57,7 +57,7 @@ test('new with args', function (t) {
|
||||||
t.equal(psc.type, '1.2.840.113556.1.4.319');
|
t.equal(psc.type, '1.2.840.113556.1.4.319');
|
||||||
t.ok(psc.criticality);
|
t.ok(psc.criticality);
|
||||||
t.equal(psc.value.size, 1000);
|
t.equal(psc.value.size, 1000);
|
||||||
bufferEqual(t,psc.value.cookie, new Buffer([1,2,3]));
|
bufferEqual(t, psc.value.cookie, new Buffer([1, 2, 3]));
|
||||||
|
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
@ -80,7 +80,7 @@ test('tober', function (t) {
|
||||||
t.equal(c.type, '1.2.840.113556.1.4.319');
|
t.equal(c.type, '1.2.840.113556.1.4.319');
|
||||||
t.ok(c.criticality);
|
t.ok(c.criticality);
|
||||||
t.equal(c.value.size, 20);
|
t.equal(c.value.size, 20);
|
||||||
bufferEqual(t,c.value.cookie, new Buffer(0));
|
bufferEqual(t, c.value.cookie, new Buffer(0));
|
||||||
|
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue