PageRenderTime 57ms CodeModel.GetById 30ms RepoModel.GetById 1ms app.codeStats 0ms

/test/socket/test_socket.rb

https://github.com/vuxuandung/ruby
Ruby | 551 lines | 503 code | 34 blank | 14 comment | 42 complexity | 4699a0f6493f2fea84dfd2be1962ca04 MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, AGPL-3.0, 0BSD
  1. begin
  2. require "socket"
  3. require "tmpdir"
  4. require "fcntl"
  5. require "test/unit"
  6. rescue LoadError
  7. end
  8. class TestSocket < Test::Unit::TestCase
  9. def test_socket_new
  10. begin
  11. s = Socket.new(:INET, :STREAM)
  12. assert_kind_of(Socket, s)
  13. ensure
  14. s.close
  15. end
  16. end
  17. def test_socket_new_cloexec
  18. return unless defined? Fcntl::FD_CLOEXEC
  19. begin
  20. s = Socket.new(:INET, :STREAM)
  21. assert(s.close_on_exec?)
  22. ensure
  23. s.close
  24. end
  25. end
  26. def test_unpack_sockaddr
  27. sockaddr_in = Socket.sockaddr_in(80, "")
  28. assert_raise(ArgumentError) { Socket.unpack_sockaddr_un(sockaddr_in) }
  29. sockaddr_un = Socket.sockaddr_un("/testdir/s")
  30. assert_raise(ArgumentError) { Socket.unpack_sockaddr_in(sockaddr_un) }
  31. assert_raise(ArgumentError) { Socket.unpack_sockaddr_in("") }
  32. assert_raise(ArgumentError) { Socket.unpack_sockaddr_un("") }
  33. end if Socket.respond_to?(:sockaddr_un)
  34. def test_sysaccept
  35. serv = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
  36. serv.bind(Socket.sockaddr_in(0, "127.0.0.1"))
  37. serv.listen 5
  38. c = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
  39. c.connect(serv.getsockname)
  40. fd, peeraddr = serv.sysaccept
  41. assert_equal(c.getsockname, peeraddr.to_sockaddr)
  42. ensure
  43. serv.close if serv
  44. c.close if c
  45. IO.for_fd(fd).close if fd
  46. end
  47. def test_initialize
  48. Socket.open(Socket::AF_INET, Socket::SOCK_STREAM, 0) {|s|
  49. s.bind(Socket.sockaddr_in(0, "127.0.0.1"))
  50. addr = s.getsockname
  51. assert_nothing_raised { Socket.unpack_sockaddr_in(addr) }
  52. assert_raise(ArgumentError, NoMethodError) { Socket.unpack_sockaddr_un(addr) }
  53. }
  54. Socket.open("AF_INET", "SOCK_STREAM", 0) {|s|
  55. s.bind(Socket.sockaddr_in(0, "127.0.0.1"))
  56. addr = s.getsockname
  57. assert_nothing_raised { Socket.unpack_sockaddr_in(addr) }
  58. assert_raise(ArgumentError, NoMethodError) { Socket.unpack_sockaddr_un(addr) }
  59. }
  60. Socket.open(:AF_INET, :SOCK_STREAM, 0) {|s|
  61. s.bind(Socket.sockaddr_in(0, "127.0.0.1"))
  62. addr = s.getsockname
  63. assert_nothing_raised { Socket.unpack_sockaddr_in(addr) }
  64. assert_raise(ArgumentError, NoMethodError) { Socket.unpack_sockaddr_un(addr) }
  65. }
  66. end
  67. def test_getaddrinfo
  68. # This should not send a DNS query because AF_UNIX.
  69. assert_raise(SocketError) { Socket.getaddrinfo("www.kame.net", 80, "AF_UNIX") }
  70. end
  71. def test_getaddrinfo_raises_no_errors_on_port_argument_of_0 # [ruby-core:29427]
  72. assert_nothing_raised('[ruby-core:29427]'){ Socket.getaddrinfo('localhost', 0, Socket::AF_INET, Socket::SOCK_STREAM, nil, Socket::AI_CANONNAME) }
  73. assert_nothing_raised('[ruby-core:29427]'){ Socket.getaddrinfo('localhost', '0', Socket::AF_INET, Socket::SOCK_STREAM, nil, Socket::AI_CANONNAME) }
  74. assert_nothing_raised('[ruby-core:29427]'){ Socket.getaddrinfo('localhost', '00', Socket::AF_INET, Socket::SOCK_STREAM, nil, Socket::AI_CANONNAME) }
  75. assert_raise(SocketError, '[ruby-core:29427]'){ Socket.getaddrinfo(nil, nil, Socket::AF_INET, Socket::SOCK_STREAM, nil, Socket::AI_CANONNAME) }
  76. assert_nothing_raised('[ruby-core:29427]'){ TCPServer.open('localhost', 0) {} }
  77. end
  78. def test_getnameinfo
  79. assert_raise(SocketError) { Socket.getnameinfo(["AF_UNIX", 80, "0.0.0.0"]) }
  80. end
  81. def test_ip_address_list
  82. begin
  83. list = Socket.ip_address_list
  84. rescue NotImplementedError
  85. return
  86. end
  87. list.each {|ai|
  88. assert_instance_of(Addrinfo, ai)
  89. assert(ai.ip?)
  90. }
  91. end
  92. def test_tcp
  93. TCPServer.open(0) {|serv|
  94. addr = serv.connect_address
  95. addr.connect {|s1|
  96. s2 = serv.accept
  97. begin
  98. assert_equal(s2.remote_address.ip_unpack, s1.local_address.ip_unpack)
  99. ensure
  100. s2.close
  101. end
  102. }
  103. }
  104. end
  105. def test_tcp_cloexec
  106. return unless defined? Fcntl::FD_CLOEXEC
  107. TCPServer.open(0) {|serv|
  108. addr = serv.connect_address
  109. addr.connect {|s1|
  110. s2 = serv.accept
  111. begin
  112. assert(s2.close_on_exec?)
  113. ensure
  114. s2.close
  115. end
  116. }
  117. }
  118. end
  119. def random_port
  120. # IANA suggests dynamic port for 49152 to 65535
  121. # http://www.iana.org/assignments/port-numbers
  122. 49152 + rand(65535-49152+1)
  123. end
  124. def errors_addrinuse
  125. [Errno::EADDRINUSE]
  126. end
  127. def test_tcp_server_sockets
  128. port = random_port
  129. begin
  130. sockets = Socket.tcp_server_sockets(port)
  131. rescue *errors_addrinuse
  132. return # not test failure
  133. end
  134. begin
  135. sockets.each {|s|
  136. assert_equal(port, s.local_address.ip_port)
  137. }
  138. ensure
  139. sockets.each {|s|
  140. s.close
  141. }
  142. end
  143. end
  144. def test_tcp_server_sockets_port0
  145. sockets = Socket.tcp_server_sockets(0)
  146. ports = sockets.map {|s| s.local_address.ip_port }
  147. the_port = ports.first
  148. ports.each {|port|
  149. assert_equal(the_port, port)
  150. }
  151. ensure
  152. if sockets
  153. sockets.each {|s|
  154. s.close
  155. }
  156. end
  157. end
  158. if defined? UNIXSocket
  159. def test_unix
  160. Dir.mktmpdir {|tmpdir|
  161. path = "#{tmpdir}/sock"
  162. UNIXServer.open(path) {|serv|
  163. Socket.unix(path) {|s1|
  164. s2 = serv.accept
  165. begin
  166. s2raddr = s2.remote_address
  167. s1laddr = s1.local_address
  168. assert(s2raddr.to_sockaddr.empty? ||
  169. s1laddr.to_sockaddr.empty? ||
  170. s2raddr.unix_path == s1laddr.unix_path)
  171. assert(s2.close_on_exec?)
  172. ensure
  173. s2.close
  174. end
  175. }
  176. }
  177. }
  178. end
  179. def test_unix_server_socket
  180. Dir.mktmpdir {|tmpdir|
  181. path = "#{tmpdir}/sock"
  182. 2.times {
  183. serv = Socket.unix_server_socket(path)
  184. begin
  185. assert_kind_of(Socket, serv)
  186. assert(File.socket?(path))
  187. assert_equal(path, serv.local_address.unix_path)
  188. ensure
  189. serv.close
  190. end
  191. }
  192. }
  193. end
  194. def test_accept_loop_with_unix
  195. Dir.mktmpdir {|tmpdir|
  196. tcp_servers = []
  197. clients = []
  198. accepted = []
  199. begin
  200. tcp_servers = Socket.tcp_server_sockets(0)
  201. unix_server = Socket.unix_server_socket("#{tmpdir}/sock")
  202. tcp_servers.each {|s|
  203. addr = s.connect_address
  204. assert_nothing_raised("connect to #{addr.inspect}") {
  205. clients << addr.connect
  206. }
  207. }
  208. addr = unix_server.connect_address
  209. assert_nothing_raised("connect to #{addr.inspect}") {
  210. clients << addr.connect
  211. }
  212. Socket.accept_loop(tcp_servers, unix_server) {|s|
  213. accepted << s
  214. break if clients.length == accepted.length
  215. }
  216. assert_equal(clients.length, accepted.length)
  217. ensure
  218. tcp_servers.each {|s| s.close if !s.closed? }
  219. unix_server.close if unix_server && !unix_server.closed?
  220. clients.each {|s| s.close if !s.closed? }
  221. accepted.each {|s| s.close if !s.closed? }
  222. end
  223. }
  224. end
  225. end
  226. def test_accept_loop
  227. servers = []
  228. begin
  229. servers = Socket.tcp_server_sockets(0)
  230. port = servers[0].local_address.ip_port
  231. Socket.tcp("localhost", port) {|s1|
  232. Socket.accept_loop(servers) {|s2, client_ai|
  233. begin
  234. assert_equal(s1.local_address.ip_unpack, client_ai.ip_unpack)
  235. ensure
  236. s2.close
  237. end
  238. break
  239. }
  240. }
  241. ensure
  242. servers.each {|s| s.close if !s.closed? }
  243. end
  244. end
  245. def test_accept_loop_multi_port
  246. servers = []
  247. begin
  248. servers = Socket.tcp_server_sockets(0)
  249. port = servers[0].local_address.ip_port
  250. servers2 = Socket.tcp_server_sockets(0)
  251. servers.concat servers2
  252. port2 = servers2[0].local_address.ip_port
  253. Socket.tcp("localhost", port) {|s1|
  254. Socket.accept_loop(servers) {|s2, client_ai|
  255. begin
  256. assert_equal(s1.local_address.ip_unpack, client_ai.ip_unpack)
  257. ensure
  258. s2.close
  259. end
  260. break
  261. }
  262. }
  263. Socket.tcp("localhost", port2) {|s1|
  264. Socket.accept_loop(servers) {|s2, client_ai|
  265. begin
  266. assert_equal(s1.local_address.ip_unpack, client_ai.ip_unpack)
  267. ensure
  268. s2.close
  269. end
  270. break
  271. }
  272. }
  273. ensure
  274. servers.each {|s| s.close if !s.closed? }
  275. end
  276. end
  277. def test_udp_server
  278. begin
  279. ip_addrs = Socket.ip_address_list
  280. rescue NotImplementedError
  281. skip "Socket.ip_address_list not implemented"
  282. end
  283. ifconfig = nil
  284. Socket.udp_server_sockets(0) {|sockets|
  285. famlies = {}
  286. sockets.each {|s| famlies[s.local_address.afamily] = s }
  287. nd6 = {}
  288. ip_addrs.reject! {|ai|
  289. s = famlies[ai.afamily]
  290. next true unless s
  291. case RUBY_PLATFORM
  292. when /linux/
  293. if ai.ip_address.include?('%') and
  294. (`uname -r`[/[0-9.]+/].split('.').map(&:to_i) <=> [2,6,18]) <= 0
  295. # Cent OS 5.6 (2.6.18-238.19.1.el5xen) doesn't correctly work
  296. # sendmsg with pktinfo for link-local ipv6 addresses
  297. next true
  298. end
  299. when /freebsd/
  300. if ifr_name = ai.ip_address[/%(.*)/, 1]
  301. # FreeBSD 9.0 with default setting (ipv6_activate_all_interfaces
  302. # is not YES) sets IFDISABLED to interfaces which don't have
  303. # global IPv6 address.
  304. # Link-local IPv6 addresses on those interfaces don't work.
  305. ulSIOCGIFINFO_IN6 = 3225971052
  306. ulND6_IFF_IFDISABLED = 8
  307. in6_ondireq = ifr_name
  308. s.ioctl(ulSIOCGIFINFO_IN6, in6_ondireq)
  309. flag = in6_ondireq.unpack('A16L6').last
  310. next true if flag & ulND6_IFF_IFDISABLED != 0
  311. nd6[ai] = flag
  312. end
  313. when /darwin/
  314. if !ai.ipv6?
  315. elsif ai.ipv6_unique_local? && /darwin1[01]\./ =~ RUBY_PLATFORM
  316. next true # iCloud addresses do not work, see Bug #6692
  317. elsif ifr_name = ai.ip_address[/%(.*)/, 1]
  318. # Mac OS X may sets IFDISABLED as FreeBSD does
  319. ulSIOCGIFFLAGS = 3223349521
  320. ulSIOCGIFINFO_IN6 = 3224398156
  321. ulSIOCGIFAFLAG_IN6 = 3240126793
  322. ulIFF_POINTOPOINT = 0x10
  323. ulND6_IFF_IFDISABLED = 8
  324. in6_ondireq = ifr_name
  325. s.ioctl(ulSIOCGIFINFO_IN6, in6_ondireq)
  326. flag = in6_ondireq.unpack('A16L6').last
  327. next true if (flag & ulND6_IFF_IFDISABLED) != 0
  328. nd6[ai] = flag
  329. in6_ifreq = [ifr_name,ai.to_sockaddr].pack('a16A*')
  330. s.ioctl(ulSIOCGIFFLAGS, in6_ifreq)
  331. next true if in6_ifreq.unpack('A16L1').last & ulIFF_POINTOPOINT != 0
  332. else
  333. ifconfig ||= `/sbin/ifconfig`
  334. next true if ifconfig.scan(/^(\w+):(.*(?:\n\t.*)*)/).find do|ifname, value|
  335. value.include?(ai.ip_address) && value.include?('POINTOPOINT')
  336. end
  337. end
  338. end
  339. false
  340. }
  341. skipped = false
  342. begin
  343. port = sockets.first.local_address.ip_port
  344. ping_p = false
  345. th = Thread.new {
  346. Socket.udp_server_loop_on(sockets) {|msg, msg_src|
  347. break if msg == "exit"
  348. rmsg = Marshal.dump([msg, msg_src.remote_address, msg_src.local_address])
  349. ping_p = true
  350. msg_src.reply rmsg
  351. }
  352. }
  353. ip_addrs.each {|ai|
  354. Addrinfo.udp(ai.ip_address, port).connect {|s|
  355. ping_p = false
  356. msg1 = "<<<#{ai.inspect}>>>"
  357. s.sendmsg msg1
  358. unless IO.select([s], nil, nil, 10)
  359. nd6options = nd6.key?(ai) ? "nd6=%x " % nd6[ai] : ''
  360. raise "no response from #{ai.inspect} #{nd6options}ping=#{ping_p}"
  361. end
  362. msg2, addr = s.recvmsg
  363. msg2, remote_address, local_address = Marshal.load(msg2)
  364. assert_equal(msg1, msg2)
  365. assert_equal(ai.ip_address, addr.ip_address)
  366. }
  367. }
  368. rescue NotImplementedError, Errno::ENOSYS
  369. skipped = true
  370. skip "need sendmsg and recvmsg"
  371. ensure
  372. if th
  373. if skipped
  374. Thread.kill th unless th.join(10)
  375. else
  376. Addrinfo.udp("127.0.0.1", port).connect {|s| s.sendmsg "exit" }
  377. unless th.join(10)
  378. Thread.kill th
  379. th.join(10)
  380. raise "thread killed"
  381. end
  382. end
  383. end
  384. end
  385. }
  386. end
  387. def test_linger
  388. opt = Socket::Option.linger(true, 0)
  389. assert_equal([true, 0], opt.linger)
  390. Addrinfo.tcp("127.0.0.1", 0).listen {|serv|
  391. serv.local_address.connect {|s1|
  392. s2, _ = serv.accept
  393. begin
  394. s1.setsockopt(opt)
  395. s1.close
  396. assert_raise(Errno::ECONNRESET) { s2.read }
  397. ensure
  398. s2.close
  399. end
  400. }
  401. }
  402. end
  403. def test_timestamp
  404. return if /linux|freebsd|netbsd|openbsd|solaris|darwin/ !~ RUBY_PLATFORM
  405. return if !defined?(Socket::AncillaryData)
  406. t1 = Time.now.strftime("%Y-%m-%d")
  407. stamp = nil
  408. Addrinfo.udp("127.0.0.1", 0).bind {|s1|
  409. Addrinfo.udp("127.0.0.1", 0).bind {|s2|
  410. s1.setsockopt(:SOCKET, :TIMESTAMP, true)
  411. s2.send "a", 0, s1.local_address
  412. msg, addr, rflags, stamp = s1.recvmsg
  413. assert_equal("a", msg)
  414. assert(stamp.cmsg_is?(:SOCKET, :TIMESTAMP))
  415. }
  416. }
  417. t2 = Time.now.strftime("%Y-%m-%d")
  418. pat = Regexp.union([t1, t2].uniq)
  419. assert_match(pat, stamp.inspect)
  420. t = stamp.timestamp
  421. assert_match(pat, t.strftime("%Y-%m-%d"))
  422. pat = /\.#{"%06d" % t.usec}/
  423. assert_match(pat, stamp.inspect)
  424. end
  425. def test_timestampns
  426. return if /linux/ !~ RUBY_PLATFORM || !defined?(Socket::SO_TIMESTAMPNS)
  427. t1 = Time.now.strftime("%Y-%m-%d")
  428. stamp = nil
  429. Addrinfo.udp("127.0.0.1", 0).bind {|s1|
  430. Addrinfo.udp("127.0.0.1", 0).bind {|s2|
  431. begin
  432. s1.setsockopt(:SOCKET, :TIMESTAMPNS, true)
  433. rescue Errno::ENOPROTOOPT
  434. # SO_TIMESTAMPNS is available since Linux 2.6.22
  435. return
  436. end
  437. s2.send "a", 0, s1.local_address
  438. msg, addr, rflags, stamp = s1.recvmsg
  439. assert_equal("a", msg)
  440. assert(stamp.cmsg_is?(:SOCKET, :TIMESTAMPNS))
  441. }
  442. }
  443. t2 = Time.now.strftime("%Y-%m-%d")
  444. pat = Regexp.union([t1, t2].uniq)
  445. assert_match(pat, stamp.inspect)
  446. t = stamp.timestamp
  447. assert_match(pat, t.strftime("%Y-%m-%d"))
  448. pat = /\.#{"%09d" % t.nsec}/
  449. assert_match(pat, stamp.inspect)
  450. end
  451. def test_bintime
  452. return if /freebsd/ !~ RUBY_PLATFORM
  453. t1 = Time.now.strftime("%Y-%m-%d")
  454. stamp = nil
  455. Addrinfo.udp("127.0.0.1", 0).bind {|s1|
  456. Addrinfo.udp("127.0.0.1", 0).bind {|s2|
  457. s1.setsockopt(:SOCKET, :BINTIME, true)
  458. s2.send "a", 0, s1.local_address
  459. msg, addr, rflags, stamp = s1.recvmsg
  460. assert_equal("a", msg)
  461. assert(stamp.cmsg_is?(:SOCKET, :BINTIME))
  462. }
  463. }
  464. t2 = Time.now.strftime("%Y-%m-%d")
  465. pat = Regexp.union([t1, t2].uniq)
  466. assert_match(pat, stamp.inspect)
  467. t = stamp.timestamp
  468. assert_match(pat, t.strftime("%Y-%m-%d"))
  469. assert_equal(stamp.data[-8,8].unpack("Q")[0], t.subsec * 2**64)
  470. end
  471. def test_closed_read
  472. require 'timeout'
  473. require 'socket'
  474. bug4390 = '[ruby-core:35203]'
  475. server = TCPServer.new("localhost", 0)
  476. serv_thread = Thread.new {server.accept}
  477. begin sleep(0.1) end until serv_thread.stop?
  478. sock = TCPSocket.new("localhost", server.addr[1])
  479. client_thread = Thread.new do
  480. sock.readline
  481. end
  482. begin sleep(0.1) end until client_thread.stop?
  483. Timeout.timeout(1) do
  484. sock.close
  485. sock = nil
  486. assert_raise(IOError, bug4390) {client_thread.join}
  487. end
  488. ensure
  489. server.close
  490. end
  491. def test_connect_timeout
  492. host = "127.0.0.1"
  493. server = TCPServer.new(host, 0)
  494. port = server.addr[1]
  495. serv_thread = Thread.new {server.accept}
  496. sock = Socket.tcp(host, port, :connect_timeout => 30)
  497. accepted = serv_thread.value
  498. assert_kind_of TCPSocket, accepted
  499. assert_equal sock, IO.select(nil, [ sock ])[1][0], "not writable"
  500. sock.close
  501. # some platforms may not timeout when the listener queue overflows,
  502. # but we know Linux does with the default listen backlog of SOMAXCONN for
  503. # TCPServer.
  504. assert_raises(Errno::ETIMEDOUT) do
  505. (Socket::SOMAXCONN*2).times do |i|
  506. sock = Socket.tcp(host, port, :connect_timeout => 0)
  507. assert_equal sock, IO.select(nil, [ sock ])[1][0],
  508. "not writable (#{i})"
  509. sock.close
  510. end
  511. end if RUBY_PLATFORM =~ /linux/
  512. ensure
  513. server.close
  514. accepted.close if accepted
  515. sock.close if sock && ! sock.closed?
  516. end
  517. end if defined?(Socket)