[前][次][番号順一覧][スレッド一覧]

ruby-changes:20857

From: akr <ko1@a...>
Date: Wed, 10 Aug 2011 22:14:06 +0900 (JST)
Subject: [ruby-changes:20857] akr:r32906 (trunk): * ext/socket/lib/socket.rb (Socket.tcp): add :connect_timeout option.

akr	2011-08-10 22:13:57 +0900 (Wed, 10 Aug 2011)

  New Revision: 32906

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=32906

  Log:
    * ext/socket/lib/socket.rb (Socket.tcp): add :connect_timeout option.
      (Addrinfo#connect_from): add :timeout option.
      (Addrinfo#connect): ditto.
      (Addrinfo#connect_to): ditto.

  Modified files:
    trunk/ChangeLog
    trunk/ext/socket/lib/socket.rb

Index: ChangeLog
===================================================================
--- ChangeLog	(revision 32905)
+++ ChangeLog	(revision 32906)
@@ -1,3 +1,10 @@
+Wed Aug 10 22:12:28 2011  Tanaka Akira  <akr@f...>
+
+	* ext/socket/lib/socket.rb (Socket.tcp): add :connect_timeout option.
+	  (Addrinfo#connect_from): add :timeout option.
+	  (Addrinfo#connect): ditto.
+	  (Addrinfo#connect_to): ditto.
+
 Wed Aug 10 21:27:19 2011  Tanaka Akira  <akr@f...>
 
 	* lib/net/pop.rb: fix typo in document.
Index: ext/socket/lib/socket.rb
===================================================================
--- ext/socket/lib/socket.rb	(revision 32905)
+++ ext/socket/lib/socket.rb	(revision 32906)
@@ -31,16 +31,33 @@
 
   # creates a new Socket connected to the address of +local_addrinfo+.
   #
-  # If no arguments are given, the address of the socket is not bound.
+  # If _local_addrinfo_ is nil, the address of the socket is not bound.
   #
+  # The _timeout_ specify the seconds for timeout.
+  # Errno::ETIMEDOUT is raised when timeout occur.
+  #
   # If a block is given the created socket is yielded for each address.
   #
-  def connect_internal(local_addrinfo) # :yields: socket
+  def connect_internal(local_addrinfo, timeout=nil) # :yields: socket
     sock = Socket.new(self.pfamily, self.socktype, self.protocol)
     begin
       sock.ipv6only! if self.ipv6?
       sock.bind local_addrinfo if local_addrinfo
-      sock.connect(self)
+      if timeout
+        begin
+          sock.connect_nonblock(self)
+        rescue IO::WaitWritable
+          if !IO.select(nil, [sock], nil, timeout)
+            raise Errno::ETIMEDOUT, 'user specified timeout'
+          end
+          begin
+            sock.connect_nonblock(self) # check connection failure
+          rescue Errno::EISCONN
+          end
+        end
+      else
+        sock.connect(self)
+      end
       if block_given?
         yield sock
       else
@@ -52,14 +69,23 @@
   end
   private :connect_internal
 
+  # :call-seq:
+  #   addrinfo.connect_from([local_addr_args], [opts]) {|socket| ... }
+  #   addrinfo.connect_from([local_addr_args], [opts])
+  #
   # creates a socket connected to the address of self.
   #
   # If one or more arguments given as _local_addr_args_,
   # it is used as the local address of the socket.
   # _local_addr_args_ is given for family_addrinfo to obtain actual address.
   #
-  # If no arguments given, the local address of the socket is not bound.
+  # If _local_addr_args_ is not given, the local address of the socket is not bound.
   #
+  # The optional last argument _opts_ is options represented by a hash.
+  # _opts_ may have following options:
+  #
+  # [:timeout] specify the timeout in seconds.
+  #
   # If a block is given, it is called with the socket and the value of the block is returned.
   # The socket is returned otherwise.
   #
@@ -74,12 +100,24 @@
   #     puts s.read
   #   }
   #
-  def connect_from(*local_addr_args, &block)
-    connect_internal(family_addrinfo(*local_addr_args), &block)
+  def connect_from(*args, &block)
+    opts = Hash === args.last ? args.pop : {}
+    raise ArgumentError, "wrong number of arguments (#{args.length} for 1)" if 1 < args.length
+    local_addr_args = args
+    connect_internal(family_addrinfo(*local_addr_args), opts[:timeout], &block)
   end
 
+  # :call-seq:
+  #   addrinfo.connect([opts]) {|socket| ... }
+  #   addrinfo.connect([opts])
+  #
   # creates a socket connected to the address of self.
   #
+  # The optional argument _opts_ is options represented by a hash.
+  # _opts_ may have following options:
+  #
+  # [:timeout] specify the timeout in seconds.
+  #
   # If a block is given, it is called with the socket and the value of the block is returned.
   # The socket is returned otherwise.
   #
@@ -88,12 +126,21 @@
   #     puts s.read
   #   }
   #
-  def connect(&block)
-    connect_internal(nil, &block)
+  def connect(opts={}, &block)
+    connect_internal(nil, opts[:timeout], &block)
   end
 
+  # :call-seq:
+  #   addrinfo.connect_to([remote_addr_args], [opts]) {|socket| ... }
+  #   addrinfo.connect_to([remote_addr_args], [opts])
+  #
   # creates a socket connected to _remote_addr_args_ and bound to self.
   #
+  # The optional last argument _opts_ is options represented by a hash.
+  # _opts_ may have following options:
+  #
+  # [:timeout] specify the timeout in seconds.
+  #
   # If a block is given, it is called with the socket and the value of the block is returned.
   # The socket is returned otherwise.
   #
@@ -102,9 +149,12 @@
   #     puts s.read
   #   }
   #
-  def connect_to(*remote_addr_args, &block)
+  def connect_to(*args, &block)
+    opts = Hash === args.last ? args.pop : {}
+    raise ArgumentError, "wrong number of arguments (#{args.length} for 1)" if 1 < args.length
+    remote_addr_args = args
     remote_addrinfo = family_addrinfo(*remote_addr_args)
-    remote_addrinfo.send(:connect_internal, self, &block)
+    remote_addrinfo.send(:connect_internal, self, opts[:timeout], &block)
   end
 
   # creates a socket bound to self.
@@ -216,15 +266,29 @@
     end
   end
 
+  # :call-seq:
+  #   Socket.tcp(host, port, local_host=nil, local_port=nil, [opts]) {|socket| ... }
+  #   Socket.tcp(host, port, local_host=nil, local_port=nil, [opts])
+  #
   # creates a new socket object connected to host:port using TCP/IP.
   #
   # If local_host:local_port is given,
   # the socket is bound to it.
   #
+  # The optional last argument _opts_ is options represented by a hash.
+  # _opts_ may have following options:
+  #
+  # [:connect_timeout] specify the timeout in seconds.
+  #
   # If a block is given, the block is called with the socket.
   # The value of the block is returned.
   # The socket is closed when this method returns.
   #
+  # The optional last argument _opts_ is options represented by a hash.
+  # _opts_ may have following options:
+  #
+  # [:timeout] specify the timeout in seconds.
+  #
   # If no block is given, the socket is returned.
   #
   #   Socket.tcp("www.ruby-lang.org", 80) {|sock|
@@ -233,10 +297,15 @@
   #     puts sock.read
   #   }
   #
-  def self.tcp(host, port, local_host=nil, local_port=nil) # :yield: socket
+  def self.tcp(host, port, *rest) # :yield: socket
+    opts = Hash === rest.last ? rest.pop : {}
+    raise ArgumentError, "wrong number of arguments (#{args.length} for 2)" if 2 < args.length
+    local_host, local_port = rest
     last_error = nil
     ret = nil
 
+    connect_timeout = opts[:connect_timeout]
+
     local_addr_list = nil
     if local_host != nil || local_port != nil
       local_addr_list = Addrinfo.getaddrinfo(local_host, local_port, nil, :STREAM, nil)
@@ -250,7 +319,9 @@
         local_addr = nil
       end
       begin
-        sock = local_addr ? ai.connect_from(local_addr) : ai.connect
+        sock = local_addr ?
+          ai.connect_from(local_addr, :timeout => connect_timeout) :
+          ai.connect(:timeout => connect_timeout)
       rescue SystemCallError
         last_error = $!
         next

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]