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/dsl.rb
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (C)2010 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
|
+
# A module we stash all the connections defined by the DSL under
|
9
|
+
module Connections; end
|
10
|
+
|
11
|
+
# A DSL for defining Cool.io connection types and servers
|
12
|
+
module DSL
|
13
|
+
# Define all methods on the metaclass
|
14
|
+
module_function
|
15
|
+
|
16
|
+
# Run the default Cool.io event loop
|
17
|
+
def run
|
18
|
+
Cool.io::Loop.default.run
|
19
|
+
end
|
20
|
+
|
21
|
+
# Connect to the given host and port using the given connection class
|
22
|
+
def connect(host, port, connection_name = nil, *initializer_args, &block)
|
23
|
+
if block_given?
|
24
|
+
initializer_args.unshift connection_name if connection_name
|
25
|
+
|
26
|
+
klass = Class.new Cool.io::TCPSocket
|
27
|
+
connection_builder = ConnectionBuilder.new klass
|
28
|
+
connection_builder.instance_eval(&block)
|
29
|
+
else
|
30
|
+
raise ArgumentError, "no connection name or block given" unless connection_name
|
31
|
+
klass = self[connection_name]
|
32
|
+
end
|
33
|
+
|
34
|
+
client = klass.connect host, port, *initializer_args
|
35
|
+
client.attach Cool.io::Loop.default
|
36
|
+
client
|
37
|
+
end
|
38
|
+
|
39
|
+
# Create a new Cool.io::TCPServer
|
40
|
+
def server(host, port, connection_name = nil, *initializer_args, &block)
|
41
|
+
if block_given?
|
42
|
+
initializer_args.unshift connection_name if connection_name
|
43
|
+
|
44
|
+
klass = Class.new Cool.io::TCPSocket
|
45
|
+
connection_builder = ConnectionBuilder.new klass
|
46
|
+
connection_builder.instance_eval(&block)
|
47
|
+
else
|
48
|
+
raise ArgumentError, "no connection name or block given" unless connection_name
|
49
|
+
klass = self[connection_name]
|
50
|
+
end
|
51
|
+
|
52
|
+
server = Cool.io::TCPServer.new host, port, klass, *initializer_args
|
53
|
+
server.attach Cool.io::Loop.default
|
54
|
+
server
|
55
|
+
end
|
56
|
+
|
57
|
+
# Create a new Cool.io::TCPSocket class
|
58
|
+
def connection(name, &block)
|
59
|
+
# Camelize class name
|
60
|
+
class_name = name.to_s.split('_').map { |s| s.capitalize }.join
|
61
|
+
|
62
|
+
connection = Class.new Cool.io::TCPSocket
|
63
|
+
connection_builder = ConnectionBuilder.new connection
|
64
|
+
connection_builder.instance_eval(&block)
|
65
|
+
|
66
|
+
Coolio::Connections.const_set class_name, connection
|
67
|
+
end
|
68
|
+
|
69
|
+
# Look up a connection class by its name
|
70
|
+
def [](connection_name)
|
71
|
+
class_name = connection_name.to_s.split('_').map { |s| s.capitalize }.join
|
72
|
+
|
73
|
+
begin
|
74
|
+
Coolio::Connections.const_get class_name
|
75
|
+
rescue NameError
|
76
|
+
raise NameError, "No connection type registered for #{connection_name.inspect}"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Builder for Cool.io::TCPSocket classes
|
81
|
+
class ConnectionBuilder
|
82
|
+
def initialize(klass)
|
83
|
+
@klass = klass
|
84
|
+
end
|
85
|
+
|
86
|
+
# Declare an initialize function
|
87
|
+
def initializer(&action)
|
88
|
+
@klass.send :define_method, :initialize, &action
|
89
|
+
end
|
90
|
+
|
91
|
+
# Declare the on_connect callback
|
92
|
+
def on_connect(&action)
|
93
|
+
@klass.send :define_method, :on_connect, &action
|
94
|
+
end
|
95
|
+
|
96
|
+
# Declare a callback fired if we failed to connect
|
97
|
+
def on_connect_failed(&action)
|
98
|
+
@klass.send :define_method, :on_connect_failed, &action
|
99
|
+
end
|
100
|
+
|
101
|
+
# Declare a callback fired if DNS resolution failed
|
102
|
+
def on_resolve_failed(&action)
|
103
|
+
@klass.send :define_method, :on_resolve_failed, &action
|
104
|
+
end
|
105
|
+
|
106
|
+
# Declare the on_close callback
|
107
|
+
def on_close(&action)
|
108
|
+
@klass.send :define_method, :on_close, &action
|
109
|
+
end
|
110
|
+
|
111
|
+
# Declare the on_read callback
|
112
|
+
def on_read(&action)
|
113
|
+
@klass.send :define_method, :on_read, &action
|
114
|
+
end
|
115
|
+
|
116
|
+
# Declare the on_write_complete callback
|
117
|
+
def on_write_complete(&action)
|
118
|
+
@klass.send :define_method, :on_write_complete, &action
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# The Cool module containing all our coolness
|
125
|
+
module Cool
|
126
|
+
module Coolness
|
127
|
+
def cool
|
128
|
+
Cool::IOThunk
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
module IOThunk
|
133
|
+
def self.io
|
134
|
+
Coolio::DSL
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
extend Cool::Coolness
|
data/lib/cool.io/io.rb
ADDED
@@ -0,0 +1,194 @@
|
|
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
|
+
# A buffered I/O class witch fits into the Coolio Watcher framework.
|
9
|
+
# It provides both an observer which reads data as it's received
|
10
|
+
# from the wire and a buffered write watcher which stores data and writes
|
11
|
+
# it out each time the socket becomes writable.
|
12
|
+
#
|
13
|
+
# This class is primarily meant as a base class for other streams
|
14
|
+
# which need non-blocking writing, and is used to implement Coolio's
|
15
|
+
# Socket class and its associated subclasses.
|
16
|
+
class IO
|
17
|
+
extend Meta
|
18
|
+
|
19
|
+
# Maximum number of bytes to consume at once
|
20
|
+
INPUT_SIZE = 16384
|
21
|
+
|
22
|
+
def initialize(io)
|
23
|
+
@_io = io
|
24
|
+
@_write_buffer ||= ::IO::Buffer.new
|
25
|
+
@_read_watcher = Watcher.new(io, self, :r)
|
26
|
+
@_write_watcher = Watcher.new(io, self, :w)
|
27
|
+
end
|
28
|
+
|
29
|
+
#
|
30
|
+
# Watcher methods, delegated to @_read_watcher
|
31
|
+
#
|
32
|
+
|
33
|
+
# Attach to the event loop
|
34
|
+
def attach(loop)
|
35
|
+
@_read_watcher.attach(loop)
|
36
|
+
schedule_write if !@_write_buffer.empty?
|
37
|
+
self
|
38
|
+
end
|
39
|
+
|
40
|
+
# Detach from the event loop
|
41
|
+
def detach
|
42
|
+
# TODO should these detect write buffers, as well?
|
43
|
+
@_read_watcher.detach
|
44
|
+
self
|
45
|
+
end
|
46
|
+
|
47
|
+
# Enable the watcher
|
48
|
+
def enable
|
49
|
+
@_read_watcher.enable
|
50
|
+
self
|
51
|
+
end
|
52
|
+
|
53
|
+
# Disable the watcher
|
54
|
+
def disable
|
55
|
+
@_read_watcher.disable
|
56
|
+
self
|
57
|
+
end
|
58
|
+
|
59
|
+
# Is the watcher attached?
|
60
|
+
def attached?
|
61
|
+
@_read_watcher.attached?
|
62
|
+
end
|
63
|
+
|
64
|
+
# Is the watcher enabled?
|
65
|
+
def enabled?
|
66
|
+
@_read_watcher.enabled?
|
67
|
+
end
|
68
|
+
|
69
|
+
# Obtain the event loop associated with this object
|
70
|
+
def evloop
|
71
|
+
@_read_watcher.evloop
|
72
|
+
end
|
73
|
+
|
74
|
+
#
|
75
|
+
# Callbacks for asynchronous events
|
76
|
+
#
|
77
|
+
|
78
|
+
# Called whenever the IO object receives data
|
79
|
+
def on_read(data); end
|
80
|
+
event_callback :on_read
|
81
|
+
|
82
|
+
# Called whenever a write completes and the output buffer is empty
|
83
|
+
def on_write_complete; end
|
84
|
+
event_callback :on_write_complete
|
85
|
+
|
86
|
+
# Called whenever the IO object hits EOF
|
87
|
+
def on_close; end
|
88
|
+
event_callback :on_close
|
89
|
+
|
90
|
+
#
|
91
|
+
# Write interface
|
92
|
+
#
|
93
|
+
|
94
|
+
# Write data in a buffered, non-blocking manner
|
95
|
+
def write(data)
|
96
|
+
@_write_buffer << data
|
97
|
+
schedule_write
|
98
|
+
data.size
|
99
|
+
end
|
100
|
+
|
101
|
+
# Close the IO stream
|
102
|
+
def close
|
103
|
+
detach if attached?
|
104
|
+
detach_write_watcher
|
105
|
+
@_io.close unless closed?
|
106
|
+
|
107
|
+
on_close
|
108
|
+
nil
|
109
|
+
end
|
110
|
+
|
111
|
+
# Is the IO object closed?
|
112
|
+
def closed?
|
113
|
+
@_io.nil? or @_io.closed?
|
114
|
+
end
|
115
|
+
|
116
|
+
#########
|
117
|
+
protected
|
118
|
+
#########
|
119
|
+
|
120
|
+
# Read from the input buffer and dispatch to on_read
|
121
|
+
def on_readable
|
122
|
+
begin
|
123
|
+
on_read @_io.read_nonblock(INPUT_SIZE)
|
124
|
+
rescue Errno::EAGAIN, Errno::EINTR
|
125
|
+
return
|
126
|
+
|
127
|
+
# SystemCallError catches Errno::ECONNRESET amongst others.
|
128
|
+
rescue SystemCallError, EOFError, IOError, SocketError
|
129
|
+
close
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# Write the contents of the output buffer
|
134
|
+
def on_writable
|
135
|
+
begin
|
136
|
+
@_write_buffer.write_to(@_io)
|
137
|
+
rescue Errno::EINTR
|
138
|
+
return
|
139
|
+
|
140
|
+
# SystemCallError catches Errno::EPIPE & Errno::ECONNRESET amongst others.
|
141
|
+
rescue SystemCallError, IOError, SocketError
|
142
|
+
return close
|
143
|
+
end
|
144
|
+
|
145
|
+
if @_write_buffer.empty?
|
146
|
+
disable_write_watcher
|
147
|
+
on_write_complete
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
# Schedule a write to be performed when the IO object becomes writable
|
152
|
+
def schedule_write
|
153
|
+
return unless @_io # this would mean 'we are still pre DNS here'
|
154
|
+
return unless @_read_watcher.attached? # this would mean 'currently unattached' -- ie still pre DNS, or just plain not attached, which is ok
|
155
|
+
begin
|
156
|
+
enable_write_watcher
|
157
|
+
rescue IOError
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def enable_write_watcher
|
162
|
+
if @_write_watcher.attached?
|
163
|
+
@_write_watcher.enable unless @_write_watcher.enabled?
|
164
|
+
else
|
165
|
+
@_write_watcher.attach(evloop)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def disable_write_watcher
|
170
|
+
@_write_watcher.disable if @_write_watcher and @_write_watcher.enabled?
|
171
|
+
end
|
172
|
+
|
173
|
+
def detach_write_watcher
|
174
|
+
@_write_watcher.detach if @_write_watcher and @_write_watcher.attached?
|
175
|
+
end
|
176
|
+
|
177
|
+
# Internal class implementing watchers used by Coolio::IO
|
178
|
+
class Watcher < IOWatcher
|
179
|
+
def initialize(ruby_io, coolio_io, flags)
|
180
|
+
@coolio_io = coolio_io
|
181
|
+
super(ruby_io, flags)
|
182
|
+
end
|
183
|
+
|
184
|
+
# Configure IOWatcher event callbacks to call the method passed to #initialize
|
185
|
+
def on_readable
|
186
|
+
@coolio_io.__send__(:on_readable)
|
187
|
+
end
|
188
|
+
|
189
|
+
def on_writable
|
190
|
+
@coolio_io.__send__(:on_writable)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
@@ -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,99 @@
|
|
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
|
+
def listen(backlog)
|
25
|
+
@listen_socket.listen(backlog)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Close the listener
|
29
|
+
def close
|
30
|
+
detach if attached?
|
31
|
+
@listen_socket.close
|
32
|
+
end
|
33
|
+
|
34
|
+
# Called whenever the server receives a new connection
|
35
|
+
def on_connection(socket); end
|
36
|
+
event_callback :on_connection
|
37
|
+
|
38
|
+
#########
|
39
|
+
protected
|
40
|
+
#########
|
41
|
+
|
42
|
+
# Coolio callback for handling new connections
|
43
|
+
def on_readable
|
44
|
+
begin
|
45
|
+
on_connection @listen_socket.accept_nonblock
|
46
|
+
rescue Errno::EAGAIN, Errno::ECONNABORTED
|
47
|
+
# EAGAIN can be triggered here if the socket is shared between
|
48
|
+
# multiple processes and a thundering herd is woken up to accept
|
49
|
+
# one connection, only one process will get the connection and
|
50
|
+
# the others will be awoken.
|
51
|
+
# ECONNABORTED is documented in accept() manpages but modern TCP
|
52
|
+
# stacks with syncookies and/or accept()-filtering for DoS
|
53
|
+
# protection do not see it. In any case this error is harmless
|
54
|
+
# and we should instead spend our time with clients that follow
|
55
|
+
# through on connection attempts.
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
DEFAULT_BACKLOG = 1024
|
61
|
+
|
62
|
+
class TCPListener < Listener
|
63
|
+
# Create a new Coolio::TCPListener on the specified address and port.
|
64
|
+
# Accepts the following options:
|
65
|
+
#
|
66
|
+
# :backlog - Max size of the pending connection queue (default 1024)
|
67
|
+
# :reverse_lookup - Retain BasicSocket's reverse DNS functionality (default false)
|
68
|
+
#
|
69
|
+
# If the specified address is an TCPServer object, it will ignore
|
70
|
+
# the port and :backlog option and create a new Coolio::TCPListener out
|
71
|
+
# of the existing TCPServer object.
|
72
|
+
def initialize(addr, port = nil, options = {})
|
73
|
+
BasicSocket.do_not_reverse_lookup = true unless options[:reverse_lookup]
|
74
|
+
options[:backlog] ||= DEFAULT_BACKLOG
|
75
|
+
|
76
|
+
listen_socket = if ::TCPServer === addr
|
77
|
+
addr
|
78
|
+
else
|
79
|
+
raise ArgumentError, "port must be an integer" if nil == port
|
80
|
+
::TCPServer.new(addr, port)
|
81
|
+
end
|
82
|
+
listen_socket.instance_eval { listen(options[:backlog]) }
|
83
|
+
super(listen_socket)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
class UNIXListener < Listener
|
88
|
+
# Create a new Coolio::UNIXListener
|
89
|
+
#
|
90
|
+
# Accepts the same arguments as UNIXServer.new
|
91
|
+
# Optionally, it can also take anyn existing UNIXServer object
|
92
|
+
# and create a Coolio::UNIXListener out of it.
|
93
|
+
def initialize(*args)
|
94
|
+
s = ::UNIXServer === args.first ? args.first : ::UNIXServer.new(*args)
|
95
|
+
s.instance_eval { listen(DEFAULT_BACKLOG) }
|
96
|
+
super(s)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|