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.
Files changed (76) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +29 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +13 -0
  5. data/CHANGES.md +229 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE +20 -0
  8. data/README.md +166 -0
  9. data/Rakefile +79 -0
  10. data/cool.io.gemspec +29 -0
  11. data/examples/callbacked_echo_server.rb +24 -0
  12. data/examples/dslified_echo_client.rb +34 -0
  13. data/examples/dslified_echo_server.rb +24 -0
  14. data/examples/echo_client.rb +38 -0
  15. data/examples/echo_server.rb +27 -0
  16. data/examples/google.rb +9 -0
  17. data/ext/cool.io/.gitignore +5 -0
  18. data/ext/cool.io/cool.io.h +59 -0
  19. data/ext/cool.io/cool.io_ext.c +25 -0
  20. data/ext/cool.io/ev_wrap.h +10 -0
  21. data/ext/cool.io/extconf.rb +61 -0
  22. data/ext/cool.io/iowatcher.c +189 -0
  23. data/ext/cool.io/libev.c +8 -0
  24. data/ext/cool.io/loop.c +261 -0
  25. data/ext/cool.io/stat_watcher.c +269 -0
  26. data/ext/cool.io/timer_watcher.c +219 -0
  27. data/ext/cool.io/utils.c +122 -0
  28. data/ext/cool.io/watcher.c +264 -0
  29. data/ext/cool.io/watcher.h +71 -0
  30. data/ext/iobuffer/extconf.rb +9 -0
  31. data/ext/iobuffer/iobuffer.c +767 -0
  32. data/ext/libev/Changes +507 -0
  33. data/ext/libev/LICENSE +37 -0
  34. data/ext/libev/README +58 -0
  35. data/ext/libev/README.embed +3 -0
  36. data/ext/libev/ev.c +5054 -0
  37. data/ext/libev/ev.h +853 -0
  38. data/ext/libev/ev_epoll.c +282 -0
  39. data/ext/libev/ev_kqueue.c +214 -0
  40. data/ext/libev/ev_poll.c +148 -0
  41. data/ext/libev/ev_port.c +185 -0
  42. data/ext/libev/ev_select.c +362 -0
  43. data/ext/libev/ev_vars.h +204 -0
  44. data/ext/libev/ev_win32.c +163 -0
  45. data/ext/libev/ev_wrap.h +200 -0
  46. data/ext/libev/ruby_gil.patch +97 -0
  47. data/ext/libev/test_libev_win32.c +123 -0
  48. data/ext/libev/win_select.patch +115 -0
  49. data/lib/.gitignore +2 -0
  50. data/lib/cool.io.rb +34 -0
  51. data/lib/cool.io/async_watcher.rb +43 -0
  52. data/lib/cool.io/custom_require.rb +9 -0
  53. data/lib/cool.io/dns_resolver.rb +219 -0
  54. data/lib/cool.io/dsl.rb +139 -0
  55. data/lib/cool.io/io.rb +194 -0
  56. data/lib/cool.io/iowatcher.rb +17 -0
  57. data/lib/cool.io/listener.rb +99 -0
  58. data/lib/cool.io/loop.rb +122 -0
  59. data/lib/cool.io/meta.rb +49 -0
  60. data/lib/cool.io/server.rb +75 -0
  61. data/lib/cool.io/socket.rb +230 -0
  62. data/lib/cool.io/timer_watcher.rb +17 -0
  63. data/lib/cool.io/version.rb +7 -0
  64. data/lib/coolio.rb +2 -0
  65. data/spec/async_watcher_spec.rb +57 -0
  66. data/spec/dns_spec.rb +43 -0
  67. data/spec/iobuffer_spec.rb +147 -0
  68. data/spec/spec_helper.rb +19 -0
  69. data/spec/stat_watcher_spec.rb +77 -0
  70. data/spec/tcp_server_spec.rb +225 -0
  71. data/spec/tcp_socket_spec.rb +185 -0
  72. data/spec/timer_watcher_spec.rb +59 -0
  73. data/spec/udp_socket_spec.rb +58 -0
  74. data/spec/unix_listener_spec.rb +25 -0
  75. data/spec/unix_server_spec.rb +27 -0
  76. metadata +182 -0
@@ -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
@@ -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