PageRenderTime 31ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/ext/socket/lib/socket.rb

https://github.com/thewoolleyman/ruby
Ruby | 729 lines | 396 code | 35 blank | 298 comment | 92 complexity | 7ec2eb240b3ef613b9cbdaf07ed83a2f MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, LGPL-2.1, AGPL-3.0
  1. require 'socket.so'
  2. class Addrinfo
  3. # creates an Addrinfo object from the arguments.
  4. #
  5. # The arguments are interpreted as similar to self.
  6. #
  7. # Addrinfo.tcp("0.0.0.0", 4649).family_addrinfo("www.ruby-lang.org", 80)
  8. # #=> #<Addrinfo: 221.186.184.68:80 TCP (www.ruby-lang.org:80)>
  9. #
  10. # Addrinfo.unix("/tmp/sock").family_addrinfo("/tmp/sock2")
  11. # #=> #<Addrinfo: /tmp/sock2 SOCK_STREAM>
  12. #
  13. def family_addrinfo(*args)
  14. if args.empty?
  15. raise ArgumentError, "no address specified"
  16. elsif Addrinfo === args.first
  17. raise ArgumentError, "too many arguments" if args.length != 1
  18. elsif self.ip?
  19. raise ArgumentError, "IP address needs host and port but #{args.length} arguments given" if args.length != 2
  20. host, port = args
  21. Addrinfo.getaddrinfo(host, port, self.pfamily, self.socktype, self.protocol)[0]
  22. elsif self.unix?
  23. raise ArgumentError, "UNIX socket needs single path argument but #{args.length} arguments given" if args.length != 1
  24. path, = args
  25. Addrinfo.unix(path)
  26. else
  27. raise ArgumentError, "unexpected family"
  28. end
  29. end
  30. def connect_internal(local_addrinfo)
  31. sock = Socket.new(self.pfamily, self.socktype, self.protocol)
  32. begin
  33. sock.ipv6only! if self.ipv6?
  34. sock.bind local_addrinfo if local_addrinfo
  35. sock.connect(self)
  36. if block_given?
  37. yield sock
  38. else
  39. sock
  40. end
  41. ensure
  42. sock.close if !sock.closed? && (block_given? || $!)
  43. end
  44. end
  45. private :connect_internal
  46. # creates a socket connected to the address of self.
  47. #
  48. # If one or more arguments given as _local_addr_args_,
  49. # it is used as the local address of the socket.
  50. # _local_addr_args_ is given for family_addrinfo to obtain actual address.
  51. #
  52. # If no arguments given, the local address of the socket is not bound.
  53. #
  54. # If a block is given, it is called with the socket and the value of the block is returned.
  55. # The socket is returned otherwise.
  56. #
  57. # Addrinfo.tcp("www.ruby-lang.org", 80).connect_from("0.0.0.0", 4649) {|s|
  58. # s.print "GET / HTTP/1.0\r\n\r\n"
  59. # p s.read
  60. # }
  61. #
  62. # # Addrinfo object can be taken for the argument.
  63. # Addrinfo.tcp("www.ruby-lang.org", 80).connect_from(Addrinfo.tcp("0.0.0.0", 4649)) {|s|
  64. # s.print "GET / HTTP/1.0\r\n\r\n"
  65. # p s.read
  66. # }
  67. #
  68. def connect_from(*local_addr_args, &block)
  69. connect_internal(family_addrinfo(*local_addr_args), &block)
  70. end
  71. # creates a socket connected to the address of self.
  72. #
  73. # If a block is given, it is called with the socket and the value of the block is returned.
  74. # The socket is returned otherwise.
  75. #
  76. # Addrinfo.tcp("www.ruby-lang.org", 80).connect {|s|
  77. # s.print "GET / HTTP/1.0\r\n\r\n"
  78. # p s.read
  79. # }
  80. #
  81. def connect(&block)
  82. connect_internal(nil, &block)
  83. end
  84. # creates a socket connected to _remote_addr_args_ and bound to self.
  85. #
  86. # If a block is given, it is called with the socket and the value of the block is returned.
  87. # The socket is returned otherwise.
  88. #
  89. # Addrinfo.tcp("0.0.0.0", 4649).connect_to("www.ruby-lang.org", 80) {|s|
  90. # s.print "GET / HTTP/1.0\r\n\r\n"
  91. # p s.read
  92. # }
  93. #
  94. def connect_to(*remote_addr_args, &block)
  95. remote_addrinfo = family_addrinfo(*remote_addr_args)
  96. remote_addrinfo.send(:connect_internal, self, &block)
  97. end
  98. # creates a socket bound to self.
  99. #
  100. # If a block is given, it is called with the socket and the value of the block is returned.
  101. # The socket is returned otherwise.
  102. #
  103. # Addrinfo.udp("0.0.0.0", 9981).bind {|s|
  104. # s.local_address.connect {|s| s.send "hello", 0 }
  105. # p s.recv(10) #=> "hello"
  106. # }
  107. #
  108. def bind
  109. sock = Socket.new(self.pfamily, self.socktype, self.protocol)
  110. begin
  111. sock.ipv6only! if self.ipv6?
  112. sock.setsockopt(:SOCKET, :REUSEADDR, 1)
  113. sock.bind(self)
  114. if block_given?
  115. yield sock
  116. else
  117. sock
  118. end
  119. ensure
  120. sock.close if !sock.closed? && (block_given? || $!)
  121. end
  122. end
  123. # creates a listening socket bound to self.
  124. def listen(backlog=5)
  125. sock = Socket.new(self.pfamily, self.socktype, self.protocol)
  126. begin
  127. sock.ipv6only! if self.ipv6?
  128. sock.setsockopt(:SOCKET, :REUSEADDR, 1)
  129. sock.bind(self)
  130. sock.listen(backlog)
  131. if block_given?
  132. yield sock
  133. else
  134. sock
  135. end
  136. ensure
  137. sock.close if !sock.closed? && (block_given? || $!)
  138. end
  139. end
  140. # iterates over the list of Addrinfo objects obtained by Addrinfo.getaddrinfo.
  141. #
  142. # Addrinfo.foreach(nil, 80) {|x| p x }
  143. # #=> #<Addrinfo: 127.0.0.1:80 TCP (:80)>
  144. # # #<Addrinfo: 127.0.0.1:80 UDP (:80)>
  145. # # #<Addrinfo: [::1]:80 TCP (:80)>
  146. # # #<Addrinfo: [::1]:80 UDP (:80)>
  147. #
  148. def self.foreach(nodename, service, family=nil, socktype=nil, protocol=nil, flags=nil, &block)
  149. Addrinfo.getaddrinfo(nodename, service, family, socktype, protocol, flags).each(&block)
  150. end
  151. end
  152. class BasicSocket
  153. # Returns an address of the socket suitable for connect in the local machine.
  154. #
  155. # This method returns _self_.local_address, except following condition.
  156. #
  157. # - IPv4 unspecified address (0.0.0.0) is replaced by IPv4 loopback address (127.0.0.1).
  158. # - IPv6 unspecified address (::) is replaced by IPv6 loopback address (::1).
  159. #
  160. # If the local address is not suitable for connect, SocketError is raised.
  161. # IPv4 and IPv6 address which port is 0 is not suitable for connect.
  162. # Unix domain socket which has no path is not suitable for connect.
  163. #
  164. # Addrinfo.tcp("0.0.0.0", 0).listen {|serv|
  165. # p serv.connect_address #=> #<Addrinfo: 127.0.0.1:53660 TCP>
  166. # serv.connect_address.connect {|c|
  167. # s, _ = serv.accept
  168. # p [c, s] #=> [#<Socket:fd 4>, #<Socket:fd 6>]
  169. # }
  170. # }
  171. #
  172. def connect_address
  173. addr = local_address
  174. afamily = addr.afamily
  175. if afamily == Socket::AF_INET
  176. raise SocketError, "unbound IPv4 socket" if addr.ip_port == 0
  177. if addr.ip_address == "0.0.0.0"
  178. addr = Addrinfo.new(["AF_INET", addr.ip_port, nil, "127.0.0.1"], addr.pfamily, addr.socktype, addr.protocol)
  179. end
  180. elsif defined?(Socket::AF_INET6) && afamily == Socket::AF_INET6
  181. raise SocketError, "unbound IPv6 socket" if addr.ip_port == 0
  182. if addr.ip_address == "::"
  183. addr = Addrinfo.new(["AF_INET6", addr.ip_port, nil, "::1"], addr.pfamily, addr.socktype, addr.protocol)
  184. elsif addr.ip_address == "0.0.0.0" # MacOS X 10.4 returns "a.b.c.d" for IPv4-mapped IPv6 address.
  185. addr = Addrinfo.new(["AF_INET6", addr.ip_port, nil, "::1"], addr.pfamily, addr.socktype, addr.protocol)
  186. end
  187. elsif defined?(Socket::AF_UNIX) && afamily == Socket::AF_UNIX
  188. raise SocketError, "unbound Unix socket" if addr.unix_path == ""
  189. end
  190. addr
  191. end
  192. end
  193. class Socket
  194. # enable the socket option IPV6_V6ONLY if IPV6_V6ONLY is available.
  195. def ipv6only!
  196. if defined? Socket::IPV6_V6ONLY
  197. self.setsockopt(:IPV6, :V6ONLY, 1)
  198. end
  199. end
  200. # creates a new socket object connected to host:port using TCP/IP.
  201. #
  202. # If local_host:local_port is given,
  203. # the socket is bound to it.
  204. #
  205. # If a block is given, the block is called with the socket.
  206. # The value of the block is returned.
  207. # The socket is closed when this method returns.
  208. #
  209. # If no block is given, the socket is returned.
  210. #
  211. # Socket.tcp("www.ruby-lang.org", 80) {|sock|
  212. # sock.print "GET / HTTP/1.0\r\n\r\n"
  213. # sock.close_write
  214. # print sock.read
  215. # }
  216. #
  217. def self.tcp(host, port, local_host=nil, local_port=nil) # :yield: socket
  218. last_error = nil
  219. ret = nil
  220. local_addr_list = nil
  221. if local_host != nil || local_port != nil
  222. local_addr_list = Addrinfo.getaddrinfo(local_host, local_port, nil, :STREAM, nil)
  223. end
  224. Addrinfo.foreach(host, port, nil, :STREAM) {|ai|
  225. if local_addr_list
  226. local_addr = local_addr_list.find {|local_ai| local_ai.afamily == ai.afamily }
  227. next if !local_addr
  228. else
  229. local_addr = nil
  230. end
  231. begin
  232. sock = local_addr ? ai.connect_from(local_addr) : ai.connect
  233. rescue SystemCallError
  234. last_error = $!
  235. next
  236. end
  237. ret = sock
  238. break
  239. }
  240. if !ret
  241. if last_error
  242. raise last_error
  243. else
  244. raise SocketError, "no appropriate local address"
  245. end
  246. end
  247. if block_given?
  248. begin
  249. yield ret
  250. ensure
  251. ret.close if !ret.closed?
  252. end
  253. else
  254. ret
  255. end
  256. end
  257. def self.ip_sockets_port0(ai_list, reuseaddr)
  258. begin
  259. sockets = []
  260. port = nil
  261. ai_list.each {|ai|
  262. begin
  263. s = Socket.new(ai.pfamily, ai.socktype, ai.protocol)
  264. rescue SystemCallError
  265. next
  266. end
  267. sockets << s
  268. s.ipv6only! if ai.ipv6?
  269. if reuseaddr
  270. s.setsockopt(:SOCKET, :REUSEADDR, 1)
  271. end
  272. if !port
  273. s.bind(ai)
  274. port = s.local_address.ip_port
  275. else
  276. s.bind(ai.family_addrinfo(ai.ip_address, port))
  277. end
  278. }
  279. rescue Errno::EADDRINUSE
  280. sockets.each {|s|
  281. s.close
  282. }
  283. retry
  284. end
  285. sockets
  286. ensure
  287. sockets.each {|s| s.close if !s.closed? } if $!
  288. end
  289. class << self
  290. private :ip_sockets_port0
  291. end
  292. def self.tcp_server_sockets_port0(host)
  293. ai_list = Addrinfo.getaddrinfo(host, 0, nil, :STREAM, nil, Socket::AI_PASSIVE)
  294. sockets = ip_sockets_port0(ai_list, true)
  295. sockets.each {|s|
  296. s.listen(5)
  297. }
  298. sockets
  299. ensure
  300. sockets.each {|s| s.close if !s.closed? } if $! && sockets
  301. end
  302. class << self
  303. private :tcp_server_sockets_port0
  304. end
  305. # creates TCP/IP server sockets for _host_ and _port_.
  306. # _host_ is optional.
  307. #
  308. # If no block given,
  309. # it returns an array of listening sockets.
  310. #
  311. # If a block is given, the block is called with the sockets.
  312. # The value of the block is returned.
  313. # The socket is closed when this method returns.
  314. #
  315. # If _port_ is 0, actual port number is choosen dynamically.
  316. # However all sockets in the result has same port number.
  317. #
  318. # # tcp_server_sockets returns two sockets.
  319. # sockets = Socket.tcp_server_sockets(1296)
  320. # p sockets #=> [#<Socket:fd 3>, #<Socket:fd 4>]
  321. #
  322. # # The sockets contains IPv6 and IPv4 sockets.
  323. # sockets.each {|s| p s.local_address }
  324. # #=> #<Addrinfo: [::]:1296 TCP>
  325. # # #<Addrinfo: 0.0.0.0:1296 TCP>
  326. #
  327. # # IPv6 and IPv4 socket has same port number, 53114, even if it is choosen dynamically.
  328. # sockets = Socket.tcp_server_sockets(0)
  329. # sockets.each {|s| p s.local_address }
  330. # #=> #<Addrinfo: [::]:53114 TCP>
  331. # # #<Addrinfo: 0.0.0.0:53114 TCP>
  332. #
  333. # # The block is called with the sockets.
  334. # Socket.tcp_server_sockets(0) {|sockets|
  335. # p sockets #=> [#<Socket:fd 3>, #<Socket:fd 4>]
  336. # }
  337. #
  338. def self.tcp_server_sockets(host=nil, port)
  339. if port == 0
  340. sockets = tcp_server_sockets_port0(host)
  341. else
  342. begin
  343. last_error = nil
  344. sockets = []
  345. Addrinfo.foreach(host, port, nil, :STREAM, nil, Socket::AI_PASSIVE) {|ai|
  346. begin
  347. s = ai.listen
  348. rescue SystemCallError
  349. last_error = $!
  350. next
  351. end
  352. sockets << s
  353. }
  354. if sockets.empty?
  355. raise last_error
  356. end
  357. ensure
  358. sockets.each {|s| s.close if !s.closed? } if $!
  359. end
  360. end
  361. if block_given?
  362. begin
  363. yield sockets
  364. ensure
  365. sockets.each {|s| s.close if !s.closed? }
  366. end
  367. else
  368. sockets
  369. end
  370. end
  371. # yield socket and client address for each a connection accepted via given sockets.
  372. #
  373. # The arguments are a list of sockets.
  374. # The individual argument should be a socket or an array of sockets.
  375. #
  376. # This method yields the block sequentially.
  377. # It means that the next connection is not accepted until the block returns.
  378. # So concurrent mechanism, thread for example, should be used to service multiple clients at a time.
  379. #
  380. def self.accept_loop(*sockets) # :yield: socket, client_addrinfo
  381. sockets.flatten!(1)
  382. if sockets.empty?
  383. raise ArgumentError, "no sockets"
  384. end
  385. loop {
  386. readable, _, _ = IO.select(sockets)
  387. readable.each {|r|
  388. begin
  389. sock, addr = r.accept_nonblock
  390. rescue IO::WaitReadable
  391. next
  392. end
  393. yield sock, addr
  394. }
  395. }
  396. end
  397. # creates a TCP/IP server on _port_ and calls the block for each connection accepted.
  398. # The block is called with a socket and a client_address as an Addrinfo object.
  399. #
  400. # If _host_ is specified, it is used with _port_ to determine the server addresses.
  401. #
  402. # The socket is *not* closed when the block returns.
  403. # So application should close it explicitly.
  404. #
  405. # This method calls the block sequentially.
  406. # It means that the next connection is not accepted until the block returns.
  407. # So concurrent mechanism, thread for example, should be used to service multiple clients at a time.
  408. #
  409. # Note that Addrinfo.getaddrinfo is used to determine the server socket addresses.
  410. # When Addrinfo.getaddrinfo returns two or more addresses,
  411. # IPv4 and IPv6 address for example,
  412. # all of them are used.
  413. # Socket.tcp_server_loop succeeds if one socket can be used at least.
  414. #
  415. # # Sequential echo server.
  416. # # It services only one client at a time.
  417. # Socket.tcp_server_loop(16807) {|sock, client_addrinfo|
  418. # begin
  419. # IO.copy_stream(sock, sock)
  420. # ensure
  421. # sock.close
  422. # end
  423. # }
  424. #
  425. # # Threaded echo server
  426. # # It services multiple clients at a time.
  427. # # Note that it may accept connections too much.
  428. # Socket.tcp_server_loop(16807) {|sock, client_addrinfo|
  429. # Thread.new {
  430. # begin
  431. # IO.copy_stream(sock, sock)
  432. # ensure
  433. # sock.close
  434. # end
  435. # }
  436. # }
  437. #
  438. def self.tcp_server_loop(host=nil, port, &b) # :yield: socket, client_addrinfo
  439. tcp_server_sockets(host, port) {|sockets|
  440. accept_loop(sockets, &b)
  441. }
  442. end
  443. # :call-seq:
  444. # Socket.udp_server_sockets([host, ] port)
  445. #
  446. # Creates UDP/IP sockets for a UDP server.
  447. #
  448. # If no block given, it returns an array of sockets.
  449. #
  450. # If a block is given, the block is called with the sockets.
  451. # The value of the block is returned.
  452. # The sockets are closed when this method returns.
  453. #
  454. # If _port_ is zero, some port is choosen.
  455. # But the choosen port is used for the all sockets.
  456. #
  457. # # UDP/IP echo server
  458. # Socket.udp_server_sockets(0) {|sockets|
  459. # p sockets.first.local_address.ip_port #=> 32963
  460. # Socket.udp_server_loop_on(sockets) {|msg, msg_src|
  461. # msg_src.reply msg
  462. # }
  463. # }
  464. #
  465. def self.udp_server_sockets(host=nil, port)
  466. last_error = nil
  467. sockets = []
  468. addr_hash = {}
  469. ipv6_recvpktinfo = nil
  470. if defined? Socket::AncillaryData
  471. if defined? Socket::IPV6_RECVPKTINFO # RFC 3542
  472. ipv6_recvpktinfo = Socket::IPV6_RECVPKTINFO
  473. elsif defined? Socket::IPV6_PKTINFO # RFC 2292
  474. ipv6_recvpktinfo = Socket::IPV6_PKTINFO
  475. end
  476. end
  477. local_addrs = Socket.ip_address_list
  478. ip_list = []
  479. Addrinfo.foreach(host, port, nil, :DGRAM, nil, Socket::AI_PASSIVE) {|ai|
  480. if ai.ipv4? && ai.ip_address == "0.0.0.0"
  481. local_addrs.each {|a|
  482. next if !a.ipv4?
  483. ip_list << Addrinfo.new(a.to_sockaddr, :INET, :DGRAM, 0);
  484. }
  485. elsif ai.ipv6? && ai.ip_address == "::" && !ipv6_recvpktinfo
  486. local_addrs.each {|a|
  487. next if !a.ipv6?
  488. ip_list << Addrinfo.new(a.to_sockaddr, :INET6, :DGRAM, 0);
  489. }
  490. else
  491. ip_list << ai
  492. end
  493. }
  494. if port == 0
  495. sockets = ip_sockets_port0(ip_list, false)
  496. else
  497. ip_list.each {|ip|
  498. ai = Addrinfo.udp(ip.ip_address, port)
  499. begin
  500. s = ai.bind
  501. rescue SystemCallError
  502. last_error = $!
  503. next
  504. end
  505. sockets << s
  506. }
  507. if sockets.empty?
  508. raise last_error
  509. end
  510. end
  511. pktinfo_sockets = {}
  512. sockets.each {|s|
  513. ai = s.local_address
  514. if ipv6_recvpktinfo && ai.ipv6? && ai.ip_address == "::"
  515. s.setsockopt(:IPV6, ipv6_recvpktinfo, 1)
  516. pktinfo_sockets[s] = true
  517. end
  518. }
  519. if block_given?
  520. begin
  521. yield sockets
  522. ensure
  523. sockets.each {|s| s.close if !s.closed? } if sockets
  524. end
  525. else
  526. sockets
  527. end
  528. end
  529. # :call-seq:
  530. # Socket.udp_server_loop_on(sockets) {|msg, msg_src| ... }
  531. #
  532. # Run UDP/IP server loop on the given sockets.
  533. #
  534. # The return value of Socket.udp_server_sockets is appropriate for the argument.
  535. #
  536. # It calls the block for each message received.
  537. #
  538. def self.udp_server_loop_on(sockets) # :yield: msg, msg_src
  539. loop {
  540. readable, _, _ = IO.select(sockets)
  541. readable.each {|r|
  542. begin
  543. msg, sender_addrinfo, rflags, *controls = r.recvmsg_nonblock
  544. rescue IO::WaitReadable
  545. next
  546. end
  547. ai = r.local_address
  548. if ai.ipv6? and pktinfo = controls.find {|c| c.cmsg_is?(:IPV6, :PKTINFO) }
  549. ai = Addrinfo.udp(pktinfo.ipv6_pktinfo_addr.ip_address, ai.ip_port)
  550. yield msg, UDPSource.new(sender_addrinfo, ai) {|reply_msg|
  551. r.sendmsg reply_msg, 0, sender_addrinfo, pktinfo
  552. }
  553. else
  554. yield msg, UDPSource.new(sender_addrinfo, ai) {|reply_msg|
  555. r.send reply_msg, 0, sender_addrinfo
  556. }
  557. end
  558. }
  559. }
  560. end
  561. # :call-seq:
  562. # Socket.udp_server_loop(port) {|msg, msg_src| ... }
  563. # Socket.udp_server_loop(host, port) {|msg, msg_src| ... }
  564. #
  565. # creates a UDP/IP server on _port_ and calls the block for each message arrived.
  566. # The block is called with the message and its source information.
  567. #
  568. # This method allocates sockets internally using _port_.
  569. # If _host_ is specified, it is used conjunction with _port_ to determine the server addresses.
  570. #
  571. # The _msg_ is a string.
  572. #
  573. # The _msg_src_ is a Socket::UDPSource object.
  574. # It is used for reply.
  575. #
  576. # # UDP/IP echo server.
  577. # Socket.udp_server_loop(9261) {|msg, msg_src|
  578. # msg_src.reply msg
  579. # }
  580. #
  581. def self.udp_server_loop(host=nil, port, &b) # :yield: message, message_source
  582. udp_server_sockets(host, port) {|sockets|
  583. udp_server_loop_on(sockets, &b)
  584. }
  585. end
  586. # UDP/IP address information used by Socket.udp_server_loop.
  587. class UDPSource
  588. def initialize(remote_address, local_address, &reply_proc)
  589. @remote_address = remote_address
  590. @local_address = local_address
  591. @reply_proc = reply_proc
  592. end
  593. attr_reader :remote_address, :local_address
  594. def inspect
  595. "\#<#{self.class}: #{@remote_address.inspect_sockaddr} to #{@local_address.inspect_sockaddr}>"
  596. end
  597. def reply(msg)
  598. @reply_proc.call msg
  599. end
  600. end
  601. # creates a new socket connected to path using UNIX socket socket.
  602. #
  603. # If a block is given, the block is called with the socket.
  604. # The value of the block is returned.
  605. # The socket is closed when this method returns.
  606. #
  607. # If no block is given, the socket is returned.
  608. #
  609. # # talk to /tmp/sock socket.
  610. # Socket.unix("/tmp/sock") {|sock|
  611. # t = Thread.new { IO.copy_stream(sock, STDOUT) }
  612. # IO.copy_stream(STDIN, sock)
  613. # t.join
  614. # }
  615. #
  616. def self.unix(path) # :yield: socket
  617. addr = Addrinfo.unix(path)
  618. sock = addr.connect
  619. if block_given?
  620. begin
  621. yield sock
  622. ensure
  623. sock.close if !sock.closed?
  624. end
  625. else
  626. sock
  627. end
  628. end
  629. # creates UNIX server sockets on _path_
  630. #
  631. # If no block given, it returns a listening socket.
  632. #
  633. # If a block is given, it is called with the socket and the block value is returned.
  634. # When the block exits, the socket is closed and the socket file is removed.
  635. #
  636. # socket = Socket.unix_server_socket("/tmp/s")
  637. # p socket #=> #<Socket:fd 3>
  638. # p socket.local_address #=> #<Addrinfo: /tmp/s SOCK_STREAM>
  639. #
  640. # Socket.unix_server_socket("/tmp/sock") {|s|
  641. # p s #=> #<Socket:fd 3>
  642. # p s.local_address #=> # #<Addrinfo: /tmp/sock SOCK_STREAM>
  643. # }
  644. #
  645. def self.unix_server_socket(path)
  646. begin
  647. st = File.lstat(path)
  648. rescue Errno::ENOENT
  649. end
  650. if st && st.socket? && st.owned?
  651. File.unlink path
  652. end
  653. s = Addrinfo.unix(path).listen
  654. if block_given?
  655. begin
  656. yield s
  657. ensure
  658. s.close if !s.closed?
  659. File.unlink path
  660. end
  661. else
  662. s
  663. end
  664. end
  665. # creates a UNIX socket server on _path_.
  666. # It calls the block for each socket accepted.
  667. #
  668. # If _host_ is specified, it is used with _port_ to determine the server ports.
  669. #
  670. # The socket is *not* closed when the block returns.
  671. # So application should close it.
  672. #
  673. # This method deletes the socket file pointed by _path_ at first if
  674. # the file is a socket file and it is owned by the user of the application.
  675. # This is safe only if the directory of _path_ is not changed by a malicious user.
  676. # So don't use /tmp/malicious-users-directory/socket.
  677. # Note that /tmp/socket and /tmp/your-private-directory/socket is safe assuming that /tmp has sticky bit.
  678. #
  679. # # Sequential echo server.
  680. # # It services only one client at a time.
  681. # Socket.unix_server_loop("/tmp/sock") {|sock, client_addrinfo|
  682. # begin
  683. # IO.copy_stream(sock, sock)
  684. # ensure
  685. # sock.close
  686. # end
  687. # }
  688. #
  689. def self.unix_server_loop(path, &b) # :yield: socket, client_addrinfo
  690. unix_server_socket(path) {|serv|
  691. accept_loop(serv, &b)
  692. }
  693. end
  694. end