ruby-net-ldap icon indicating copy to clipboard operation
ruby-net-ldap copied to clipboard

support a connection callback for proxies

Open ccutrer opened this issue 10 years ago • 13 comments

also refactors connection establishment to only go through one method

ccutrer avatar Dec 17 '14 21:12 ccutrer

@ccutrer thanks for the pull. I like refactoring for new_connection and use_connection. Can you elaborate more on your use case for the connect callback? What decorated socket object would you return there?

jch avatar Dec 18 '14 01:12 jch

Not a problem. I was just following the instructions from https://github.com/ruby-ldap/ruby-net-ldap/blob/master/Hacking.rdoc

I'm planning on establishing the connection with https://github.com/samuelkadolph/ruby-proxifier.

ccutrer avatar Dec 18 '14 02:12 ccutrer

Sorry for the terse comment. I was writing it, then realized I was late for a short appointment.

I updated the commit to remove the history entry. I plan on using proxifier because in our environment we need to proxy LDAP requests (and LDAP requests only) through a specific server so that clients can whitelist a specific IP address. The application makes many other network requests that need to take the general route over the network for performance reasons, so I need to be able to control LDAP specifically (and without monkey patching all of TCPSocket).

ccutrer avatar Dec 18 '14 02:12 ccutrer

Nice low hanging fruit refactor, I was about to embark on it myself. Well done.

mynameisrufus avatar Dec 18 '14 02:12 mynameisrufus

Not a problem. I was just following the instructions from https://github.com/ruby-ldap/ruby-net-ldap/blob/master/Hacking.rdoc

Hah, oops. Sorry about that, I didn't see that previously.

Great explanation of your reasoning behind the callback. How do you feel about parameterizing the socket passed into connection instead? For example:

Net::LDAP.new(:socket => @proxy_socket, ...)

@mtodd @schaary could you chime in about allowing callers to customize the socket class? It feels pretty low level, but I think the above use case is reasonable. We can also implement this and maintain backwards compatibility.

jch avatar Dec 18 '14 22:12 jch

I had originally planned on just passing the socket through, until I realized that there may be 0 or more connections created by a Net::LDAP - i.e. if open is not used, and you call multiple other methods, a new connection is established each time. Re-using the cached @socket would cause errors on the second connection attempt (or waste resources if a connection is never opened and you just always create a Net::LDAP object before you know if you're going to use it).

ccutrer avatar Dec 18 '14 22:12 ccutrer

@ccutrer good point, but your refactoring of new_connection is now the only entry point for opening a new socket. Wouldn't it be the same as the callback if we saved the passed in socket in an instance variable and use that?

module Net
  class LDAP
    def initialize(args = {})
      @socket = args[:socket]
      # snip other ivars
    end

    def new_connection
      socket = @socket || TCPSocket.new(...)
      Connection.new(:socket => socket, ....)
    end
  end
end

jch avatar Dec 18 '14 22:12 jch

I explored a similar implementation in https://github.com/ruby-ldap/ruby-net-ldap/pull/135 which allowed users to set a connection_class (to wrap Net::LDAP::Connection) and socket_class (to wrap/replace TCPSocket). We decided to scrap that, for the time being.

I'm :-1: to adding a callback, but absolutely support the need for more control over the socket.

The API that comes to mind looks something like this:

ldap = Net::LDAP.new(options)

ldap.open(:socket => TCPSocket.new(ip, port)) do |conn|
  conn.search
end

This would require new_connection to accept a socket instead of requiring a callback to get that value, but it forces users to go through Net::LDAP#open.

I think this is more adaptable, since different socket classes may not have compatible instantiation with TCPSocket (which would be a problem if socket_class were the only configurable option).

Huge :+1: to consolidating connection handling through the new_connection and use_connection APIs.

mtodd avatar Dec 18 '14 23:12 mtodd

@jch - the callback can return a new socket each time it is called

@mtodd - shall I separate out the use_connection refactor into a separate pull request and get that in, and then we can continue the discussion on how to offer better control of the connection process?

ccutrer avatar Dec 18 '14 23:12 ccutrer

shall I separate out the use_connection refactor into a separate pull request and get that in

:+1: to a separate PR

@mtodd I'm skeptical of duplicating all or any of the initialization options in #open. It does address @ccutrer's point about having fine grained control of whether you want a new socket or not.

Taking a step back, I dislike the implicit connection handling in Net::LDAP. I'd prefer Net::LDAP::Connection to be the public interface rather than what's currently in Net::LDAP. But that's outside the scope of this PR.

jch avatar Dec 18 '14 23:12 jch

https://github.com/ruby-ldap/ruby-net-ldap/pull/180 for the refactor; I'll rebase this PR on top of that

ccutrer avatar Dec 18 '14 23:12 ccutrer

@mtodd I'm skeptical of duplicating all or any of the initialization options in #open.

It shouldn't be necessary since Net::LDAP#open should defer to new_connection or use_connection appropriately. It should just merge nicely with the options already set and override only for the scope of that block. Net::LDAP#open feels like a very natural place for this kind of behavior, too.

mtodd avatar Dec 19 '14 01:12 mtodd

Sorry to comment over this old pull request, but I've been having troubles to connect to LDAP through proxy (Nginx + Puma) with ECONNREFUSED error. I don't know why it's taking localhost as default socket, but I want to authenticate to a remote LDAP server and I can't find how is that possible given that it seems impossible to change host and port for connection.

Any advice will be truly appreciated.

colmexdev avatar Mar 01 '23 08:03 colmexdev