@@ -111,6 +111,45 @@ def close
111
111
@conn = nil
112
112
end
113
113
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
+
114
153
# Internal: Reads and parses data from the configured connection.
115
154
#
116
155
# - syntax: the BER syntax to use to parse the read data with
@@ -146,9 +185,9 @@ def read(syntax = Net::LDAP::AsnSyntax)
146
185
#
147
186
# Returns the return value from writing to the connection, which in some
148
187
# 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 )
150
189
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
152
191
payload [ :content_length ] = @conn . write ( packet )
153
192
end
154
193
end
@@ -375,7 +414,10 @@ def search(args = nil)
375
414
result_pdu = nil
376
415
n_results = 0
377
416
417
+ message_id = next_msgid
418
+
378
419
instrument "search.net_ldap_connection" ,
420
+ message_id : message_id ,
379
421
filter : filter ,
380
422
base : base ,
381
423
scope : scope ,
@@ -422,12 +464,12 @@ def search(args = nil)
422
464
controls << ber_sort if ber_sort
423
465
controls = controls . empty? ? nil : controls . to_ber_contextspecific ( 0 )
424
466
425
- write ( request , controls )
467
+ write ( request , controls , message_id )
426
468
427
469
result_pdu = nil
428
470
controls = [ ]
429
471
430
- while pdu = read
472
+ while pdu = queued_read ( message_id )
431
473
case pdu . app_tag
432
474
when Net ::LDAP ::PDU ::SearchReturnedData
433
475
n_results += 1
@@ -495,6 +537,16 @@ def search(args = nil)
495
537
496
538
result_pdu || OpenStruct . new ( :status => :failure , :result_code => 1 , :message => "Invalid search" )
497
539
end # instrument
540
+ ensure
541
+ # clean up message queue for this search
542
+ messages = message_queue . delete ( message_id )
543
+
544
+ # in the exceptional case some messages were *not* consumed from the queue,
545
+ # instrument the event but do not fail.
546
+ unless messages . empty?
547
+ instrument "search_messages_unread.net_ldap_connection" ,
548
+ message_id : message_id , messages : messages
549
+ end
498
550
end
499
551
500
552
MODIFY_OPERATIONS = { #:nodoc:
0 commit comments