cool.io 1.4.1-x64-mingw32
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.
- checksums.yaml +7 -0
- data/.gitignore +29 -0
- data/.rspec +3 -0
- data/.travis.yml +13 -0
- data/CHANGES.md +229 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.md +166 -0
- data/Rakefile +79 -0
- data/cool.io.gemspec +29 -0
- data/examples/callbacked_echo_server.rb +24 -0
- data/examples/dslified_echo_client.rb +34 -0
- data/examples/dslified_echo_server.rb +24 -0
- data/examples/echo_client.rb +38 -0
- data/examples/echo_server.rb +27 -0
- data/examples/google.rb +9 -0
- data/ext/cool.io/.gitignore +5 -0
- data/ext/cool.io/cool.io.h +59 -0
- data/ext/cool.io/cool.io_ext.c +25 -0
- data/ext/cool.io/ev_wrap.h +10 -0
- data/ext/cool.io/extconf.rb +61 -0
- data/ext/cool.io/iowatcher.c +189 -0
- data/ext/cool.io/libev.c +8 -0
- data/ext/cool.io/loop.c +261 -0
- data/ext/cool.io/stat_watcher.c +269 -0
- data/ext/cool.io/timer_watcher.c +219 -0
- data/ext/cool.io/utils.c +122 -0
- data/ext/cool.io/watcher.c +264 -0
- data/ext/cool.io/watcher.h +71 -0
- data/ext/iobuffer/extconf.rb +9 -0
- data/ext/iobuffer/iobuffer.c +767 -0
- data/ext/libev/Changes +507 -0
- data/ext/libev/LICENSE +37 -0
- data/ext/libev/README +58 -0
- data/ext/libev/README.embed +3 -0
- data/ext/libev/ev.c +5054 -0
- data/ext/libev/ev.h +853 -0
- data/ext/libev/ev_epoll.c +282 -0
- data/ext/libev/ev_kqueue.c +214 -0
- data/ext/libev/ev_poll.c +148 -0
- data/ext/libev/ev_port.c +185 -0
- data/ext/libev/ev_select.c +362 -0
- data/ext/libev/ev_vars.h +204 -0
- data/ext/libev/ev_win32.c +163 -0
- data/ext/libev/ev_wrap.h +200 -0
- data/ext/libev/ruby_gil.patch +97 -0
- data/ext/libev/test_libev_win32.c +123 -0
- data/ext/libev/win_select.patch +115 -0
- data/lib/.gitignore +2 -0
- data/lib/cool.io.rb +34 -0
- data/lib/cool.io/async_watcher.rb +43 -0
- data/lib/cool.io/custom_require.rb +9 -0
- data/lib/cool.io/dns_resolver.rb +219 -0
- data/lib/cool.io/dsl.rb +139 -0
- data/lib/cool.io/io.rb +194 -0
- data/lib/cool.io/iowatcher.rb +17 -0
- data/lib/cool.io/listener.rb +99 -0
- data/lib/cool.io/loop.rb +122 -0
- data/lib/cool.io/meta.rb +49 -0
- data/lib/cool.io/server.rb +75 -0
- data/lib/cool.io/socket.rb +230 -0
- data/lib/cool.io/timer_watcher.rb +17 -0
- data/lib/cool.io/version.rb +7 -0
- data/lib/coolio.rb +2 -0
- data/spec/async_watcher_spec.rb +57 -0
- data/spec/dns_spec.rb +43 -0
- data/spec/iobuffer_spec.rb +147 -0
- data/spec/spec_helper.rb +19 -0
- data/spec/stat_watcher_spec.rb +77 -0
- data/spec/tcp_server_spec.rb +225 -0
- data/spec/tcp_socket_spec.rb +185 -0
- data/spec/timer_watcher_spec.rb +59 -0
- data/spec/udp_socket_spec.rb +58 -0
- data/spec/unix_listener_spec.rb +25 -0
- data/spec/unix_server_spec.rb +27 -0
- metadata +182 -0
data/lib/cool.io/loop.rb
ADDED
@@ -0,0 +1,122 @@
|
|
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
|
+
# Retrieve the default event loop for the current thread
|
19
|
+
def self.default
|
20
|
+
Thread.current._coolio_loop
|
21
|
+
end
|
22
|
+
|
23
|
+
# Create a new Coolio::Loop
|
24
|
+
#
|
25
|
+
# Options:
|
26
|
+
#
|
27
|
+
# :skip_environment (boolean)
|
28
|
+
# Ignore the $LIBEV_FLAGS environment variable
|
29
|
+
#
|
30
|
+
# :fork_check (boolean)
|
31
|
+
# Enable autodetection of forks
|
32
|
+
#
|
33
|
+
# :backend
|
34
|
+
# Choose the default backend, one (or many in an array) of:
|
35
|
+
# :select (most platforms)
|
36
|
+
# :poll (most platforms except Windows)
|
37
|
+
# :epoll (Linux)
|
38
|
+
# :kqueue (BSD/Mac OS X)
|
39
|
+
# :port (Solaris 10)
|
40
|
+
#
|
41
|
+
def initialize(options = {})
|
42
|
+
@watchers = {}
|
43
|
+
@active_watchers = 0
|
44
|
+
|
45
|
+
flags = 0
|
46
|
+
|
47
|
+
options.each do |option, value|
|
48
|
+
case option
|
49
|
+
when :skip_environment
|
50
|
+
flags |= EVFLAG_NOEV if value
|
51
|
+
when :fork_check
|
52
|
+
flags |= EVFLAG_FORKCHECK if value
|
53
|
+
when :backend
|
54
|
+
value = [value] unless value.is_a? Array
|
55
|
+
value.each do |backend|
|
56
|
+
case backend
|
57
|
+
when :select then flags |= EVBACKEND_SELECT
|
58
|
+
when :poll then flags |= EVBACKEND_POLL
|
59
|
+
when :epoll then flags |= EVBACKEND_EPOLL
|
60
|
+
when :kqueue then flags |= EVBACKEND_KQUEUE
|
61
|
+
when :port then flags |= EVBACKEND_PORT
|
62
|
+
else raise ArgumentError, "no such backend: #{backend}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
else raise ArgumentError, "no such option: #{option}"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
@loop = ev_loop_new(flags)
|
70
|
+
end
|
71
|
+
|
72
|
+
# Attach a watcher to the loop
|
73
|
+
def attach(watcher)
|
74
|
+
watcher.attach self
|
75
|
+
end
|
76
|
+
|
77
|
+
# Run the event loop and dispatch events back to Ruby. If there
|
78
|
+
# are no watchers associated with the event loop it will return
|
79
|
+
# immediately. Otherwise, run will continue blocking and making
|
80
|
+
# event callbacks to watchers until all watchers associated with
|
81
|
+
# the loop have been disabled or detached. The loop may be
|
82
|
+
# explicitly stopped by calling the stop method on the loop object.
|
83
|
+
def run(timeout = nil)
|
84
|
+
raise RuntimeError, "no watchers for this loop" if @watchers.empty?
|
85
|
+
|
86
|
+
@running = true
|
87
|
+
while @running and not @active_watchers.zero?
|
88
|
+
run_once(timeout)
|
89
|
+
end
|
90
|
+
@running = false
|
91
|
+
end
|
92
|
+
|
93
|
+
# Stop the event loop if it's running
|
94
|
+
def stop
|
95
|
+
raise RuntimeError, "loop not running" unless @running
|
96
|
+
@running = false
|
97
|
+
end
|
98
|
+
|
99
|
+
# Does the loop have any active watchers?
|
100
|
+
def has_active_watchers?
|
101
|
+
@active_watchers > 0
|
102
|
+
end
|
103
|
+
|
104
|
+
# All watchers attached to the current loop
|
105
|
+
def watchers
|
106
|
+
@watchers.keys
|
107
|
+
end
|
108
|
+
|
109
|
+
#######
|
110
|
+
private
|
111
|
+
#######
|
112
|
+
|
113
|
+
EVFLAG_NOENV = 0x1000000 # do NOT consult environment
|
114
|
+
EVFLAG_FORKCHECK = 0x2000000 # check for a fork in each iteration
|
115
|
+
|
116
|
+
EVBACKEND_SELECT = 0x00000001 # supported about anywhere
|
117
|
+
EVBACKEND_POLL = 0x00000002 # !win
|
118
|
+
EVBACKEND_EPOLL = 0x00000004 # linux
|
119
|
+
EVBACKEND_KQUEUE = 0x00000008 # bsd
|
120
|
+
EVBACKEND_PORT = 0x00000020 # solaris 10
|
121
|
+
end
|
122
|
+
end
|
data/lib/cool.io/meta.rb
ADDED
@@ -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 attached? detach enable disable}.each do |method|
|
15
|
+
module_eval <<-EOD
|
16
|
+
def #{method}(*args)
|
17
|
+
if defined? #{proxy_var} and #{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 defined? @#{method}_callback and @#{method}_callback
|
42
|
+
@#{method}_callback.call(*args)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
EOD
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,75 @@
|
|
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(DEFAULT_BACKLOG) } # 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
|
+
s.instance_eval { listen(DEFAULT_BACKLOG) }
|
72
|
+
super(s, klass, *args, &block)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,230 @@
|
|
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
|
+
# Just initializes some instance variables to avoid
|
21
|
+
# warnings and calls super().
|
22
|
+
def initialize *args
|
23
|
+
@_failed = nil
|
24
|
+
@_connector = nil
|
25
|
+
super
|
26
|
+
end
|
27
|
+
|
28
|
+
watcher_delegate :@_connector
|
29
|
+
|
30
|
+
def attach(evloop)
|
31
|
+
raise RuntimeError, "connection failed" if @_failed
|
32
|
+
|
33
|
+
if @_connector
|
34
|
+
@_connector.attach(evloop)
|
35
|
+
return self
|
36
|
+
end
|
37
|
+
|
38
|
+
super
|
39
|
+
end
|
40
|
+
|
41
|
+
# Called upon completion of a socket connection
|
42
|
+
def on_connect; end
|
43
|
+
event_callback :on_connect
|
44
|
+
|
45
|
+
# Called if a socket connection failed to complete
|
46
|
+
def on_connect_failed; end
|
47
|
+
event_callback :on_connect_failed
|
48
|
+
|
49
|
+
# Called if a hostname failed to resolve when connecting
|
50
|
+
# Defaults to calling on_connect_failed
|
51
|
+
alias_method :on_resolve_failed, :on_connect_failed
|
52
|
+
|
53
|
+
#########
|
54
|
+
protected
|
55
|
+
#########
|
56
|
+
|
57
|
+
class Connector < IOWatcher
|
58
|
+
def initialize(coolio_socket, ruby_socket)
|
59
|
+
@coolio_socket, @ruby_socket = coolio_socket, ruby_socket
|
60
|
+
super(ruby_socket, :w)
|
61
|
+
end
|
62
|
+
|
63
|
+
def on_writable
|
64
|
+
evl = evloop
|
65
|
+
detach
|
66
|
+
|
67
|
+
if connect_successful?
|
68
|
+
@coolio_socket.instance_eval { @_connector = nil }
|
69
|
+
@coolio_socket.attach(evl)
|
70
|
+
@ruby_socket.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, [1].pack("l"))
|
71
|
+
@ruby_socket.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_KEEPALIVE, true)
|
72
|
+
|
73
|
+
@coolio_socket.__send__(:on_connect)
|
74
|
+
else
|
75
|
+
@coolio_socket.instance_eval { @_failed = true }
|
76
|
+
@coolio_socket.__send__(:on_connect_failed)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
#######
|
81
|
+
private
|
82
|
+
#######
|
83
|
+
|
84
|
+
def connect_successful?
|
85
|
+
@ruby_socket.getsockopt(::Socket::SOL_SOCKET, ::Socket::SO_ERROR).unpack('i').first == 0
|
86
|
+
rescue IOError
|
87
|
+
false
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
class TCPSocket < Socket
|
93
|
+
attr_reader :remote_host, :remote_addr, :remote_port, :address_family
|
94
|
+
watcher_delegate :@_resolver
|
95
|
+
|
96
|
+
# Similar to .new, but used in cases where the resulting object is in a
|
97
|
+
# "half-open" state. This is primarily used for when asynchronous
|
98
|
+
# DNS resolution is taking place. We don't actually have a handle to
|
99
|
+
# the socket we want to use to create the watcher yet, since we don't
|
100
|
+
# know the IP address to connect to.
|
101
|
+
def self.precreate(*args, &block)
|
102
|
+
obj = allocate
|
103
|
+
obj.__send__(:preinitialize, *args, &block)
|
104
|
+
obj
|
105
|
+
end
|
106
|
+
|
107
|
+
# Perform a non-blocking connect to the given host and port
|
108
|
+
# see examples/echo_client.rb
|
109
|
+
# addr is a string, can be an IP address or a hostname.
|
110
|
+
def self.connect(addr, port, *args)
|
111
|
+
family = nil
|
112
|
+
|
113
|
+
if (Resolv::IPv4.create(addr) rescue nil)
|
114
|
+
family = ::Socket::AF_INET
|
115
|
+
elsif(Resolv::IPv6.create(addr) rescue nil)
|
116
|
+
family = ::Socket::AF_INET6
|
117
|
+
end
|
118
|
+
|
119
|
+
if family
|
120
|
+
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
|
121
|
+
end
|
122
|
+
|
123
|
+
if host = Coolio::DNSResolver.hosts(addr)
|
124
|
+
return connect(host, port, *args) # calls this same function
|
125
|
+
end
|
126
|
+
|
127
|
+
precreate(addr, port, *args)
|
128
|
+
end
|
129
|
+
|
130
|
+
# Called by precreate during asyncronous DNS resolution
|
131
|
+
def preinitialize(addr, port, *args)
|
132
|
+
@_write_buffer = ::IO::Buffer.new # allow for writing BEFORE DNS has resolved
|
133
|
+
@remote_host, @remote_addr, @remote_port = addr, addr, port
|
134
|
+
@_resolver = TCPConnectResolver.new(self, addr, port, *args)
|
135
|
+
end
|
136
|
+
|
137
|
+
private :preinitialize
|
138
|
+
|
139
|
+
def initialize(socket)
|
140
|
+
unless socket.is_a?(::TCPSocket) or socket.is_a?(TCPConnectSocket)
|
141
|
+
raise TypeError, "socket must be a TCPSocket"
|
142
|
+
end
|
143
|
+
|
144
|
+
super
|
145
|
+
|
146
|
+
@address_family, @remote_port, @remote_host, @remote_addr = socket.peeraddr
|
147
|
+
end
|
148
|
+
|
149
|
+
def peeraddr
|
150
|
+
[@address_family, @remote_port, @remote_host, @remote_addr]
|
151
|
+
end
|
152
|
+
|
153
|
+
#########
|
154
|
+
protected
|
155
|
+
#########
|
156
|
+
|
157
|
+
class TCPConnectSocket < ::Socket
|
158
|
+
def initialize(family, addr, port, host = addr)
|
159
|
+
@host, @addr, @port = host, addr, port
|
160
|
+
@address_family = nil
|
161
|
+
|
162
|
+
super(family, ::Socket::SOCK_STREAM, 0)
|
163
|
+
begin
|
164
|
+
connect_nonblock(::Socket.sockaddr_in(port, addr))
|
165
|
+
rescue Errno::EINPROGRESS
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def peeraddr
|
170
|
+
[
|
171
|
+
@address_family == ::Socket::AF_INET ? 'AF_INET' : 'AF_INET6',
|
172
|
+
@port,
|
173
|
+
@host,
|
174
|
+
@addr
|
175
|
+
]
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
class TCPConnectResolver < Coolio::DNSResolver
|
180
|
+
def initialize(socket, host, port, *args)
|
181
|
+
@sock, @host, @port, @args = socket, host, port, args
|
182
|
+
super(host)
|
183
|
+
end
|
184
|
+
|
185
|
+
def on_success(addr)
|
186
|
+
host, port, args = @host, @port, @args
|
187
|
+
|
188
|
+
@sock.instance_eval do
|
189
|
+
# DNSResolver only supports IPv4 so we can safely assume IPv4 address
|
190
|
+
begin
|
191
|
+
socket = TCPConnectSocket.new(::Socket::AF_INET, addr, port, host)
|
192
|
+
rescue Errno::ENETUNREACH
|
193
|
+
on_connect_failed
|
194
|
+
return
|
195
|
+
end
|
196
|
+
|
197
|
+
initialize(socket, *args)
|
198
|
+
@_connector = Socket::Connector.new(self, socket)
|
199
|
+
@_resolver = nil
|
200
|
+
end
|
201
|
+
@sock.attach(evloop)
|
202
|
+
end
|
203
|
+
|
204
|
+
def on_failure
|
205
|
+
@sock.__send__(:on_resolve_failed)
|
206
|
+
@sock.instance_eval do
|
207
|
+
@_resolver = nil
|
208
|
+
@_failed = true
|
209
|
+
end
|
210
|
+
return
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
class UNIXSocket < Socket
|
216
|
+
attr_reader :path, :address_family
|
217
|
+
|
218
|
+
# Connect to the given UNIX domain socket
|
219
|
+
def self.connect(path, *args)
|
220
|
+
new(::UNIXSocket.new(path), *args)
|
221
|
+
end
|
222
|
+
|
223
|
+
def initialize(socket)
|
224
|
+
raise ArgumentError, "socket must be a UNIXSocket" unless socket.is_a? ::UNIXSocket
|
225
|
+
|
226
|
+
super
|
227
|
+
@address_family, @path = socket.peeraddr
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|