Skip to content
This repository was archived by the owner on May 14, 2024. It is now read-only.

Commit 9b1a10f

Browse files
authored
Merge branch 'master' into master
2 parents c2494ec + 505f1f8 commit 9b1a10f

File tree

4 files changed

+58
-19
lines changed

4 files changed

+58
-19
lines changed

docs/client.md

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -276,15 +276,15 @@ containing the following fields:
276276
|timeLimit |the maximum amount of time the server should take in responding, in seconds. Defaults to 10. Lots of servers will ignore this.|
277277
|paged |enable and/or configure automatic result paging|
278278

279-
Responses from the `search` method are an `EventEmitter` where you will get a
280-
notification for each `searchEntry` that comes back from the server. You will
281-
additionally be able to listen for a `searchReference`, `error` and `end` event.
282-
Note that the `error` event will only be for client/TCP errors, not LDAP error
283-
codes like the other APIs. You'll want to check the LDAP status code
284-
(likely for `0`) on the `end` event to assert success. LDAP search results
285-
can give you a lot of status codes, such as time or size exceeded, busy,
286-
inappropriate matching, etc., which is why this method doesn't try to wrap up
287-
the code matching.
279+
Responses inside callback of the `search` method are an `EventEmitter` where you will get a notification for
280+
each `searchEntry` that comes back from the server. You will additionally be able to listen for a `searchRequest`
281+
, `searchReference`, `error` and `end` event.
282+
`searchRequest` is emitted immediately after every `SearchRequest` is sent with a `SearchRequest` parameter. You can do operations
283+
like `client.abandon` with `searchRequest.messageID` to abandon this search request. Note that the `error` event will
284+
only be for client/TCP errors, not LDAP error codes like the other APIs. You'll want to check the LDAP status code
285+
(likely for `0`) on the `end` event to assert success. LDAP search results can give you a lot of status codes, such as
286+
time or size exceeded, busy, inappropriate matching, etc., which is why this method doesn't try to wrap up the code
287+
matching.
288288

289289
Example:
290290

@@ -298,6 +298,9 @@ const opts = {
298298
client.search('o=example', opts, (err, res) => {
299299
assert.ifError(err);
300300

301+
res.on('searchRequest', (searchRequest) => {
302+
console.log('searchRequest: ', searchRequest.messageID);
303+
});
301304
res.on('searchEntry', (entry) => {
302305
console.log('entry: ' + JSON.stringify(entry.object));
303306
});

lib/client/client.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -630,9 +630,9 @@ Client.prototype.search = function search (base,
630630
callback: callback,
631631
controls: controls,
632632
pageSize: size,
633-
pagePause: pageOpts.pagePause
633+
pagePause: pageOpts.pagePause,
634+
sendRequest: sendRequest
634635
})
635-
pager.on('search', sendRequest)
636636
pager.begin()
637637
} else {
638638
sendRequest(controls, new CorkedEmitter(), callback)
@@ -1251,7 +1251,9 @@ Client.prototype._sendSocket = function _sendSocket (message,
12511251
conn.end()
12521252
} else if (emitter) {
12531253
sentEmitter = true
1254-
return callback(null, emitter)
1254+
callback(null, emitter)
1255+
emitter.emit('searchRequest', message)
1256+
return
12551257
}
12561258
return false
12571259
}

lib/client/search_pager.js

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ const assert = require('assert-plus')
1010
// var Protocol = require('../protocol')
1111
const PagedControl = require('../controls/paged_results_control.js')
1212

13+
const CorkedEmitter = require('../corked_emitter.js')
14+
1315
/// --- API
1416

1517
/**
@@ -29,19 +31,20 @@ const PagedControl = require('../controls/paged_results_control.js')
2931
* will be emitted (and 'end' will not be). By listening to
3032
* 'pageError', a successful search that lacks paging will be
3133
* able to emit 'end'.
32-
* 3. search - Emitted as an internal event to trigger another client search.
3334
*/
3435
function SearchPager (opts) {
3536
assert.object(opts)
3637
assert.func(opts.callback)
3738
assert.number(opts.pageSize)
39+
assert.func(opts.sendRequest)
3840

39-
EventEmitter.call(this, {})
41+
CorkedEmitter.call(this, {})
4042

4143
this.callback = opts.callback
4244
this.controls = opts.controls
4345
this.pageSize = opts.pageSize
4446
this.pagePause = opts.pagePause
47+
this.sendRequest = opts.sendRequest
4548

4649
this.controls.forEach(function (control) {
4750
if (control.type === PagedControl.OID) {
@@ -55,12 +58,13 @@ function SearchPager (opts) {
5558
this.started = false
5659

5760
const emitter = new EventEmitter()
61+
emitter.on('searchRequest', this.emit.bind(this, 'searchRequest'))
5862
emitter.on('searchEntry', this.emit.bind(this, 'searchEntry'))
5963
emitter.on('end', this._onEnd.bind(this))
6064
emitter.on('error', this._onError.bind(this))
6165
this.childEmitter = emitter
6266
}
63-
util.inherits(SearchPager, EventEmitter)
67+
util.inherits(SearchPager, CorkedEmitter)
6468
module.exports = SearchPager
6569

6670
/**
@@ -143,8 +147,7 @@ SearchPager.prototype._nextPage = function _nextPage (cookie) {
143147
}
144148
}))
145149

146-
this.emit('search', controls, this.childEmitter,
147-
this._sendCallback.bind(this))
150+
this.sendRequest(controls, this.childEmitter, this._sendCallback.bind(this))
148151
}
149152

150153
/**

test/client.test.js

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -777,14 +777,23 @@ tap.test('search paged', { timeout: 10000 }, function (t) {
777777
t.test('paged - no pauses', function (t2) {
778778
let countEntries = 0
779779
let countPages = 0
780+
let currentSearchRequest = null
780781
t.context.client.search('cn=paged', { paged: { pageSize: 100 } }, function (err, res) {
781782
t2.error(err)
782783
res.on('searchEntry', entryListener)
784+
res.on('searchRequest', (searchRequest) => {
785+
t2.ok(searchRequest instanceof ldap.SearchRequest)
786+
if (currentSearchRequest === null) {
787+
t2.equal(countPages, 0)
788+
}
789+
currentSearchRequest = searchRequest
790+
})
783791
res.on('page', pageListener)
784792
res.on('error', (err) => t2.error(err))
785-
res.on('end', function () {
793+
res.on('end', function (result) {
786794
t2.equal(countEntries, 1000)
787795
t2.equal(countPages, 10)
796+
t2.equal(result.messageID, currentSearchRequest.messageID)
788797
t2.end()
789798
})
790799

@@ -797,8 +806,11 @@ tap.test('search paged', { timeout: 10000 }, function (t) {
797806
countEntries += 1
798807
}
799808

800-
function pageListener () {
809+
function pageListener (result) {
801810
countPages += 1
811+
if (countPages < 10) {
812+
t2.equal(result.messageID, currentSearchRequest.messageID)
813+
}
802814
}
803815
})
804816
})
@@ -899,6 +911,25 @@ tap.test('search paged', { timeout: 10000 }, function (t) {
899911
})
900912
})
901913

914+
tap.test('paged - search with delayed event listener binding', function (t) {
915+
t.context.client.search('cn=paged', { filter: '(objectclass=*)', paged: true }, function (err, res) {
916+
t.error(err)
917+
setTimeout(() => {
918+
let gotEntry = 0
919+
res.on('searchEntry', function () {
920+
gotEntry++
921+
})
922+
res.on('error', function (err) {
923+
t.fail(err)
924+
})
925+
res.on('end', function () {
926+
t.equal(gotEntry, 1000)
927+
t.end()
928+
})
929+
}, 100)
930+
})
931+
})
932+
902933
t.end()
903934
})
904935

0 commit comments

Comments
 (0)