Skip to content

Commit 44281c4

Browse files
author
Jerry Cheung
committed
Merge remote-tracking branch 'origin/master' into search-timeout
Conflicts: test/integration/test_search.rb
2 parents e5e47a4 + 3320248 commit 44281c4

File tree

7 files changed

+159
-11
lines changed

7 files changed

+159
-11
lines changed

lib/net/ldap.rb

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -679,7 +679,18 @@ def search(args = {})
679679
end
680680

681681
if return_result_set
682-
(!@result.nil? && @result.result_code == 0) ? result_set : nil
682+
unless @result.nil?
683+
case @result.result_code
684+
when ResultStrings.key("Success")
685+
# everything good
686+
result_set
687+
when ResultStrings.key("Size Limit Exceeded")
688+
# LDAP: Size limit exceeded
689+
# This happens when we use size option and results are truncated
690+
# Still we need to return user results
691+
result_set
692+
end
693+
end
683694
else
684695
@result.success?
685696
end

lib/net/ldap/connection.rb

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,45 @@ def close
111111
@conn = nil
112112
end
113113

114+
# Internal: Reads messages by ID from a queue, falling back to reading from
115+
# the connected socket until a message matching the ID is read. Any messages
116+
# with mismatched IDs gets queued for subsequent reads by the origin of that
117+
# message ID.
118+
#
119+
# Returns a Net::LDAP::PDU object or nil.
120+
def queued_read(message_id)
121+
if pdu = message_queue[message_id].shift
122+
return pdu
123+
end
124+
125+
# read messages until we have a match for the given message_id
126+
while pdu = read
127+
if pdu.message_id == message_id
128+
return pdu
129+
else
130+
message_queue[pdu.message_id].push pdu
131+
next
132+
end
133+
end
134+
135+
pdu
136+
end
137+
138+
# Internal: The internal queue of messages, read from the socket, grouped by
139+
# message ID.
140+
#
141+
# Used by `queued_read` to return messages sent by the server with the given
142+
# ID. If no messages are queued for that ID, `queued_read` will `read` from
143+
# the socket and queue messages that don't match the given ID for other
144+
# readers.
145+
#
146+
# Returns the message queue Hash.
147+
def message_queue
148+
@message_queue ||= Hash.new do |hash, key|
149+
hash[key] = []
150+
end
151+
end
152+
114153
# Internal: Reads and parses data from the configured connection.
115154
#
116155
# - syntax: the BER syntax to use to parse the read data with
@@ -146,9 +185,9 @@ def read(syntax = Net::LDAP::AsnSyntax)
146185
#
147186
# Returns the return value from writing to the connection, which in some
148187
# cases is the Integer number of bytes written to the socket.
149-
def write(request, controls = nil)
188+
def write(request, controls = nil, message_id = next_msgid)
150189
instrument "write.net_ldap_connection" do |payload|
151-
packet = [next_msgid.to_ber, request, controls].compact.to_ber_sequence
190+
packet = [message_id.to_ber, request, controls].compact.to_ber_sequence
152191
payload[:content_length] = @conn.write(packet)
153192
end
154193
end
@@ -377,7 +416,10 @@ def search(args = nil)
377416
result_pdu = nil
378417
n_results = 0
379418

419+
message_id = next_msgid
420+
380421
instrument "search.net_ldap_connection",
422+
message_id: message_id,
381423
filter: filter,
382424
base: base,
383425
scope: scope,
@@ -425,12 +467,12 @@ def search(args = nil)
425467
controls << ber_sort if ber_sort
426468
controls = controls.empty? ? nil : controls.to_ber_contextspecific(0)
427469

428-
write(request, controls)
470+
write(request, controls, message_id)
429471

430472
result_pdu = nil
431473
controls = []
432474

433-
while pdu = read
475+
while pdu = queued_read(message_id)
434476
case pdu.app_tag
435477
when Net::LDAP::PDU::SearchReturnedData
436478
n_results += 1
@@ -498,6 +540,16 @@ def search(args = nil)
498540

499541
result_pdu || OpenStruct.new(:status => :failure, :result_code => 1, :message => "Invalid search")
500542
end # instrument
543+
ensure
544+
# clean up message queue for this search
545+
messages = message_queue.delete(message_id)
546+
547+
# in the exceptional case some messages were *not* consumed from the queue,
548+
# instrument the event but do not fail.
549+
unless messages.empty?
550+
instrument "search_messages_unread.net_ldap_connection",
551+
message_id: message_id, messages: messages
552+
end
501553
end
502554

503555
MODIFY_OPERATIONS = { #:nodoc:

test/integration/test_open.rb

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
require_relative '../test_helper'
2+
3+
class TestBindIntegration < LDAPIntegrationTestCase
4+
def test_binds_without_open
5+
events = @service.subscribe "bind.net_ldap_connection"
6+
7+
@ldap.search(filter: "uid=user1", base: "ou=People,dc=rubyldap,dc=com", ignore_server_caps: true)
8+
@ldap.search(filter: "uid=user1", base: "ou=People,dc=rubyldap,dc=com", ignore_server_caps: true)
9+
10+
assert_equal 2, events.size
11+
end
12+
13+
def test_binds_with_open
14+
events = @service.subscribe "bind.net_ldap_connection"
15+
16+
@ldap.open do
17+
@ldap.search(filter: "uid=user1", base: "ou=People,dc=rubyldap,dc=com", ignore_server_caps: true)
18+
@ldap.search(filter: "uid=user1", base: "ou=People,dc=rubyldap,dc=com", ignore_server_caps: true)
19+
end
20+
21+
assert_equal 1, events.size
22+
end
23+
24+
def test_nested_search_without_open
25+
entries = []
26+
nested_entry = nil
27+
28+
@ldap.search(filter: "(|(uid=user1)(uid=user2))", base: "ou=People,dc=rubyldap,dc=com") do |entry|
29+
entries << entry.uid.first
30+
nested_entry ||= @ldap.search(filter: "uid=user3", base: "ou=People,dc=rubyldap,dc=com").first
31+
end
32+
33+
assert_equal "user3", nested_entry.uid.first
34+
assert_equal %w(user1 user2), entries
35+
end
36+
37+
def test_nested_search_with_open
38+
entries = []
39+
nested_entry = nil
40+
41+
@ldap.open do
42+
@ldap.search(filter: "(|(uid=user1)(uid=user2))", base: "ou=People,dc=rubyldap,dc=com") do |entry|
43+
entries << entry.uid.first
44+
nested_entry ||= @ldap.search(filter: "uid=user3", base: "ou=People,dc=rubyldap,dc=com").first
45+
end
46+
end
47+
48+
assert_equal "user3", nested_entry.uid.first
49+
assert_equal %w(user1 user2), entries
50+
end
51+
end

test/integration/test_return_codes.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def test_time_limit_exceeded
2929
end
3030

3131
def test_size_limit_exceeded
32-
refute @ldap.search(filter: "cn=sizeLimitExceeded", base: "ou=Retcodes,dc=rubyldap,dc=com")
32+
@ldap.search(filter: "cn=sizeLimitExceeded", base: "ou=Retcodes,dc=rubyldap,dc=com")
3333
assert result = @ldap.get_operation_result
3434

3535
assert_equal 4, result.code

test/integration/test_search.rb

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,22 @@ def test_search_without_result
2828
def test_search_timeout
2929
events = @service.subscribe "search.net_ldap_connection"
3030

31-
@ldap.search(base: "dc=rubyldap,dc=com", time: 5)
31+
result = @ldap.search(base: "dc=rubyldap,dc=com", time: 5)
3232

3333
payload, result = events.pop
34-
assert_equal 1, payload[:time]
34+
assert_equal 5, payload[:time]
35+
assert_equal entries, result
36+
end
37+
38+
def test_search_with_size
39+
entries = []
40+
41+
result = @ldap.search(filter: "(uid=user1)", base: "dc=rubyldap,dc=com", size: 1) do |entry|
42+
assert_kind_of Net::LDAP::Entry, entry
43+
entries << entry
44+
end
45+
46+
refute entries.empty?
47+
assert_equal entries, result
3548
end
3649
end

test/test_ldap.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,21 @@ def test_instrument_search
4040
assert_equal [entry], payload[:result]
4141
assert_equal "(uid=user1)", payload[:filter]
4242
end
43+
44+
def test_instrument_search_with_size
45+
events = @service.subscribe "search.net_ldap"
46+
47+
flexmock(@connection).should_receive(:bind).and_return(flexmock(:bind_result, :result_code => 0))
48+
flexmock(@connection).should_receive(:search).with(Hash, Proc).
49+
yields(entry = Net::LDAP::Entry.new("uid=user1,ou=users,dc=example,dc=com")).
50+
and_return(flexmock(:search_result, :success? => true, :result_code => 4))
51+
52+
refute_nil @subject.search(:filter => "(uid=user1)", :size => 1)
53+
54+
payload, result = events.pop
55+
assert_equal [entry], result
56+
assert_equal [entry], payload[:result]
57+
assert_equal "(uid=user1)", payload[:filter]
58+
assert_equal result.size, payload[:size]
59+
end
4360
end

test/test_ldap_connection.rb

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -185,20 +185,21 @@ def test_bind_net_ldap_connection_event
185185

186186
def test_search_net_ldap_connection_event
187187
# search data
188-
search_data_ber = Net::BER::BerIdentifiedArray.new([2, [
188+
search_data_ber = Net::BER::BerIdentifiedArray.new([1, [
189189
"uid=user1,ou=People,dc=rubyldap,dc=com",
190190
[ ["uid", ["user1"]] ]
191191
]])
192192
search_data_ber.ber_identifier = Net::LDAP::PDU::SearchReturnedData
193-
search_data = [2, search_data_ber]
193+
search_data = [1, search_data_ber]
194194
# search result (end of results)
195195
search_result_ber = Net::BER::BerIdentifiedArray.new([0, "", ""])
196196
search_result_ber.ber_identifier = Net::LDAP::PDU::SearchResult
197-
search_result = [2, search_result_ber]
197+
search_result = [1, search_result_ber]
198198
@tcp_socket.should_receive(:read_ber).and_return(search_data).
199199
and_return(search_result)
200200

201201
events = @service.subscribe "search.net_ldap_connection"
202+
unread = @service.subscribe "search_messages_unread.net_ldap_connection"
202203

203204
result = @connection.search(filter: "(uid=user1)", base: "ou=People,dc=rubyldap,dc=com")
204205
assert result.success?, "should be success"
@@ -209,5 +210,8 @@ def test_search_net_ldap_connection_event
209210
assert payload.has_key?(:filter)
210211
assert_equal "(uid=user1)", payload[:filter].to_s
211212
assert result
213+
214+
# ensure no unread
215+
assert unread.empty?, "should not have any leftover unread messages"
212216
end
213217
end

0 commit comments

Comments
 (0)