Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to set a client-side timeout on requests #57

Merged
merged 2 commits into from
Apr 15, 2016

Conversation

sarahkevinking
Copy link
Contributor

If the Ldap process is stopped or otherwise unresponsive,
Bind and SimpleBind will block waiting for a response.

This commit adds BindWithTimeout and SimpleBindWithTimeout to
give the developer control over the wait time. The Bind and
SimpleBind will return an error after DefaultTimeout.

case res := <-resChan:
return res, nil
case <-time.After(timeout):
return nil, NewError(ErrorNetwork, errors.New("ldap: timeout"))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the connection become unusable in this case? What happens if the server responds after this timeout? What if another operation is in progress at that point?

@liggitt
Copy link
Contributor

liggitt commented Apr 6, 2016

a few general comments

  • Are bind/simplebind special, or does this behavior apply to all LDAP requests (search, etc)
  • do we know what happens to the connection once we start working in multiple goroutines? my guess is the connection becomes unusable if we return while we still have a response from a previous request pending

@sarahkevinking
Copy link
Contributor Author

Thank you for the feedback.

This behavior will apply to any LDAP requests that blocks waiting for a response.

Each message has its own out channel that is stored in a channel map conn.chanResults by MessageID. If a message is received close to the timeout it may be possible for a race condition to occur which I will need to address.

Below is an incomplete outline of the changes I am making:

Create a public method Conn.SetTimeout(time.Duration) to define a timeout that will be used for all requests.

Modify processMessages to create a timeout for all requests.

case MessageRequest:
    // Add to message list and write to network
    l.Debug.Printf("Sending message %d", messagePacket.MessageID)
    l.chanResults[messagePacket.MessageID] = messagePacket.Channel
    go func(messageID int64) {
        _, ok := <- time.After(l.timeout)
        if ok {
            message := &messagePacket{
                Op:        MessageTimeout,
                MessageID: messageID,
            }
            l.sendProcessMessage()
        }
    }(messagePacket.messageID)
    // go routine
    buf := messagePacket.Packet.Bytes()

    _, err := l.conn.Write(buf)
    if err != nil {
        l.Debug.Printf("Error Sending Message: %s", err.Error())
        break
    }

Modify MessageFinish to close the timeout channel to stop the timeout from writing to the message channel.

Remove the BindWithTimeout and SimpleBindWithTimeout methods.

if packet == nil {
return nil, NewError(ErrorNetwork, errors.New("ldap: could not retrieve response"))
packetResponse, ok := <-channel
packet, err = packetResponse.ReadPacket()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will panic if packetResponse is nil

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I take it back, hadn't gotten to the nil receiver impl below... that's nonstandard enough that I think you should always check ok before doing anything with packetResponse

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alternately, don't check ok at all and trust the nil receiver impl of PacketResponse

@liggitt
Copy link
Contributor

liggitt commented Apr 7, 2016

looking really good. can you update the interface in client.go, and is there a way to add a test for this?

@sarahkevinking
Copy link
Contributor Author

I'm controlling my own ldap process in our go tests, but I could test it by creating an ldap.Conn, sending a message, and asserting that the timeout is triggered. Would that suffice?

if err.Error() != "ldap: connection timed out" {
t.Fatalf("unexpected error: %v", err)
}
conn.Close()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

already deferred this above

@liggitt
Copy link
Contributor

liggitt commented Apr 8, 2016

Test looks good, thanks. You can go ahead and squash to a single commit.

@sarahkevinking sarahkevinking force-pushed the master branch 5 times, most recently from 6d4e0a8 to 01788e2 Compare April 8, 2016 20:16
Add SetTimeout to Client API to configure timeout
Add MessageTimeout event handler
Return explicit errors from message handler
if err != nil {
return nil, err
}
if !ok {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check this ok before doing anything with packetResponse

@liggitt
Copy link
Contributor

liggitt commented Apr 10, 2016

since PacketResponse#ReadPacket() that returns an error if the receiver is nil, I'm not sure we need to check for a closed channel in all those places. I think I'd rather just do this:

packetResponse := <-channel
packet, err = packetResponse.ReadPacket()
if err != nil {
  ...
}
...

@sarahkevinking
Copy link
Contributor Author

@liggitt I went ahead and made the change that you asked for. Let me know if there is anything else that you notice before it can be merged in.

@liggitt liggitt merged commit 0e7db8e into go-ldap:master Apr 15, 2016
@liggitt
Copy link
Contributor

liggitt commented Apr 15, 2016

thanks, this looks good

@liggitt liggitt changed the title Apply DefaultTimeout to Bind and SimpleBind Add ability to set a client-side timeout on requests Apr 15, 2016
@jamesboswell
Copy link

jamesboswell commented Apr 24, 2016

@liggitt can this be tagged for gopkg.in ? Being able to set client side timeout would be very useful.

@liggitt
Copy link
Contributor

liggitt commented Apr 25, 2016

Yeah, I was just trying to decide if it was a point release or major version (since changing an interface or adding a public method can break some consumers)

@liggitt
Copy link
Contributor

liggitt commented Apr 25, 2016

And if it was a major version, if there were any other changes we wanted to include while were at it

@liggitt
Copy link
Contributor

liggitt commented May 2, 2016

tagged in v2.3.0

vetinari added a commit to vetinari/ldap that referenced this pull request Jun 9, 2016
hspak added a commit to ripple/ldap that referenced this pull request May 5, 2017
* add ModifyDN() - moddn/modrdn

* Update README

* add ModifyDN() to client

* update docs, changes needed for go-ldap#57

* update to use msgCtx

* fix missing comma, add moddn_test.go with examples

* rename example functions to match the conventions for godoc

* add missing parens
jamesboswell pushed a commit to jamesboswell/ldap that referenced this pull request Dec 21, 2017
liggitt added a commit that referenced this pull request Feb 12, 2018
Update README

update docs, changes needed for #57

add ModifyDN() to client

update to use msgCtx

fix missing comma, add moddn_test.go with examples

rename example functions to match the conventions for godoc

add missing parens
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants