Add persistent search cache, minor bug fix to client
This commit is contained in:
parent
763f4630f7
commit
116bedb487
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
titile: Persistent Search Cache API| ldapjs
|
||||
markdown2extras: wiki-tables
|
||||
logo-color: green
|
||||
logo-font-family: google:Aldrich, Verdana, sans-serif
|
||||
header-font-family: google:Aldrich, Verdana, sans-serif
|
||||
---
|
||||
|
||||
# ldapjs Persistent Search Cache API
|
||||
|
||||
This document covers the ldapjs Persistent Search Cache API and assumes you are familiar with LDAP. If you're not, read the [guide](http://ldapjs.org/guide.html) first.
|
||||
|
||||
This document also assumes you are familiar with LDAP persistent search. If you're not, read the [rfc](http://tools.ietf.org/id/draft-ietf-ldapext-psearch-03.txt) first.
|
||||
|
||||
Note this API is a cache used to store all connected persistent search clients, and does not actually implement persistent search.
|
||||
|
||||
# addClient(req, res, callback)
|
||||
|
||||
Adds a client to the cache.
|
||||
|
||||
# removeClient(req, res, callback)
|
||||
|
||||
Removes a client from the cache.
|
|
@ -52,7 +52,7 @@ var MAX_MSGID = Math.pow(2, 31) - 1;
|
|||
function xor() {
|
||||
var b = false;
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
if (arguments[i] && !b) {
|
||||
if (arguments[i] && !b) {
|
||||
b = true;
|
||||
} else if (arguments[i] && b) {
|
||||
return false;
|
||||
|
@ -64,7 +64,7 @@ function xor() {
|
|||
|
||||
function validateControls(controls) {
|
||||
if (Array.isArray(controls)) {
|
||||
controls.forEach(function (c) {
|
||||
controls.forEach(function(c) {
|
||||
if (!(c instanceof Control))
|
||||
throw new TypeError('controls must be [Control]');
|
||||
});
|
||||
|
@ -119,20 +119,21 @@ function Client(options) {
|
|||
|
||||
EventEmitter.call(this, options);
|
||||
|
||||
var parsedUrl;
|
||||
if (options.url)
|
||||
options.url = url.parse(options.url);
|
||||
parsedUrl = url.parse(options.url);
|
||||
|
||||
this.connection = null;
|
||||
this.connectTimeout = options.connectTimeout || false;
|
||||
this.connectOptions = {
|
||||
port: options.url ? options.url.port : options.socketPath,
|
||||
host: options.url ? options.url.hostname : undefined,
|
||||
port: parsedUrl ? parsedUrl.port : options.socketPath,
|
||||
host: parsedUrl ? parsedUrl.hostname : undefined,
|
||||
socketPath: options.socketPath || undefined
|
||||
};
|
||||
this.log = options.log;
|
||||
this.secure = options.url ? options.url.secure : false;
|
||||
this.secure = parsedUrl ? parsedUrl.secure : false;
|
||||
this.timeout = options.timeout || false;
|
||||
this.url = options.url || false;
|
||||
this.url = parsedUrl || false;
|
||||
|
||||
// We'll emit a connect event when this is done
|
||||
this.connect();
|
||||
|
@ -201,7 +202,7 @@ Client.prototype.add = function add(name, entry, controls, callback) {
|
|||
throw new TypeError('callback (function) required');
|
||||
|
||||
if (Array.isArray(entry)) {
|
||||
entry.forEach(function (a) {
|
||||
entry.forEach(function(a) {
|
||||
if (!Attribute.isAttribute(a))
|
||||
throw new TypeError('entry must be an Array of Attributes');
|
||||
});
|
||||
|
@ -209,10 +210,10 @@ Client.prototype.add = function add(name, entry, controls, callback) {
|
|||
var save = entry;
|
||||
|
||||
entry = [];
|
||||
Object.keys(save).forEach(function (k) {
|
||||
Object.keys(save).forEach(function(k) {
|
||||
var attr = new Attribute({type: k});
|
||||
if (Array.isArray(save[k])) {
|
||||
save[k].forEach(function (v) {
|
||||
save[k].forEach(function(v) {
|
||||
attr.addValue(v.toString());
|
||||
});
|
||||
} else {
|
||||
|
@ -303,7 +304,7 @@ Client.prototype.compare = function compare(name,
|
|||
controls: controls
|
||||
});
|
||||
|
||||
return this._send(req, CMP_EXPECT, null, function (err, res) {
|
||||
return this._send(req, CMP_EXPECT, null, function(err, res) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
|
@ -379,7 +380,7 @@ Client.prototype.exop = function exop(name, value, controls, callback) {
|
|||
controls: controls
|
||||
});
|
||||
|
||||
return this._send(req, [errors.LDAP_SUCCESS], null, function (err, res) {
|
||||
return this._send(req, [errors.LDAP_SUCCESS], null, function(err, res) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
|
@ -411,7 +412,7 @@ Client.prototype.modify = function modify(name, change, controls, callback) {
|
|||
if (typeof (change.modification) !== 'object')
|
||||
throw new Error('change.modification (object) required');
|
||||
|
||||
Object.keys(change.modification).forEach(function (k) {
|
||||
Object.keys(change.modification).forEach(function(k) {
|
||||
var mod = {};
|
||||
mod[k] = change.modification[k];
|
||||
changes.push(new Change({
|
||||
|
@ -424,7 +425,7 @@ Client.prototype.modify = function modify(name, change, controls, callback) {
|
|||
if (change instanceof Change) {
|
||||
changes.push(change);
|
||||
} else if (Array.isArray(change)) {
|
||||
change.forEach(function (c) {
|
||||
change.forEach(function(c) {
|
||||
if (c instanceof Change) {
|
||||
changes.push(c);
|
||||
} else {
|
||||
|
@ -599,7 +600,7 @@ Client.prototype.search = function search(base, options, controls, callback) {
|
|||
*/
|
||||
Client.prototype.unbind = function unbind(callback) {
|
||||
if (!callback)
|
||||
callback = function () {};
|
||||
callback = function() {};
|
||||
|
||||
if (typeof (callback) !== 'function')
|
||||
throw new TypeError('callback must be a function');
|
||||
|
@ -629,7 +630,7 @@ Client.prototype.connect = function connect(callback) {
|
|||
c = proto.connect(opts.port, opts.host);
|
||||
|
||||
if (this.connectTimeout) {
|
||||
timer = setTimeout(function () {
|
||||
timer = setTimeout(function() {
|
||||
c.destroy();
|
||||
|
||||
self.emit('connectTimeout', new ConnectionError('timeout'));
|
||||
|
@ -657,7 +658,7 @@ Client.prototype.connect = function connect(callback) {
|
|||
})
|
||||
};
|
||||
|
||||
c.on('connect', function () {
|
||||
c.on('connect', function() {
|
||||
if (timer)
|
||||
clearTimeout(timer);
|
||||
|
||||
|
@ -674,7 +675,7 @@ Client.prototype.connect = function connect(callback) {
|
|||
return (typeof (callback) === 'function' ? callback(null, c) : false);
|
||||
});
|
||||
|
||||
c.on('end', function () {
|
||||
c.on('end', function() {
|
||||
if (log.trace())
|
||||
log.trace('%s end event', c.ldap.id);
|
||||
|
||||
|
@ -683,11 +684,11 @@ Client.prototype.connect = function connect(callback) {
|
|||
|
||||
// On close we have to walk the outstanding messages and go invoke their
|
||||
// callback with an error
|
||||
c.on('close', function (had_err) {
|
||||
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) {
|
||||
Object.keys(c.ldap.messages).forEach(function(msgid) {
|
||||
var err;
|
||||
if (c.unbindMessageID !== parseInt(msgid, 10)) {
|
||||
err = new ConnectionError(c.ldap.id + ' closed');
|
||||
|
@ -714,7 +715,7 @@ Client.prototype.connect = function connect(callback) {
|
|||
});
|
||||
});
|
||||
|
||||
c.on('error', function (err) {
|
||||
c.on('error', function(err) {
|
||||
if (log.trace())
|
||||
log.trace({err: err}, '%s error event', c.ldap.id);
|
||||
|
||||
|
@ -724,7 +725,7 @@ Client.prototype.connect = function connect(callback) {
|
|||
c.end();
|
||||
});
|
||||
|
||||
c.on('timeout', function () {
|
||||
c.on('timeout', function() {
|
||||
if (log.trace())
|
||||
log.trace('%s timeout event=%s', c.ldap.id);
|
||||
|
||||
|
@ -732,7 +733,7 @@ Client.prototype.connect = function connect(callback) {
|
|||
c.end();
|
||||
});
|
||||
|
||||
c.on('data', function (data) {
|
||||
c.on('data', function(data) {
|
||||
if (log.trace())
|
||||
log.trace('%s data event: %s', c.ldap.id, util.inspect(data));
|
||||
|
||||
|
@ -740,7 +741,7 @@ Client.prototype.connect = function connect(callback) {
|
|||
});
|
||||
|
||||
// The "router"
|
||||
c.ldap.parser.on('message', function (message) {
|
||||
c.ldap.parser.on('message', function(message) {
|
||||
message.connection = c;
|
||||
var callback = c.ldap.messages[message.messageID];
|
||||
|
||||
|
@ -752,8 +753,8 @@ Client.prototype.connect = function connect(callback) {
|
|||
return callback(message);
|
||||
});
|
||||
|
||||
c.ldap.parser.on('error', function (err) {
|
||||
log.debug({err: err}, '%s parser error event', c.ldap.id);
|
||||
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);
|
||||
|
@ -829,7 +830,7 @@ Client.prototype._send = function _send(message, expect, emitter, callback) {
|
|||
|
||||
// If there's a user specified timeout, pick that up
|
||||
if (this.timeout) {
|
||||
timer = setTimeout(function () {
|
||||
timer = setTimeout(function() {
|
||||
self.emit('timeout', message);
|
||||
if (conn.ldap.messages[message.messageID]) {
|
||||
conn.ldap.messages[message.messageID](new LDAPResult({
|
||||
|
|
15
lib/index.js
15
lib/index.js
|
@ -5,12 +5,12 @@ var Logger = require('bunyan');
|
|||
var Client = require('./client');
|
||||
var Attribute = require('./attribute');
|
||||
var Change = require('./change');
|
||||
var PersistentSearch = require('./persistent_search');
|
||||
var Protocol = require('./protocol');
|
||||
var Server = require('./server');
|
||||
|
||||
var assert = require('assert');
|
||||
var controls = require('./controls');
|
||||
var persistentSearch = require('./persistent_search');
|
||||
var dn = require('./dn');
|
||||
var errors = require('./errors');
|
||||
var filters = require('./filters');
|
||||
|
@ -22,7 +22,7 @@ var url = require('./url');
|
|||
/// Hack a few things we need (i.e., "monkey patch" the prototype)
|
||||
|
||||
if (!String.prototype.startsWith) {
|
||||
String.prototype.startsWith = function (str) {
|
||||
String.prototype.startsWith = function(str) {
|
||||
var re = new RegExp('^' + str);
|
||||
return re.test(this);
|
||||
};
|
||||
|
@ -30,7 +30,7 @@ if (!String.prototype.startsWith) {
|
|||
|
||||
|
||||
if (!String.prototype.endsWith) {
|
||||
String.prototype.endsWith = function (str) {
|
||||
String.prototype.endsWith = function(str) {
|
||||
var re = new RegExp(str + '$');
|
||||
return re.test(this);
|
||||
};
|
||||
|
@ -43,7 +43,7 @@ if (!String.prototype.endsWith) {
|
|||
module.exports = {
|
||||
|
||||
Client: Client,
|
||||
createClient: function (options) {
|
||||
createClient: function(options) {
|
||||
if (typeof (options) !== 'object')
|
||||
throw new TypeError('options (object) required');
|
||||
|
||||
|
@ -58,7 +58,7 @@ module.exports = {
|
|||
},
|
||||
|
||||
Server: Server,
|
||||
createServer: function (options) {
|
||||
createServer: function(options) {
|
||||
if (options === undefined)
|
||||
options = {};
|
||||
|
||||
|
@ -80,16 +80,19 @@ module.exports = {
|
|||
Change: Change,
|
||||
|
||||
DN: dn.DN,
|
||||
PersistentSearchCache: persistentSearch.PersistentSearchCache,
|
||||
RDN: dn.RDN,
|
||||
|
||||
parseDN: dn.parse,
|
||||
dn: dn,
|
||||
|
||||
persistentSearch: persistentSearch,
|
||||
|
||||
filters: filters,
|
||||
parseFilter: filters.parseString,
|
||||
|
||||
parseURL: url.parse,
|
||||
|
||||
PersistentSearch: PersistentSearch,
|
||||
url: url
|
||||
};
|
||||
|
||||
|
|
|
@ -7,12 +7,11 @@ var EntryChangeNotificationControl =
|
|||
|
||||
///--- API
|
||||
|
||||
|
||||
// Cache used to store connected persistent search clients
|
||||
function PersistentSearch() {
|
||||
this.clientList = [];
|
||||
}
|
||||
module.exports = PersistentSearch;
|
||||
|
||||
|
||||
PersistentSearch.prototype.addClient = function(req, res, callback) {
|
||||
if (typeof(req) !== 'object')
|
||||
|
@ -69,7 +68,7 @@ PersistentSearch.prototype.removeClient = function(req, res, callback) {
|
|||
};
|
||||
|
||||
|
||||
getOperationType = function(requestType) {
|
||||
function getOperationType(requestType) {
|
||||
switch (requestType) {
|
||||
case 'AddRequest':
|
||||
case 'add':
|
||||
|
@ -87,13 +86,10 @@ getOperationType = function(requestType) {
|
|||
throw new TypeError('requestType %s, is an invalid request type',
|
||||
request);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
PersistentSearch.getEntryChangeNotificationControl =
|
||||
function(req, obj, callback) {
|
||||
|
||||
console.log('getEntryChangeNotificationControl', obj);
|
||||
function getEntryChangeNotificationControl(req, obj, callback) {
|
||||
// if we want to return a ECNC
|
||||
if (req.persistentSearch.value.returnECs) {
|
||||
var attrs = obj.attributes;
|
||||
|
@ -109,10 +105,19 @@ PersistentSearch.getEntryChangeNotificationControl =
|
|||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
PersistentSearch.checkChangeType = function(req, requestType) {
|
||||
function checkChangeType(req, requestType) {
|
||||
return (req.persistentSearch.value.changeTypes &
|
||||
getOperationType(requestType));
|
||||
}
|
||||
|
||||
|
||||
///--- Exports
|
||||
|
||||
module.exports = {
|
||||
PersistentSearchCache: PersistentSearch,
|
||||
checkChangeType: checkChangeType,
|
||||
getEntryChangeNotificationControl: getEntryChangeNotificationControl
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue