From 116bedb4873272df7187fc221a5376877011e280 Mon Sep 17 00:00:00 2001 From: Yunong Xiao Date: Thu, 23 Feb 2012 15:21:17 -0800 Subject: [PATCH] Add persistent search cache, minor bug fix to client --- docs/persistent_search.md | 23 ++++++++++++++++ lib/client.js | 55 ++++++++++++++++++++------------------- lib/index.js | 15 ++++++----- lib/persistent_search.js | 25 +++++++++++------- 4 files changed, 75 insertions(+), 43 deletions(-) create mode 100644 docs/persistent_search.md diff --git a/docs/persistent_search.md b/docs/persistent_search.md new file mode 100644 index 0000000..bf236be --- /dev/null +++ b/docs/persistent_search.md @@ -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. \ No newline at end of file diff --git a/lib/client.js b/lib/client.js index 664ad53..4aa717c 100644 --- a/lib/client.js +++ b/lib/client.js @@ -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({ diff --git a/lib/index.js b/lib/index.js index d87fac7..9d102bd 100644 --- a/lib/index.js +++ b/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 }; diff --git a/lib/persistent_search.js b/lib/persistent_search.js index 8b9b979..700d628 100644 --- a/lib/persistent_search.js +++ b/lib/persistent_search.js @@ -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 };