ruby-changes:10457
From: akr <ko1@a...>
Date: Tue, 3 Feb 2009 16:26:09 +0900 (JST)
Subject: [ruby-changes:10457] Ruby:r22007 (trunk): * ext/socket/lib/socket.rb (Socket.tcp_server_sockets_port0): new
akr 2009-02-03 16:25:57 +0900 (Tue, 03 Feb 2009) New Revision: 22007 http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=22007 Log: * ext/socket/lib/socket.rb (Socket.tcp_server_sockets_port0): new private function for allocating same port both IPv4 and IPv6. (Socket.tcp_server_sockets): use tcp_server_sockets_port0 for port 0. Modified files: trunk/ChangeLog trunk/ext/socket/lib/socket.rb trunk/test/socket/test_socket.rb Index: ChangeLog =================================================================== --- ChangeLog (revision 22006) +++ ChangeLog (revision 22007) @@ -1,3 +1,9 @@ +Tue Feb 3 16:23:16 2009 Tanaka Akira <akr@f...> + + * ext/socket/lib/socket.rb (Socket.tcp_server_sockets_port0): new + private function for allocating same port both IPv4 and IPv6. + (Socket.tcp_server_sockets): use tcp_server_sockets_port0 for port 0. + Tue Feb 3 14:12:10 2009 Shugo Maeda <shugo@r...> * lib/net/imap.rb: validate data before sending to a server. Index: ext/socket/lib/socket.rb =================================================================== --- ext/socket/lib/socket.rb (revision 22006) +++ ext/socket/lib/socket.rb (revision 22007) @@ -226,11 +226,50 @@ end end + def self.tcp_server_sockets_port0(host) + ai_list = AddrInfo.getaddrinfo(host, 0, nil, :STREAM, nil, Socket::AI_PASSIVE) + begin + sockets = [] + port = nil + ai_list.each {|ai| + s = Socket.new(ai.pfamily, ai.socktype, ai.protocol) + sockets << s + s.ipv6only! if ai.ipv6? + s.setsockopt(:SOCKET, :REUSEADDR, 1) + if !port + s.bind(ai) + port = s.local_address.ip_port + else + s.bind(AddrInfo.tcp(ai.ip_address, port)) + end + s.listen(5) + } + rescue Errno::EADDRINUSE + sockets.each {|s| + s.close + } + retry + end + sockets + ensure + if $! + sockets.each {|s| + s.close if !s.closed? + } + end + end + class << self + private :tcp_server_sockets_port0 + end + # creates TCP server sockets for _host_ and _port_. # _host_ is optional. # # It returns an array of listening sockets. # + # If _port_ is 0, actual port number is choosen dynamically. + # However all sockets in the result has same port number. + # # # tcp_server_sockets returns two sockets. # sockets = Socket.tcp_server_sockets(1296) # p sockets #=> [#<Socket:fd 3>, #<Socket:fd 4>] @@ -240,28 +279,37 @@ # #=> #<AddrInfo: [::]:1296 TCP> # # #<AddrInfo: 0.0.0.0:1296 TCP> # + # # IPv6 and IPv4 socket has same port number, 53114, even if it is choosen dynamically. + # sockets = Socket.tcp_server_sockets(0) + # sockets.each {|s| p s.local_address } + # #=> #<AddrInfo: [::]:53114 TCP> + # # #<AddrInfo: 0.0.0.0:53114 TCP> + # def self.tcp_server_sockets(host=nil, port) - last_error = nil - sockets = [] - AddrInfo.foreach(host, port, nil, :STREAM, nil, Socket::AI_PASSIVE) {|ai| - begin - s = ai.listen - rescue SystemCallError - last_error = $! - next + return tcp_server_sockets_port0(host) if port == 0 + begin + last_error = nil + sockets = [] + AddrInfo.foreach(host, port, nil, :STREAM, nil, Socket::AI_PASSIVE) {|ai| + begin + s = ai.listen + rescue SystemCallError + last_error = $! + next + end + sockets << s + } + if sockets.empty? + raise last_error end - sockets << s - } - if sockets.empty? - raise last_error + sockets + ensure + if $! + sockets.each {|s| + s.close if !s.closed? + } + end end - sockets - ensure - if $! - sockets.each {|s| - s.close if !s.closed? - } - end end # yield socket and client address for each a connection accepted via given sockets. Index: test/socket/test_socket.rb =================================================================== --- test/socket/test_socket.rb (revision 22006) +++ test/socket/test_socket.rb (revision 22007) @@ -111,6 +111,21 @@ end end + def test_tcp_server_sockets_port0 + sockets = Socket.tcp_server_sockets(0) + ports = sockets.map {|s| s.local_address.ip_port } + the_port = ports.first + ports.each {|port| + assert_equal(the_port, port) + } + ensure + if sockets + sockets.each {|s| + s.close + } + end + end + if defined? UNIXSocket def test_unix Dir.mktmpdir {|tmpdir| @@ -144,7 +159,7 @@ } end - def test_accept_loop + def test_accept_loop_with_unix Dir.mktmpdir {|tmpdir| tcp_servers = [] clients = [] @@ -171,4 +186,25 @@ end end + def test_accept_loop + servers = [] + begin + servers = Socket.tcp_server_sockets(0) + port = servers[0].local_address.ip_port + Socket.tcp("localhost", port) {|s1| + Socket.accept_loop(servers) {|s2, client_ai| + begin + assert_equal(s1.local_address.ip_unpack, client_ai.ip_unpack) + ensure + s2.close + end + break + } + } + ensure + servers.each {|s| s.close if !s.closed? } + end + + end + end if defined?(Socket) -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/