socketry / async-io

Concurrent wrappers for native Ruby IO & Sockets.
MIT License
208 stars 28 forks source link

Can't get local address from Endpoint #16

Closed jjyr closed 6 years ago

jjyr commented 6 years ago

For example:

s = TcpServer.new('localhost', 0)
s.local_address.ip_port # => 50038

HostEndpoint missing a method to get local_address.

https://github.com/socketry/async-io/blob/master/lib/async/io/host_endpoint.rb#L63

The annotation while binding addresses to a socket is also incorrect, should get local_address from Socket object: https://github.com/socketry/async-io/blob/master/lib/async/io/socket.rb#L175

ioquatix commented 6 years ago

I'm sorry but it's not clear what the problem is here. Can you give me a more detailed example or a failing spec?

ioquatix commented 6 years ago

Do you mean if the user specifies 0 as a port number, the OS automatically assigns a port number, but that assigned port number is not reported as part of the annotation?

jjyr commented 6 years ago

Yes, and the user cannot get the auto assigned port number from Endpoint object.

ioquatix commented 6 years ago

I will write some specs to demonstrate how to do this.

ioquatix commented 6 years ago

I hope this helps.

Endpoint would never know the actual port that was bound, it's up to socket to know this, via local_address.

One other idea I had a while ago was to make it possible for a socket to bind (initially to port 0) to a port to "reserve" it, then bind a 2nd time to that specific port, but honestly, it's not that trivial, because it is hard to guarantee the same port with both IPv4 and IPv6, there are race conditions, etc. So, it's a dead end IMHO.

jjyr commented 6 years ago

Ok, I use Endpoint#accept to listen to incoming connections, I'll try the #bind method later.

ioquatix commented 6 years ago

Unfortunately unless we provide some callback with the bound socket, it's impossible to use port 0 with #accept.

Endpoint#accept just looks like:

            def accept(backlog = Socket::SOMAXCONN, &block)
                bind do |server|
                    server.listen(backlog)

                    server.accept_each(&block)
                end
            end

So you could easily do this yourself and then do something with the bound port number before calling #accept_each.