Project

General

Profile

Feature #5101 ยป socket-tcp-connect-timeout-2.patch

akr (Akira Tanaka), 08/10/2011 09:55 PM

View differences:

ext/socket/lib/socket.rb (working copy)
31 31

  
32 32
  # creates a new Socket connected to the address of +local_addrinfo+.
33 33
  #
34
  # If no arguments are given, the address of the socket is not bound.
34
  # If _local_addrinfo_ is nil, the address of the socket is not bound.
35
  #
36
  # The _timeout_ specify the seconds for timeout.
37
  # Errno::ETIMEDOUT is raised when timeout occur.
35 38
  #
36 39
  # If a block is given the created socket is yielded for each address.
37 40
  #
38
  def connect_internal(local_addrinfo) # :yields: socket
41
  def connect_internal(local_addrinfo, timeout=nil) # :yields: socket
39 42
    sock = Socket.new(self.pfamily, self.socktype, self.protocol)
40 43
    begin
41 44
      sock.ipv6only! if self.ipv6?
42 45
      sock.bind local_addrinfo if local_addrinfo
43
      sock.connect(self)
46
      if timeout
47
        begin
48
          sock.connect_nonblock(self)
49
        rescue IO::WaitWritable
50
          if !IO.select(nil, [sock], nil, timeout)
51
            raise Errno::ETIMEDOUT, 'user specified timeout'
52
          end
53
          begin
54
            sock.connect_nonblock(self) # check connection failure
55
          rescue Errno::EISCONN
56
          end
57
        end
58
      else
59
        sock.connect(self)
60
      end
44 61
      if block_given?
45 62
        yield sock
46 63
      else
......
52 69
  end
53 70
  private :connect_internal
54 71

  
72
  # :call-seq:
73
  #   addrinfo.connect_from([local_addr_args], [opts]) {|socket| ... }
74
  #   addrinfo.connect_from([local_addr_args], [opts])
75
  #
55 76
  # creates a socket connected to the address of self.
56 77
  #
57 78
  # If one or more arguments given as _local_addr_args_,
58 79
  # it is used as the local address of the socket.
59 80
  # _local_addr_args_ is given for family_addrinfo to obtain actual address.
60 81
  #
61
  # If no arguments given, the local address of the socket is not bound.
82
  # If _local_addr_args_ is not given, the local address of the socket is not bound.
83
  #
84
  # The optional last argument _opts_ is options represented by a hash.
85
  # _opts_ may have following options:
86
  #
87
  # [:timeout] specify the timeout in seconds.
62 88
  #
63 89
  # If a block is given, it is called with the socket and the value of the block is returned.
64 90
  # The socket is returned otherwise.
......
74 100
  #     puts s.read
75 101
  #   }
76 102
  #
77
  def connect_from(*local_addr_args, &block)
78
    connect_internal(family_addrinfo(*local_addr_args), &block)
103
  def connect_from(*args, &block)
104
    opts = Hash === args.last ? args.pop : {}
105
    raise ArgumentError, "wrong number of arguments (#{args.length} for 1)" if 1 < args.length
106
    local_addr_args = args
107
    connect_internal(family_addrinfo(*local_addr_args), opts[:timeout], &block)
79 108
  end
80 109

  
110
  # :call-seq:
111
  #   addrinfo.connect([opts]) {|socket| ... }
112
  #   addrinfo.connect([opts])
113
  #
81 114
  # creates a socket connected to the address of self.
82 115
  #
116
  # The optional argument _opts_ is options represented by a hash.
117
  # _opts_ may have following options:
118
  #
119
  # [:timeout] specify the timeout in seconds.
120
  #
83 121
  # If a block is given, it is called with the socket and the value of the block is returned.
84 122
  # The socket is returned otherwise.
85 123
  #
......
88 126
  #     puts s.read
89 127
  #   }
90 128
  #
91
  def connect(&block)
92
    connect_internal(nil, &block)
129
  def connect(opts={}, &block)
130
    connect_internal(nil, opts[:timeout], &block)
93 131
  end
94 132

  
133
  # :call-seq:
134
  #   addrinfo.connect_to([remote_addr_args], [opts]) {|socket| ... }
135
  #   addrinfo.connect_to([remote_addr_args], [opts])
136
  #
95 137
  # creates a socket connected to _remote_addr_args_ and bound to self.
96 138
  #
139
  # The optional last argument _opts_ is options represented by a hash.
140
  # _opts_ may have following options:
141
  #
142
  # [:timeout] specify the timeout in seconds.
143
  #
97 144
  # If a block is given, it is called with the socket and the value of the block is returned.
98 145
  # The socket is returned otherwise.
99 146
  #
......
102 149
  #     puts s.read
103 150
  #   }
104 151
  #
105
  def connect_to(*remote_addr_args, &block)
152
  def connect_to(*args, &block)
153
    opts = Hash === args.last ? args.pop : {}
154
    raise ArgumentError, "wrong number of arguments (#{args.length} for 1)" if 1 < args.length
155
    remote_addr_args = args
106 156
    remote_addrinfo = family_addrinfo(*remote_addr_args)
107
    remote_addrinfo.send(:connect_internal, self, &block)
157
    remote_addrinfo.send(:connect_internal, self, opts[:timeout], &block)
108 158
  end
109 159

  
110 160
  # creates a socket bound to self.
......
216 266
    end
217 267
  end
218 268

  
269
  # :call-seq:
270
  #   Socket.tcp(host, port, local_host=nil, local_port=nil, [opts]) {|socket| ... }
271
  #   Socket.tcp(host, port, local_host=nil, local_port=nil, [opts])
272
  #
219 273
  # creates a new socket object connected to host:port using TCP/IP.
220 274
  #
221 275
  # If local_host:local_port is given,
222 276
  # the socket is bound to it.
223 277
  #
278
  # The optional last argument _opts_ is options represented by a hash.
279
  # _opts_ may have following options:
280
  #
281
  # [:connect_timeout] specify the timeout in seconds.
282
  #
224 283
  # If a block is given, the block is called with the socket.
225 284
  # The value of the block is returned.
226 285
  # The socket is closed when this method returns.
227 286
  #
287
  # The optional last argument _opts_ is options represented by a hash.
288
  # _opts_ may have following options:
289
  #
290
  # [:timeout] specify the timeout in seconds.
291
  #
228 292
  # If no block is given, the socket is returned.
229 293
  #
230 294
  #   Socket.tcp("www.ruby-lang.org", 80) {|sock|
......
233 297
  #     puts sock.read
234 298
  #   }
235 299
  #
236
  def self.tcp(host, port, local_host=nil, local_port=nil) # :yield: socket
300
  def self.tcp(host, port, *rest) # :yield: socket
301
    opts = Hash === rest.last ? rest.pop : {}
302
    raise ArgumentError, "wrong number of arguments (#{args.length} for 2)" if 2 < args.length
303
    local_host, local_port = rest
237 304
    last_error = nil
238 305
    ret = nil
239 306

  
307
    connect_timeout = opts[:connect_timeout]
308

  
240 309
    local_addr_list = nil
241 310
    if local_host != nil || local_port != nil
242 311
      local_addr_list = Addrinfo.getaddrinfo(local_host, local_port, nil, :STREAM, nil)
......
250 319
        local_addr = nil
251 320
      end
252 321
      begin
253
        sock = local_addr ? ai.connect_from(local_addr) : ai.connect
322
        sock = local_addr ?
323
          ai.connect_from(local_addr, :timeout => connect_timeout) :
324
          ai.connect(:timeout => connect_timeout)
254 325
      rescue SystemCallError
255 326
        last_error = $!
256 327
        next