@@ -218,6 +218,42 @@ func (s *SearchResult) PrettyPrint(indent int) {
218218 }
219219}
220220
221+ // SearchAsyncResponseType describes the SearchAsyncResponse content type
222+ type SearchAsyncResponseType uint8
223+
224+ const (
225+ SearchAsyncResponseTypeNone SearchAsyncResponseType = iota
226+ SearchAsyncResponseTypeEntry
227+ SearchAsyncResponseTypeReferral
228+ SearchAsyncResponseTypeControl
229+ )
230+
231+ // SearchAsyncResponse holds the server's response message to an async search request
232+ type SearchAsyncResponse struct {
233+ // Type indicates the SearchAsyncResponse type
234+ Type SearchAsyncResponseType
235+ // Entry is the received entry, only set if Type is SearchAsyncResponseTypeEntry
236+ Entry * Entry
237+ // Referral is the received referral, only set if Type is SearchAsyncResponseTypeReferral
238+ Referral string
239+ // Control is the received control, only set if Type is SearchAsyncResponseTypeControl
240+ Control Control
241+ // closed indicates that the request is finished
242+ closed bool
243+ // err holds the encountered error while processing server's response, if any
244+ err error
245+ }
246+
247+ // Closed returns true if the request is finished
248+ func (r * SearchAsyncResponse ) Closed () bool {
249+ return r .closed
250+ }
251+
252+ // Err returns the encountered error while processing server's response, if any
253+ func (r * SearchAsyncResponse ) Err () error {
254+ return r .err
255+ }
256+
221257// SearchRequest represents a search request to send to the server
222258type SearchRequest struct {
223259 BaseDN string
@@ -402,6 +438,59 @@ func (l *Conn) Search(searchRequest *SearchRequest) (*SearchResult, error) {
402438 }
403439}
404440
441+ // SearchAsync performs the given search request asynchronously, it returns a SearchAsyncResponse channel which will be
442+ // closed when the request finished and an error, not nil if the request to the server failed
443+ func (l * Conn ) SearchAsync (searchRequest * SearchRequest ) (<- chan * SearchAsyncResponse , error ) {
444+ msgCtx , err := l .doRequest (searchRequest )
445+ if err != nil {
446+ return nil , err
447+ }
448+ responses := make (chan * SearchAsyncResponse )
449+ go func () {
450+ defer l .finishMessage (msgCtx )
451+ defer close (responses )
452+ for {
453+ packet , err := l .readPacket (msgCtx )
454+ if err != nil {
455+ responses <- & SearchAsyncResponse {closed : true , err : err }
456+ return
457+ }
458+
459+ switch packet .Children [1 ].Tag {
460+ case 4 :
461+ entry := & Entry {
462+ DN : packet .Children [1 ].Children [0 ].Value .(string ),
463+ Attributes : unpackAttributes (packet .Children [1 ].Children [1 ].Children ),
464+ }
465+ responses <- & SearchAsyncResponse {Type : SearchAsyncResponseTypeEntry , Entry : entry }
466+ case 5 :
467+ err := GetLDAPError (packet )
468+ if err != nil {
469+ responses <- & SearchAsyncResponse {closed : true , err : err }
470+ return
471+ }
472+ var response SearchAsyncResponse
473+ if len (packet .Children ) == 3 {
474+ for _ , child := range packet .Children [2 ].Children {
475+ decodedChild , err := DecodeControl (child )
476+ if err != nil {
477+ responses <- & SearchAsyncResponse {closed : true , err : fmt .Errorf ("failed to decode child control: %s" , err )}
478+ return
479+ }
480+ response = SearchAsyncResponse {Type : SearchAsyncResponseTypeControl , Control : decodedChild }
481+ }
482+ }
483+ response .closed = true
484+ responses <- & response
485+ return
486+ case 19 :
487+ responses <- & SearchAsyncResponse {Type : SearchAsyncResponseTypeReferral , Referral : packet .Children [1 ].Children [0 ].Value .(string )}
488+ }
489+ }
490+ }()
491+ return responses , nil
492+ }
493+
405494// unpackAttributes will extract all given LDAP attributes and it's values
406495// from the ber.Packet
407496func unpackAttributes (children []* ber.Packet ) []* EntryAttribute {
0 commit comments