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

Improve handling of connection read errors #66

Merged
merged 1 commit into from
Jun 14, 2016

Conversation

jlhawn
Copy link
Contributor

@jlhawn jlhawn commented May 19, 2016

If the reader() goroutine encounters an unexpected error when reading a packet a series of unwinding steps take place:

  • The reader() goroutine shuts down. A deferred function runs which closes the connection.
  • Close() sends a MessageQuit message to the processMessages() goroutine.
  • The processMessages() goroutine shuts down. A deferred function runs which closes the results channels for any pending requests.
  • These pending request handlers receive a nil *PacketResponse because their response channel has been closed. They then return a not-very-helpful error string: "ldap: channel closed".

This patch updates the reader() goroutine to set a closeErr value on the conn when it encounters an unexpected error reading a packet from the server. The processMessages() deferred function checks for closeErr when it is shutting down due to the connection closing and sends this error in the *PacketResponse values to the pending request handlers before closing those results channels. This allows for the error which caused the shutdown to be bubbled up to all pending request calls.

@jlhawn
Copy link
Contributor Author

jlhawn commented May 19, 2016

@flowhamster Could you please take a look? I thought you might have some thoughts on this based on your recent changes in #57.

@jlhawn
Copy link
Contributor Author

jlhawn commented May 19, 2016

As a before/after example, I wrote a simple ldapsearch tool using this library. Here I am attempting to connect on port 636 (the TLS server port) but I try to force StartTLS (which is obviously incompatible):

ldapsearch --host ldap://openldap.dckr.org:636 --start-tls

Before this patch, I get an error which doesn't explain much:

FATA[0000] Unable to connect to LDAP server: unable to perform StartTLS: LDAP Result Code 200 "": ldap: channel closed

But with this patch applied I get much more helpful error message:

FATA[0000] Unable to connect to LDAP server: unable to perform StartTLS: unable to read LDAP response packet: read tcp 172.17.0.2:59256->52.35.216.26:636: read: connection reset by peer

@jlhawn
Copy link
Contributor Author

jlhawn commented Jun 6, 2016

ping @liggitt and @flowhamster

@jamesboswell
Copy link

+1, this looks like a good change to me. ldap: channel closed certainly isn't as helpful as it could be. Is there any concern with adding closeErr to the Conn struct? Backwards breakage?

@@ -330,6 +336,7 @@ func (l *Conn) processMessages() {
_, err := l.conn.Write(buf)
if err != nil {
l.Debug.Printf("Error Sending Message: %s", err.Error())
message.Channel <- &PacketResponse{Error: fmt.Errorf("unable to send request: %s", err)}
Copy link
Contributor

Choose a reason for hiding this comment

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

seems like this channel could get double-notified with this change... should we do this as well?

close(message.Channel)
delete(l.chanResults, message.MessageID)

Copy link
Contributor

Choose a reason for hiding this comment

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

or even wait to add it to l.chanResults until we've written successfully

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks, I've added a close(message.Channel) line below and moved the line which adds the channel to l.chanResults from before writing the packet (above) to after (below).

If the reader() goroutine encounters an unexpected error when reading a packet
a series of unwinding takes place:

- The reader() goroutine shuts down. A deferred function runs which closes the
  connection.
- Close() sends a MessageQuit message to the processMessages() goroutine.
- The processMessages() goroutine shuts down. A deferred function runs which
  closes the results channels for any pending requests.
- These pending request handlers receive a nil *PacketResponse because their
  response channel has been closed. They then return a not-very-helpful error
  string: "ldap: channel closed".

This patch updates the reader() goroutine to set a closeErr value on the conn
when it encounters an unexpected error reading a packet from the server. The
processMessages() deferred function checks for this closeErr when it is
shutting down due to the connection closing and sends this error in the
*PacketResponse values to the pending request handlers *before* closing those
results channels. This allows for the error which caused the shutdown to be
bubbled up to all pending request calls.

Docker-DCO-1.1-Signed-off-by: Josh Hawn <[email protected]> (github: jlhawn)
@jlhawn jlhawn force-pushed the improve_read_error branch from 0639e91 to fb58c8d Compare June 14, 2016 18:21
@liggitt liggitt merged commit 8a8cb05 into go-ldap:master Jun 14, 2016
@liggitt
Copy link
Contributor

liggitt commented Jun 14, 2016

thanks!

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.

3 participants