Skip to content
This repository has been archived by the owner on Dec 7, 2018. It is now read-only.

Commit

Permalink
Initial non-blocking connect support
Browse files Browse the repository at this point in the history
  • Loading branch information
tarcieri committed Mar 31, 2012
1 parent c2a5a5e commit b9f9ffa
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 3 deletions.
4 changes: 3 additions & 1 deletion .travis.yml
Expand Up @@ -3,7 +3,9 @@ rvm:
- 1.9.3 - 1.9.3
- ruby-head - ruby-head
- jruby-19mode - jruby-19mode
- jruby-head
# Non-blocking connect is causing deadlocks on jruby-head
# - jruby-head


# See https://github.com/rubinius/rubinius/issues/1611 # See https://github.com/rubinius/rubinius/issues/1611
# - rbx-19mode # - rbx-19mode
4 changes: 4 additions & 0 deletions CHANGES.md
@@ -1,3 +1,7 @@
HEAD
----
* True non-blocking connect support (async DNS support still pending)

0.9.0 0.9.0
----- -----
* TCPServer, TCPSocket, and UDPSocket classes in Celluloid::IO namespace * TCPServer, TCPSocket, and UDPSocket classes in Celluloid::IO namespace
Expand Down
44 changes: 42 additions & 2 deletions lib/celluloid/io/tcp_socket.rb
@@ -1,4 +1,5 @@
require 'socket' require 'socket'
require 'resolv'


module Celluloid module Celluloid
module IO module IO
Expand All @@ -22,8 +23,47 @@ def self.from_ruby_socket(ruby_socket)
# and local_port are specified, then those parameters are used on the # and local_port are specified, then those parameters are used on the
# local end to establish the connection. # local end to establish the connection.
def initialize(remote_host, remote_port, local_host = nil, local_port = nil) def initialize(remote_host, remote_port, local_host = nil, local_port = nil)
# FIXME: not using non-blocking connect # Is it an IPv4 address?
@socket = ::TCPSocket.new(remote_host, remote_port, local_host, local_port) begin
@addr = Resolv::IPv4.create(remote_host)
rescue ArgumentError
end

# Guess it's not IPv4! Is it IPv6?
unless @addr
begin
@addr = Resolv::IPv6.create(remote_host)
rescue ArgumentError
end
end

# Guess it's not an IP address, so let's try DNS
unless @addr
# TODO: suppport asynchronous DNS
# Even EventMachine doesn't do async DNS by default o_O
@addr = Resolv::DNS.new.getaddress(remote_host)
end

case @addr
when Resolv::IPv4
family = Socket::AF_INET
when Resolv::IPv6
family = Socket::AF_INET6
else raise ArgumentError, "unsupported address class: #{@addr.class}"
end

@socket = Socket.new(family, Socket::SOCK_STREAM, 0)
@socket.bind Addrinfo.tcp(local_host, local_port) if local_host

begin
@socket.connect_nonblock Socket.sockaddr_in(remote_port, @addr.to_s)
rescue Errno::EINPROGRESS
wait_writable
retry
rescue Errno::EISCONN
# We're now connected! Yay exceptions for flow control
# NOTE: This is the approach the Ruby stdlib docs suggest ;_;
end
end end


def to_io def to_io
Expand Down

1 comment on commit b9f9ffa

@therealadam
Copy link
Contributor

Choose a reason for hiding this comment

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

YES!

YES!

Please sign in to comment.