-
Notifications
You must be signed in to change notification settings - Fork 253
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
DRY up connection handling logic #224
DRY up connection handling logic #224
Conversation
end | ||
|
||
def open_connection(server) | ||
def open_connection(hosts, encryption) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know this is a private class, but why change the method signature for open_connection
? All of the information is available in the previous server
hash.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is mostly to make it explicit what arguments these methods actually use. Passing around a hash makes it necessary to inspect the guts of the method to know what is really needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 that's a good reason, but could you revert that and open it in a follow up PR?
It makes it easier to revisit merged PR's linked from the changelog.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can do that. Which would you like to tackle first, the PR that simplifies parameter handling or this one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your call. Whatever's easier for you.
This sounds like a worthwhile goal, but your PR is also trying to clean up code at the same time. Could you minimize your changeset to focus on this grouping of errors? |
open_connection(server) | ||
hosts = server[:hosts] | ||
hosts = [[server[:host], server[:port]]] if hosts.nil? | ||
open_connection(hosts, server[:encryption]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I should've suggested this in your previous PR, but what do you think of this idea: Instead of having open_connection
worry about handling multiple hosts and then checking whether it did one host, or multiple, we can iterate through server[:hosts]
here. The advantage is that open_connection
would only be responsible for what it's method name suggests: opening a single connection. You can then collect the errors here and handle it in a generic way without the code smell we're discussing below. It'd also minimize remove the changes from open_connection
, which I think make the method less readable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that's a good idea. I'll look into it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After looking into this, I think it won't work out. Most of what open_connection
does in this patch is manage exception dispatching, and it needs to do this because in the multi-host case it must suppress any exceptions raised if a connection is successfully opened. Opening the connection is actually handled by prepare_socket
and TCPSocket.new
, one line in the open_connection
method. The rest of the method is the looping over hosts to try, gathering of exceptions, and finally raising an exception based on the captured exceptions if no connection was opened.
In other words, to do what you suggest moves about 19 lines from open_connection
into initialize
, leaving open_connection
as a one-liner and initialize
rather more bloated. I don't really see how this would address the exception handling code smell either since we're just moving logic around rather than eliminating or refactoring it significantly.
Have you looked at the result of the patch rather than the diffs yet? I think you'll see that the patch is noisy only because it's simplifying the code.
Before I start working on things here, it sounds like you would like to break this into 3 PRs: 1 that changes parameter handling in the |
Just one PR for changing the method signature, and the rest in this PR is fine. |
Cool deal. I'll see what I can do this evening. |
* ConnectionError wraps the creation of several error types for backward compatibility * For now, ConnectionError is only created when more than 1 error is given to the constructor * In the future, ConnectionError should be used even in the single error case
085a622
to
e1a0d13
Compare
@jch I have rewritten the branch for this pull request to hopefully address the main concerns raised regarding exception handling and method signature changes. We can't really escape the code smell of the exception handling here, but it should be easier to clean up in the future if we allow for slightly different exceptions to be raised for the single host connection attempt. |
rescue | ||
# Ensure the connection is closed when requested in the event of an SSL | ||
# setup failure. | ||
@conn.close if close |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems like new behavior. Why this change in this PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sockets created by the library in open_connection
need to be closed in the event that they cannot successfully be configured, such as for encryption. Otherwise, we leak file descriptors and hope that the GC releases them in a timely manner. Not good, especially in the multiple host case where we may fail to configure the sockets for numerous hosts in rapid succession.
This can also be handled within the open_connection
method since that is the only place where we currently set the close
parameter. Now that you bring it up, I think I like this option better, so I'll make this change.
Thanks for picking this up again |
@jch What do you think about these latest changes? |
|
||
if server[:socket] | ||
prepare_socket(server) | ||
else | ||
server[:hosts] = [[server[:host], server[:port]]] if server[:hosts].nil? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: This works even when server[:host]
and/or server[:port]
are nil
. It raises a SocketError
and matches up with the old behavior.
@javanthropus thanks for the ping. I added one last piece of small feedback. We're getting close! |
@jch Sorry for the delay on getting the last changes in. Let me know how this looks. |
@javanthropus great work, thanks! |
=== Net::LDAP 0.12.1 * Whitespace formatting cleanup {#236}[ruby-ldap/ruby-net-ldap#236] * Set operation result if LDAP server is not accessible {#232}[ruby-ldap/ruby-net-ldap#232] === Net::LDAP 0.12.0 * DRY up connection handling logic {#224}[ruby-ldap/ruby-net-ldap#224] * Define auth adapters {#226}[ruby-ldap/ruby-net-ldap#226] * add slash to attribute value filter {#225}[ruby-ldap/ruby-net-ldap#225] * Add the ability to provide a list of hosts for a connection {#223}[ruby-ldap/ruby-net-ldap#223] * Specify the port of LDAP server by giving INTEGRATION_PORT {#221}[ruby-ldap/ruby-net-ldap#221] * Correctly set BerIdentifiedString values to UTF-8 {#212}[ruby-ldap/ruby-net-ldap#212] * Raise Net::LDAP::ConnectionRefusedError when new connection is refused. {#213}[ruby-ldap/ruby-net-ldap#213] * obscure auth password upon #inspect, added test, closes #216 {#217}[ruby-ldap/ruby-net-ldap#217] * Fixing incorrect error class name {#207}[ruby-ldap/ruby-net-ldap#207] * Travis update {#205}[ruby-ldap/ruby-net-ldap#205] * Remove obsolete rbx-19mode from Travis {#204}[ruby-ldap/ruby-net-ldap#204] * mv "sudo" from script/install-openldap to .travis.yml {#199}[ruby-ldap/ruby-net-ldap#199] * Remove meaningless shebang {#200}[ruby-ldap/ruby-net-ldap#200] * Fix Travis CI build {#202}[ruby-ldap/ruby-net-ldap#202] * README.rdoc: fix travis link {#195}[ruby-ldap/ruby-net-ldap#195]
…handling DRY up connection handling logic
This patch simplifies configuration of new connections and removes some duplicate code around setting up encryption. However, it does change the exception messages somewhat. All the same exception types are raised in all the same places, but mostly the messages of underlying exceptions that
Net::LDAP::Error
andNet::LDAP::ConnectionRefusedError
effectively wrap are used directly rather than providing new, different messages. These changes don't break any tests, but it's possible that some user of the library may depend on the specific messages currently produced.The upshot of this change of exception messages will mostly be felt when users provide host lists. If all given hosts fail, each failure message for each host is included in an exception summarizing all of the failures along with the original exception type and host parameters that triggered the failure message. This will certainly help when debugging failures in the multiple host case.