summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog6
-rw-r--r--ext/socket/lib/socket.rb86
-rw-r--r--test/socket/test_socket.rb38
3 files changed, 110 insertions, 20 deletions
diff --git a/ChangeLog b/ChangeLog
index 2b008523f9..8dffe33daf 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+Tue Feb 3 16:23:16 2009 Tanaka Akira <akr@fsij.org>
+
+ * 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@ruby-lang.org>
* lib/net/imap.rb: validate data before sending to a server.
diff --git a/ext/socket/lib/socket.rb b/ext/socket/lib/socket.rb
index 577abca010..c9b70433c1 100644
--- a/ext/socket/lib/socket.rb
+++ b/ext/socket/lib/socket.rb
@@ -226,11 +226,50 @@ class Socket
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,27 +279,36 @@ class Socket
# #=> #<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
- end
- sockets << s
- }
- if sockets.empty?
- raise last_error
- end
- sockets
- ensure
- if $!
- sockets.each {|s|
- s.close if !s.closed?
+ 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
+ ensure
+ if $!
+ sockets.each {|s|
+ s.close if !s.closed?
+ }
+ end
end
end
diff --git a/test/socket/test_socket.rb b/test/socket/test_socket.rb
index d0f401b6f2..6f6bbb62ef 100644
--- a/test/socket/test_socket.rb
+++ b/test/socket/test_socket.rb
@@ -111,6 +111,21 @@ class TestSocket < Test::Unit::TestCase
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 @@ class TestSocket < Test::Unit::TestCase
}
end
- def test_accept_loop
+ def test_accept_loop_with_unix
Dir.mktmpdir {|tmpdir|
tcp_servers = []
clients = []
@@ -171,4 +186,25 @@ class TestSocket < Test::Unit::TestCase
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)