cool.io 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. data/.gitignore +25 -0
  2. data/CHANGES +199 -0
  3. data/LICENSE +20 -0
  4. data/README.markdown +4 -0
  5. data/Rakefile +98 -0
  6. data/VERSION +1 -0
  7. data/examples/echo_client.rb +38 -0
  8. data/examples/echo_server.rb +27 -0
  9. data/examples/google.rb +9 -0
  10. data/examples/httpclient.rb +38 -0
  11. data/ext/cool.io/.gitignore +5 -0
  12. data/ext/cool.io/cool.io.h +58 -0
  13. data/ext/cool.io/cool.io_ext.c +25 -0
  14. data/ext/cool.io/ev_wrap.h +8 -0
  15. data/ext/cool.io/extconf.rb +69 -0
  16. data/ext/cool.io/iowatcher.c +189 -0
  17. data/ext/cool.io/libev.c +8 -0
  18. data/ext/cool.io/loop.c +303 -0
  19. data/ext/cool.io/stat_watcher.c +191 -0
  20. data/ext/cool.io/timer_watcher.c +219 -0
  21. data/ext/cool.io/utils.c +122 -0
  22. data/ext/cool.io/watcher.c +264 -0
  23. data/ext/cool.io/watcher.h +71 -0
  24. data/ext/http11_client/.gitignore +5 -0
  25. data/ext/http11_client/ext_help.h +14 -0
  26. data/ext/http11_client/extconf.rb +6 -0
  27. data/ext/http11_client/http11_client.c +300 -0
  28. data/ext/http11_client/http11_parser.c +403 -0
  29. data/ext/http11_client/http11_parser.h +48 -0
  30. data/ext/http11_client/http11_parser.rl +173 -0
  31. data/ext/libev/Changes +364 -0
  32. data/ext/libev/LICENSE +36 -0
  33. data/ext/libev/README +58 -0
  34. data/ext/libev/README.embed +3 -0
  35. data/ext/libev/ev.c +3867 -0
  36. data/ext/libev/ev.h +826 -0
  37. data/ext/libev/ev_epoll.c +234 -0
  38. data/ext/libev/ev_kqueue.c +198 -0
  39. data/ext/libev/ev_poll.c +148 -0
  40. data/ext/libev/ev_port.c +164 -0
  41. data/ext/libev/ev_select.c +307 -0
  42. data/ext/libev/ev_vars.h +197 -0
  43. data/ext/libev/ev_win32.c +153 -0
  44. data/ext/libev/ev_wrap.h +186 -0
  45. data/ext/libev/test_libev_win32.c +123 -0
  46. data/ext/libev/update_ev_wrap +19 -0
  47. data/lib/.gitignore +2 -0
  48. data/lib/cool.io.rb +30 -0
  49. data/lib/cool.io/async_watcher.rb +43 -0
  50. data/lib/cool.io/dns_resolver.rb +220 -0
  51. data/lib/cool.io/eventmachine.rb +234 -0
  52. data/lib/cool.io/http_client.rb +419 -0
  53. data/lib/cool.io/io.rb +174 -0
  54. data/lib/cool.io/iowatcher.rb +17 -0
  55. data/lib/cool.io/listener.rb +93 -0
  56. data/lib/cool.io/loop.rb +130 -0
  57. data/lib/cool.io/meta.rb +49 -0
  58. data/lib/cool.io/server.rb +74 -0
  59. data/lib/cool.io/socket.rb +224 -0
  60. data/lib/cool.io/timer_watcher.rb +17 -0
  61. data/lib/coolio.rb +2 -0
  62. data/lib/rev.rb +4 -0
  63. data/spec/async_watcher_spec.rb +57 -0
  64. data/spec/possible_tests/schedules_other_threads.rb +48 -0
  65. data/spec/possible_tests/test_on_resolve_failed.rb +9 -0
  66. data/spec/possible_tests/test_resolves.rb +27 -0
  67. data/spec/possible_tests/test_write_during_resolve.rb +27 -0
  68. data/spec/possible_tests/works_straight.rb +71 -0
  69. data/spec/spec_helper.rb +5 -0
  70. data/spec/timer_watcher_spec.rb +55 -0
  71. data/spec/unix_listener_spec.rb +25 -0
  72. data/spec/unix_server_spec.rb +25 -0
  73. metadata +184 -0
@@ -0,0 +1,17 @@
1
+ #--
2
+ # Copyright (C)2007-10 Tony Arcieri
3
+ # You can redistribute this under the terms of the Ruby license
4
+ # See file LICENSE for details
5
+ #++
6
+
7
+ module Coolio
8
+ class IOWatcher
9
+ # The actual implementation of this class resides in the C extension
10
+ # Here we metaprogram proper event_callbacks for the callback methods
11
+ # These can take a block and store it to be called when the event
12
+ # is actually fired.
13
+
14
+ extend Meta
15
+ event_callback :on_readable, :on_writable
16
+ end
17
+ end
@@ -0,0 +1,93 @@
1
+ #--
2
+ # Copyright (C)2007-10 Tony Arcieri
3
+ # You can redistribute this under the terms of the Ruby license
4
+ # See file LICENSE for details
5
+ #++
6
+
7
+ require 'socket'
8
+
9
+ module Coolio
10
+ # Listeners wait for incoming connections. When a listener receives a
11
+ # connection it fires the on_connection event with the newly accepted
12
+ # socket as a parameter.
13
+ class Listener < IOWatcher
14
+ def initialize(listen_socket)
15
+ @listen_socket = listen_socket
16
+ super(@listen_socket)
17
+ end
18
+
19
+ # Returns an integer representing the underlying numeric file descriptor
20
+ def fileno
21
+ @listen_socket.fileno
22
+ end
23
+
24
+ # Close the listener
25
+ def close
26
+ detach if attached?
27
+ @listen_socket.close
28
+ end
29
+
30
+ # Called whenever the server receives a new connection
31
+ def on_connection(socket); end
32
+ event_callback :on_connection
33
+
34
+ #########
35
+ protected
36
+ #########
37
+
38
+ # Coolio callback for handling new connections
39
+ def on_readable
40
+ begin
41
+ on_connection @listen_socket.accept_nonblock
42
+ rescue Errno::EAGAIN, Errno::ECONNABORTED
43
+ # EAGAIN can be triggered here if the socket is shared between
44
+ # multiple processes and a thundering herd is woken up to accept
45
+ # one connection, only one process will get the connection and
46
+ # the others will be awoken.
47
+ # ECONNABORTED is documented in accept() manpages but modern TCP
48
+ # stacks with syncookies and/or accept()-filtering for DoS
49
+ # protection do not see it. In any case this error is harmless
50
+ # and we should instead spend our time with clients that follow
51
+ # through on connection attempts.
52
+ end
53
+ end
54
+ end
55
+
56
+ class TCPListener < Listener
57
+ DEFAULT_BACKLOG = 1024
58
+
59
+ # Create a new Coolio::TCPListener on the specified address and port.
60
+ # Accepts the following options:
61
+ #
62
+ # :backlog - Max size of the pending connection queue (default 1024)
63
+ # :reverse_lookup - Retain BasicSocket's reverse DNS functionality (default false)
64
+ #
65
+ # If the specified address is an TCPServer object, it will ignore
66
+ # the port and :backlog option and create a new Coolio::TCPListener out
67
+ # of the existing TCPServer object.
68
+ def initialize(addr, port = nil, options = {})
69
+ BasicSocket.do_not_reverse_lookup = true unless options[:reverse_lookup]
70
+ options[:backlog] ||= DEFAULT_BACKLOG
71
+
72
+ listen_socket = if ::TCPServer === addr
73
+ addr
74
+ else
75
+ raise ArgumentError, "port must be an integer" if nil == port
76
+ ::TCPServer.new(addr, port)
77
+ end
78
+ listen_socket.instance_eval { listen(options[:backlog]) }
79
+ super(listen_socket)
80
+ end
81
+ end
82
+
83
+ class UNIXListener < Listener
84
+ # Create a new Coolio::UNIXListener
85
+ #
86
+ # Accepts the same arguments as UNIXServer.new
87
+ # Optionally, it can also take anyn existing UNIXServer object
88
+ # and create a Coolio::UNIXListener out of it.
89
+ def initialize(*args)
90
+ super(::UNIXServer === args.first ? args.first : ::UNIXServer.new(*args))
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,130 @@
1
+ #--
2
+ # Copyright (C)2007-10 Tony Arcieri
3
+ # You can redistribute this under the terms of the Ruby license
4
+ # See file LICENSE for details
5
+ #++
6
+
7
+ require 'thread'
8
+
9
+ # Monkeypatch Thread to include a method for obtaining the default Coolio::Loop
10
+ class Thread
11
+ def _coolio_loop
12
+ @_coolio_loop ||= Coolio::Loop.new
13
+ end
14
+ end
15
+
16
+ module Coolio
17
+ class Loop
18
+ # In Ruby 1.9 we want a Coolio::Loop per thread, but Ruby 1.8 is unithreaded
19
+ if RUBY_VERSION >= "1.9.0"
20
+ # Retrieve the default event loop for the current thread
21
+ def self.default
22
+ Thread.current._coolio_loop
23
+ end
24
+ else
25
+ # Retrieve the default event loop
26
+ def self.default
27
+ @@_coolio_loop ||= Coolio::Loop.new
28
+ end
29
+ end
30
+
31
+ # Create a new Coolio::Loop
32
+ #
33
+ # Options:
34
+ #
35
+ # :skip_environment (boolean)
36
+ # Ignore the $LIBEV_FLAGS environment variable
37
+ #
38
+ # :fork_check (boolean)
39
+ # Enable autodetection of forks
40
+ #
41
+ # :backend
42
+ # Choose the default backend, one (or many in an array) of:
43
+ # :select (most platforms)
44
+ # :poll (most platforms except Windows)
45
+ # :epoll (Linux)
46
+ # :kqueue (BSD/Mac OS X)
47
+ # :port (Solaris 10)
48
+ #
49
+ def initialize(options = {})
50
+ @watchers = {}
51
+ @active_watchers = 0
52
+
53
+ flags = 0
54
+
55
+ options.each do |option, value|
56
+ case option
57
+ when :skip_environment
58
+ flags |= EVFLAG_NOEV if value
59
+ when :fork_check
60
+ flags |= EVFLAG_FORKCHECK if value
61
+ when :backend
62
+ value = [value] unless value.is_a? Array
63
+ value.each do |backend|
64
+ case backend
65
+ when :select then flags |= EVBACKEND_SELECT
66
+ when :poll then flags |= EVBACKEND_POLL
67
+ when :epoll then flags |= EVBACKEND_EPOLL
68
+ when :kqueue then flags |= EVBACKEND_KQUEUE
69
+ when :port then flags |= EVBACKEND_PORT
70
+ else raise ArgumentError, "no such backend: #{backend}"
71
+ end
72
+ end
73
+ else raise ArgumentError, "no such option: #{option}"
74
+ end
75
+ end
76
+
77
+ @loop = ev_loop_new(flags)
78
+ end
79
+
80
+ # Attach a watcher to the loop
81
+ def attach(watcher)
82
+ watcher.attach self
83
+ end
84
+
85
+ # Run the event loop and dispatch events back to Ruby. If there
86
+ # are no watchers associated with the event loop it will return
87
+ # immediately. Otherwise, run will continue blocking and making
88
+ # event callbacks to watchers until all watchers associated with
89
+ # the loop have been disabled or detached. The loop may be
90
+ # explicitly stopped by calling the stop method on the loop object.
91
+ def run
92
+ raise RuntimeError, "no watchers for this loop" if @watchers.empty?
93
+
94
+ @running = true
95
+ while @running and not @active_watchers.zero?
96
+ run_once
97
+ end
98
+ @running = false
99
+ end
100
+
101
+ # Stop the event loop if it's running
102
+ def stop
103
+ raise RuntimeError, "loop not running" unless @running
104
+ @running = false
105
+ end
106
+
107
+ # Does the loop have any active watchers?
108
+ def has_active_watchers?
109
+ @active_watchers > 0
110
+ end
111
+
112
+ # All watchers attached to the current loop
113
+ def watchers
114
+ @watchers.keys
115
+ end
116
+
117
+ #######
118
+ private
119
+ #######
120
+
121
+ EVFLAG_NOENV = 0x1000000 # do NOT consult environment
122
+ EVFLAG_FORKCHECK = 0x2000000 # check for a fork in each iteration
123
+
124
+ EVBACKEND_SELECT = 0x00000001 # supported about anywhere
125
+ EVBACKEND_POLL = 0x00000002 # !win
126
+ EVBACKEND_EPOLL = 0x00000004 # linux
127
+ EVBACKEND_KQUEUE = 0x00000008 # bsd
128
+ EVBACKEND_PORT = 0x00000020 # solaris 10
129
+ end
130
+ end
@@ -0,0 +1,49 @@
1
+ #--
2
+ # Copyright (C)2007-10 Tony Arcieri
3
+ # You can redistribute this under the terms of the Ruby license
4
+ # See file LICENSE for details
5
+ #++
6
+
7
+ module Coolio
8
+ module Meta
9
+ # Use an alternate watcher with the attach/detach/enable/disable methods
10
+ # if it is presently assigned. This is useful if you are waiting for
11
+ # an event to occur before the current watcher can be used in earnest,
12
+ # such as making an outgoing TCP connection.
13
+ def watcher_delegate(proxy_var)
14
+ %w{attach detach enable disable}.each do |method|
15
+ module_eval <<-EOD
16
+ def #{method}(*args)
17
+ if #{proxy_var}
18
+ #{proxy_var}.#{method}(*args)
19
+ return self
20
+ end
21
+
22
+ super
23
+ end
24
+ EOD
25
+ end
26
+ end
27
+
28
+ # Define callbacks whose behavior can be changed on-the-fly per instance.
29
+ # This is done by giving a block to the callback method, which is captured
30
+ # as a proc and stored for later. If the method is called without a block,
31
+ # the stored block is executed if present, otherwise it's a noop.
32
+ def event_callback(*methods)
33
+ methods.each do |method|
34
+ module_eval <<-EOD
35
+ def #{method}(*args, &block)
36
+ if block
37
+ @#{method}_callback = block
38
+ return
39
+ end
40
+
41
+ if @#{method}_callback
42
+ instance_exec(*args, &@#{method}_callback)
43
+ end
44
+ end
45
+ EOD
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,74 @@
1
+ #--
2
+ # Copyright (C)2007-10 Tony Arcieri
3
+ # You can redistribute this under the terms of the Ruby license
4
+ # See file LICENSE for details
5
+ #++
6
+
7
+ module Coolio
8
+ class Server < Listener
9
+ # Servers listen for incoming connections and create new connection objects
10
+ # whenever incoming connections are received. The default class for new
11
+ # connections is a Socket, but any subclass of IOWatcher is acceptable.
12
+ def initialize(listen_socket, klass = Socket, *args, &block)
13
+ # Ensure the provided class responds to attach
14
+ unless klass.allocate.is_a? IO
15
+ raise ArgumentError, "can't convert #{klass} to Coolio::IO"
16
+ end
17
+
18
+ # Verify the arity of the provided arguments
19
+ arity = klass.instance_method(:initialize).arity
20
+ expected = arity >= 0 ? arity : -(arity + 1)
21
+
22
+ if (arity >= 0 and args.size + 1 != expected) or (arity < 0 and args.size + 1 < expected)
23
+ raise ArgumentError, "wrong number of arguments for #{klass}#initialize (#{args.size+1} for #{expected})"
24
+ end
25
+
26
+ @klass, @args, @block = klass, args, block
27
+ super(listen_socket)
28
+ end
29
+
30
+ # Returns an integer representing the underlying numeric file descriptor
31
+ def fileno
32
+ @listen_socket.fileno
33
+ end
34
+
35
+ #########
36
+ protected
37
+ #########
38
+
39
+ def on_connection(socket)
40
+ connection = @klass.new(socket, *@args).attach(evloop)
41
+ connection.__send__(:on_connect)
42
+ @block.call(connection) if @block
43
+ end
44
+ end
45
+
46
+ # TCP server class. Listens on the specified host and port and creates
47
+ # new connection objects of the given class. This is the most common server class.
48
+ # Note that the new connection objects will be bound by default to the same event loop that the server is attached to.
49
+ # Optionally, it can also take any existing core TCPServer object as
50
+ # +host+ and create a Coolio::TCPServer out of it.
51
+ class TCPServer < Server
52
+ def initialize(host, port = nil, klass = TCPSocket, *args, &block)
53
+ listen_socket = if ::TCPServer === host
54
+ host
55
+ else
56
+ raise ArgumentError, "port must be an integer" if nil == port
57
+ ::TCPServer.new(host, port)
58
+ end
59
+ listen_socket.instance_eval { listen(1024) } # Change listen backlog to 1024
60
+ super(listen_socket, klass, *args, &block)
61
+ end
62
+ end
63
+
64
+ # UNIX server class. Listens on the specified UNIX domain socket and
65
+ # creates new connection objects of the given class.
66
+ # Optionally, it can also take any existing core UNIXServer object as
67
+ # +path+ and create a Coolio::UNIXServer out of it.
68
+ class UNIXServer < Server
69
+ def initialize(path, klass = UNIXSocket, *args, &block)
70
+ s = ::UNIXServer === path ? path : ::UNIXServer.new(path)
71
+ super(s, klass, *args, &block)
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,224 @@
1
+ #--
2
+ # Copyright (C)2007 Tony Arcieri
3
+ # You can redistribute this under the terms of the Ruby license
4
+ # See file LICENSE for details
5
+ #++
6
+
7
+ require 'socket'
8
+ require 'resolv'
9
+
10
+ module Coolio
11
+ class Socket < IO
12
+ def self.connect(socket, *args)
13
+
14
+ new(socket, *args).instance_eval do
15
+ @_connector = Connector.new(self, socket)
16
+ self
17
+ end
18
+ end
19
+
20
+ watcher_delegate :@_connector
21
+
22
+ def attach(evloop)
23
+ raise RuntimeError, "connection failed" if @_failed
24
+
25
+ if @_connector
26
+ @_connector.attach(evloop)
27
+ return self
28
+ end
29
+
30
+ super
31
+ end
32
+
33
+ # Called upon completion of a socket connection
34
+ def on_connect; end
35
+ event_callback :on_connect
36
+
37
+ # Called if a socket connection failed to complete
38
+ def on_connect_failed; end
39
+ event_callback :on_connect_failed
40
+
41
+ # Called if a hostname failed to resolve when connecting
42
+ # Defaults to calling on_connect_failed
43
+ def on_resolve_failed
44
+ on_connect_failed
45
+ end
46
+
47
+ #########
48
+ protected
49
+ #########
50
+
51
+ class Connector < IOWatcher
52
+ def initialize(coolio_socket, ruby_socket)
53
+ @coolio_socket, @ruby_socket = coolio_socket, ruby_socket
54
+ super(ruby_socket, :w)
55
+ end
56
+
57
+ def on_writable
58
+ evl = evloop
59
+ detach
60
+
61
+ if connect_successful?
62
+ @coolio_socket.instance_eval { @_connector = nil }
63
+ @coolio_socket.attach(evl)
64
+ @ruby_socket.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, [1].pack("l"))
65
+ @ruby_socket.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_KEEPALIVE, true)
66
+
67
+ @coolio_socket.__send__(:on_connect)
68
+ else
69
+ @coolio_socket.instance_eval { @_failed = true }
70
+ @coolio_socket.__send__(:on_connect_failed)
71
+ end
72
+ end
73
+
74
+ #######
75
+ private
76
+ #######
77
+
78
+ def connect_successful?
79
+ @ruby_socket.getsockopt(::Socket::SOL_SOCKET, ::Socket::SO_ERROR).unpack('i').first == 0
80
+ rescue IOError
81
+ false
82
+ end
83
+ end
84
+ end
85
+
86
+ class TCPSocket < Socket
87
+ attr_reader :remote_host, :remote_addr, :remote_port, :address_family
88
+ watcher_delegate :@_resolver
89
+
90
+ # Similar to .new, but used in cases where the resulting object is in a
91
+ # "half-open" state. This is primarily used for when asynchronous
92
+ # DNS resolution is taking place. We don't actually have a handle to
93
+ # the socket we want to use to create the watcher yet, since we don't
94
+ # know the IP address to connect to.
95
+ def self.precreate(*args, &block)
96
+ obj = allocate
97
+ obj.__send__(:preinitialize, *args, &block)
98
+ obj
99
+ end
100
+
101
+ # Perform a non-blocking connect to the given host and port
102
+ # see examples/echo_client.rb
103
+ # addr is a string, can be an IP address or a hostname.
104
+ def self.connect(addr, port, *args)
105
+ family = nil
106
+
107
+ if (Resolv::IPv4.create(addr) rescue nil)
108
+ family = ::Socket::AF_INET
109
+ elsif(Resolv::IPv6.create(addr) rescue nil)
110
+ family = ::Socket::AF_INET6
111
+ end
112
+
113
+ if family
114
+ return super(TCPConnectSocket.new(family, addr, port), *args) # this creates a 'real' write buffer so we're ok there with regards to already having a write buffer from the get go
115
+ end
116
+
117
+ if host = Coolio::DNSResolver.hosts(addr)
118
+ return connect(host, port, *args) # calls this same function
119
+ end
120
+
121
+ precreate(addr, port, *args)
122
+ end
123
+
124
+ # Called by precreate during asyncronous DNS resolution
125
+ def preinitialize(addr, port, *args)
126
+ @_write_buffer = ::IO::Buffer.new # allow for writing BEFORE the DNS has resolved
127
+ @remote_host, @remote_addr, @remote_port = addr, addr, port
128
+ @_resolver = TCPConnectResolver.new(self, addr, port, *args)
129
+ end
130
+
131
+ private :preinitialize
132
+
133
+ def initialize(socket)
134
+ unless socket.is_a?(::TCPSocket) or socket.is_a?(TCPConnectSocket)
135
+ raise TypeError, "socket must be a TCPSocket"
136
+ end
137
+
138
+ super
139
+
140
+ @address_family, @remote_port, @remote_host, @remote_addr = socket.peeraddr
141
+ end
142
+
143
+ def peeraddr
144
+ [@address_family, @remote_port, @remote_host, @remote_addr]
145
+ end
146
+
147
+ #########
148
+ protected
149
+ #########
150
+
151
+ class TCPConnectSocket < ::Socket
152
+ def initialize(family, addr, port, host = addr)
153
+ @host, addr, @port = host, addr, port
154
+ @address_family = nil
155
+
156
+ @socket = super(family, ::Socket::SOCK_STREAM, 0)
157
+ begin
158
+ @socket.connect_nonblock(::Socket.sockaddr_in(port, addr))
159
+ rescue Errno::EINPROGRESS
160
+ end
161
+ end
162
+
163
+ def peeraddr
164
+ [
165
+ @address_family == ::Socket::AF_INET ? 'AF_INET' : 'AF_INET6',
166
+ @port,
167
+ @host,
168
+ @addr
169
+ ]
170
+ end
171
+ end
172
+
173
+ class TCPConnectResolver < Coolio::DNSResolver
174
+ def initialize(socket, host, port, *args)
175
+ @sock, @host, @port, @args = socket, host, port, args
176
+ super(host)
177
+ end
178
+
179
+ def on_success(addr)
180
+ host, port, args = @host, @port, @args
181
+
182
+ @sock.instance_eval do
183
+ # DNSResolver only supports IPv4 so we can safely assume IPv4 address
184
+ begin
185
+ socket = TCPConnectSocket.new(::Socket::AF_INET, addr, port, host)
186
+ rescue Errno::ENETUNREACH
187
+ on_connect_failed
188
+ return
189
+ end
190
+
191
+ initialize(socket, *args)
192
+ @_connector = Socket::Connector.new(self, socket)
193
+ @_resolver = nil
194
+ end
195
+ @sock.attach(evloop)
196
+ end
197
+
198
+ def on_failure
199
+ @sock.__send__(:on_resolve_failed)
200
+ @sock.instance_eval do
201
+ @_resolver = nil
202
+ @_failed = true
203
+ end
204
+ return
205
+ end
206
+ end
207
+ end
208
+
209
+ class UNIXSocket < Socket
210
+ attr_reader :path, :address_family
211
+
212
+ # Connect to the given UNIX domain socket
213
+ def self.connect(path, *args)
214
+ new(::UNIXSocket.new(path), *args)
215
+ end
216
+
217
+ def initialize(socket)
218
+ raise ArgumentError, "socket must be a UNIXSocket" unless socket.is_a? ::UNIXSocket
219
+
220
+ super
221
+ @address_family, @path = socket.peeraddr
222
+ end
223
+ end
224
+ end