cool.io 1.4.1-x64-mingw32

Sign up to get free protection for your applications and to get access to all the features.
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,17 @@
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
+ module Coolio
8
+ class TimerWatcher
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_timer
16
+ end
17
+ end
@@ -0,0 +1,7 @@
1
+ module Coolio
2
+ VERSION = "1.4.1"
3
+
4
+ def self.version
5
+ VERSION
6
+ end
7
+ end
@@ -0,0 +1,2 @@
1
+ # For those people who don't like the cool.io styling...
2
+ require 'cool.io'
@@ -0,0 +1,57 @@
1
+ require File.expand_path('../spec_helper', __FILE__)
2
+ require 'tempfile'
3
+ require 'fcntl'
4
+
5
+ describe Cool.io::AsyncWatcher, :env => :exclude_win do
6
+
7
+ it "does not signal on spurious wakeups" do
8
+ aw = Cool.io::AsyncWatcher.new
9
+ tmp = Tempfile.new('coolio_async_watcher_test')
10
+ nr_fork = 2 # must be at least two for spurious wakeups
11
+
12
+ # We have aetter chance of failing if this overflows the pipe buffer
13
+ # which POSIX requires >= 512 bytes, Linux 2.6 uses 4096 bytes
14
+ nr_signal = 4096 * 4
15
+
16
+ append = File.open(tmp.path, "ab")
17
+ append.sync = true
18
+ rd, wr = ::IO.pipe
19
+
20
+ aw.on_signal { append.syswrite("#$$\n") }
21
+ children = nr_fork.times.map do
22
+ fork do
23
+ trap(:TERM) { exit!(0) }
24
+ rloop = Cool.io::Loop.default
25
+ aw.attach(rloop)
26
+ wr.write '.' # signal to master that we're ready
27
+ rloop.run
28
+ exit!(1) # should not get here
29
+ end
30
+ end
31
+
32
+ # ensure children are ready
33
+ nr_fork.times { expect(rd.sysread(1)).to eq('.') }
34
+
35
+ # send our signals
36
+ nr_signal.times { aw.signal }
37
+
38
+ # wait for the pipe buffer to be consumed by the children
39
+ sleep 1 while tmp.stat.ctime >= (Time.now - 4)
40
+
41
+ children.each do |pid|
42
+ Process.kill(:TERM, pid)
43
+ _, status = Process.waitpid2(pid)
44
+ expect(status.exitstatus).to eq(0)
45
+ end
46
+
47
+ # we should've written a line for every signal we sent
48
+ lines = tmp.readlines
49
+ expect(lines.size).to eq(nr_signal)
50
+
51
+ # theoretically a bad kernel scheduler could give us fewer...
52
+ expect(lines.sort.uniq.size).to eq(nr_fork)
53
+
54
+ tmp.close!
55
+ end
56
+
57
+ end
@@ -0,0 +1,43 @@
1
+ require File.expand_path('../spec_helper', __FILE__)
2
+
3
+ VALID_DOMAIN = "google.com"
4
+ INVALID_DOMAIN = "gibidigibigididibitidibigitibidigitidididi.com"
5
+
6
+ class ItWorked < StandardError; end
7
+ class WontResolve < StandardError; end
8
+
9
+ class ConnectorThingy < Cool.io::TCPSocket
10
+ def on_connect
11
+ raise ItWorked
12
+ end
13
+
14
+ def on_resolve_failed
15
+ raise WontResolve
16
+ end
17
+ end
18
+
19
+ describe "DNS" do
20
+ before :each do
21
+ @loop = Cool.io::Loop.new
22
+ end
23
+
24
+ it "connects to valid domains" do
25
+ begin
26
+ c = ConnectorThingy.connect(VALID_DOMAIN, 80).attach(@loop)
27
+
28
+ expect do
29
+ @loop.run
30
+ end.to raise_error(ItWorked)
31
+ ensure
32
+ c.close
33
+ end
34
+ end
35
+
36
+ it "fires on_resolve_failed for invalid domains" do
37
+ ConnectorThingy.connect(INVALID_DOMAIN, 80).attach(@loop)
38
+
39
+ expect do
40
+ @loop.run
41
+ end.to raise_error(WontResolve)
42
+ end
43
+ end
@@ -0,0 +1,147 @@
1
+ require File.expand_path('../spec_helper', __FILE__)
2
+
3
+
4
+ describe IO::Buffer do
5
+
6
+ let :buffer do
7
+ IO::Buffer.new
8
+ end
9
+
10
+ it "provides a subset of the methods available in Strings" do
11
+ expect(buffer << "foo").to eq "foo"
12
+ expect(buffer << "bar").to eq "bar"
13
+ expect(buffer.to_str).to eq "foobar"
14
+ expect(buffer.to_str).to eq "foobar"
15
+ expect(buffer.size).to eq 6
16
+ end
17
+
18
+ it "provides append and prepend" do
19
+ expect(buffer.append "bar").to eq "bar"
20
+ expect(buffer.prepend "foo").to eq "foo"
21
+ expect(buffer.append "baz").to eq "baz"
22
+ expect(buffer.to_str).to eq "foobarbaz"
23
+ end
24
+
25
+ context "#read" do
26
+ it "can be used to retrieve the contents of a buffer" do
27
+ expect(buffer << "foo").to eq "foo"
28
+ expect(buffer.read 2).to eq "fo"
29
+ expect(buffer << "bar").to eq "bar"
30
+ expect(buffer.read 2).to eq "ob"
31
+ expect(buffer << "baz").to eq "baz"
32
+ expect(buffer.read 3).to eq "arb"
33
+ end
34
+ end
35
+
36
+ describe "provides methods for performing non-blocking I/O" do
37
+ require "tempfile"
38
+
39
+ context "#read_from" do
40
+ context "using local file", :env => :exclude_win do
41
+ let :tmp do
42
+ t = Tempfile.open "read_from"
43
+ t << "foobar"
44
+ t.rewind
45
+ t
46
+ end
47
+
48
+ it "will read as much data as possible" do
49
+ expect(buffer.read_from tmp).to eq 6
50
+ expect(buffer.to_str).to eq "foobar"
51
+ end
52
+ end
53
+
54
+ context "using udp socket" do
55
+ before :each do
56
+ @receiver = UDPSocket.open
57
+ @receiver.bind nil, 0
58
+
59
+ @sender = UDPSocket.open
60
+ @sender.connect "localhost", @receiver.addr[1]
61
+ end
62
+ after :each do
63
+ @receiver.close
64
+ @sender.close
65
+ end
66
+
67
+ it "will read as much data as possible" do
68
+ select [], [@sender]
69
+ @sender.send "foo", 0
70
+ select [@receiver]
71
+ expect(buffer.read_from @receiver).to eq 3
72
+ expect(buffer.to_str).to eq "foo"
73
+
74
+ select [], [@sender]
75
+ @sender.send "barbaz", 0
76
+ select [@receiver]
77
+ expect(buffer.read_from @receiver).to eq 6
78
+ expect(buffer.to_str).to eq "foobarbaz"
79
+ end
80
+ end
81
+ end
82
+
83
+ context "#write_to" do
84
+ context "using local file", :env => :exclude_win do
85
+ let :tmp do
86
+ Tempfile.open "write_to"
87
+ end
88
+ it "writes the contents of the buffer" do
89
+ buffer << "foo"
90
+ expect(buffer.write_to tmp).to eq 3
91
+ tmp.rewind
92
+ expect(tmp.read 3).to eq "foo"
93
+ end
94
+ end
95
+
96
+ context "using udp socket" do
97
+ before :each do
98
+ @receiver = UDPSocket.open
99
+ @receiver.bind nil, 0
100
+
101
+ @sender = UDPSocket.open
102
+ @sender.connect "localhost", @receiver.addr[1]
103
+ end
104
+ after :each do
105
+ @receiver.close
106
+ @sender.close
107
+ end
108
+
109
+ it "will read as much data as possible" do
110
+ buffer << "foo"
111
+ select [], [@sender]
112
+ expect(buffer.write_to @sender).to eq 3
113
+ select [@receiver]
114
+ expect(@receiver.recvfrom_nonblock(3)[0]).to eq "foo"
115
+ end
116
+ end
117
+ end
118
+ end
119
+
120
+ context "#clear" do
121
+ it "clear all data" do
122
+ buffer << "foo"
123
+ expect(buffer.size).to eq 3
124
+ expect(buffer.empty?).to eq false
125
+ buffer.clear
126
+ expect(buffer.size).to eq 0
127
+ expect(buffer.empty?).to eq true
128
+ end
129
+ end
130
+
131
+ context "#read_frame" do
132
+ it "Read up to and including the given frame marker" do
133
+ buffer << "foo\nbarbaz"
134
+ data = ""
135
+ expect(buffer.read_frame data, "\n".ord).to eq true
136
+ expect(buffer.empty?).to eq false
137
+ expect(data).to eq "foo\n"
138
+ expect(buffer.to_str).to eq "barbaz"
139
+
140
+ expect(buffer.read_frame data, "\n".ord).to eq false
141
+ expect(buffer.empty?).to eq true
142
+ expect(data).to eq "foo\nbarbaz"
143
+ expect(buffer.to_str).to eq ""
144
+ end
145
+ end
146
+
147
+ end
@@ -0,0 +1,19 @@
1
+ $LOAD_PATH.unshift File.dirname(__FILE__)
2
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
3
+
4
+ require 'rspec'
5
+ require 'cool.io'
6
+
7
+ def unused_port
8
+ s = TCPServer.open(0)
9
+ port = s.addr[1]
10
+ s.close
11
+ port
12
+ end
13
+
14
+ RSpec.configure do |c|
15
+ if RUBY_PLATFORM =~ /mingw|mswin/
16
+ $stderr.puts "Skip some specs on Windows"
17
+ c.filter_run_excluding :env => :exclude_win
18
+ end
19
+ end
@@ -0,0 +1,77 @@
1
+ require File.expand_path('../spec_helper', __FILE__)
2
+
3
+ TEMP_FILE_PATH = "./test.txt"
4
+
5
+ INTERVAL = 0.010
6
+
7
+ class MyStatWatcher < Cool.io::StatWatcher
8
+ attr_accessor :accessed, :previous, :current
9
+
10
+ def initialize(path)
11
+ super path, INTERVAL
12
+ end
13
+
14
+ def on_change(previous, current)
15
+ self.accessed = true
16
+ self.previous = previous
17
+ self.current = current
18
+ end
19
+ end
20
+
21
+ def run_with_file_change(path)
22
+ reactor = Cool.io::Loop.new
23
+
24
+ sw = MyStatWatcher.new(path)
25
+ sw.attach(reactor)
26
+
27
+ tw = Cool.io::TimerWatcher.new(INTERVAL, true)
28
+ tw.on_timer do
29
+ reactor.stop if sw.accessed
30
+ write_file(path)
31
+ end
32
+ tw.attach(reactor)
33
+
34
+ reactor.run
35
+
36
+ tw.detach
37
+ sw.detach
38
+
39
+ sw
40
+ end
41
+
42
+ def write_file(path)
43
+ File.open(path, "w+") { |f| f.write(rand.to_s) }
44
+ end
45
+
46
+ def delete_file(path)
47
+ File.delete(TEMP_FILE_PATH)
48
+ end
49
+
50
+ describe Cool.io::StatWatcher do
51
+
52
+ let :watcher do
53
+ run_with_file_change(TEMP_FILE_PATH)
54
+ end
55
+
56
+ before :each do
57
+ write_file(TEMP_FILE_PATH)
58
+ end
59
+
60
+ after :each do
61
+ delete_file(TEMP_FILE_PATH)
62
+ end
63
+
64
+ it "fire on_change when the file it is watching is modified" do
65
+ expect(watcher.accessed).to eq(true)
66
+ end
67
+
68
+ it "should pass previous and current file stat info given a stat watcher" do
69
+ expect(watcher.previous.ino).to eq(watcher.current.ino)
70
+ end
71
+
72
+ it "should raise when the handler does not take 2 parameters" do
73
+ class MyStatWatcher < Cool.io::StatWatcher; def on_change; end; end
74
+ expect { watcher.accessed }.to raise_error(ArgumentError)
75
+ end
76
+
77
+ end
@@ -0,0 +1,225 @@
1
+ require File.expand_path('../spec_helper', __FILE__)
2
+
3
+ TIMEOUT = 0.010
4
+ HOST = '127.0.0.1'
5
+ PORT = unused_port
6
+
7
+ def send_data(data)
8
+ io = TCPSocket.new('127.0.0.1', PORT)
9
+ begin
10
+ io.write data
11
+ ensure
12
+ io.close
13
+ end
14
+ end
15
+
16
+ class MyConnection < Coolio::Socket
17
+ attr_accessor :data, :connected, :closed
18
+
19
+ def initialize(io, on_message)
20
+ super(io)
21
+ @on_message = on_message
22
+ end
23
+
24
+ def on_connect
25
+ @connected = true
26
+ end
27
+
28
+ def on_close
29
+ @closed = true
30
+ end
31
+
32
+ def on_read(data)
33
+ @on_message.call(data)
34
+ end
35
+ end
36
+
37
+ @data = ""
38
+ def on_message(data)
39
+ @data = data
40
+ end
41
+
42
+ def test_run(data = nil)
43
+ reactor = Coolio::Loop.new
44
+ server = Cool.io::TCPServer.new(HOST, PORT, MyConnection, method(:on_message))
45
+ reactor.attach(server)
46
+ thread = Thread.new { reactor.run }
47
+ send_data(data) if data
48
+ sleep TIMEOUT
49
+ reactor.stop
50
+ server.detach
51
+ send_data('') # to leave from blocking loop
52
+ thread.join
53
+ @data
54
+ ensure
55
+ server.close
56
+ end
57
+
58
+ def test_run_once(data = nil)
59
+ reactor = Coolio::Loop.new
60
+ server = Cool.io::TCPServer.new(HOST, PORT, MyConnection, method(:on_message))
61
+ reactor.attach(server)
62
+ thread = Thread.new do
63
+ reactor.run_once # on_connect
64
+ reactor.run_once # on_read
65
+ end
66
+ send_data(data) if data
67
+ thread.join
68
+ server.detach
69
+ @data
70
+ ensure
71
+ server.close
72
+ end
73
+
74
+ def test_run_once_timeout(timeout = TIMEOUT)
75
+ reactor = Coolio::Loop.new
76
+ server = Cool.io::TCPServer.new(HOST, PORT, MyConnection, method(:on_message))
77
+ reactor.attach(server)
78
+ running = true
79
+ thread = Thread.new { reactor.run_once(timeout) }
80
+ sleep timeout
81
+ server.detach
82
+ thread.join
83
+ @data
84
+ ensure
85
+ server.close
86
+ end
87
+
88
+ def test_run_timeout(data = nil, timeout = TIMEOUT)
89
+ reactor = Coolio::Loop.new
90
+ server = Cool.io::TCPServer.new(HOST, PORT, MyConnection, method(:on_message))
91
+ reactor.attach(server)
92
+ running = true
93
+ thread = Thread.new do
94
+ while running and reactor.has_active_watchers?
95
+ reactor.run_once(timeout)
96
+ end
97
+ end
98
+ send_data(data) if data
99
+ sleep timeout
100
+ server.detach
101
+ running = false # another send is not required
102
+ thread.join
103
+ @data
104
+ ensure
105
+ server.close
106
+ end
107
+
108
+ # This test should work on Windows
109
+ describe Coolio::TCPServer do
110
+
111
+ it '#run' do
112
+ expect(test_run("hello")).to eq("hello")
113
+ end
114
+
115
+ it '#run_once' do
116
+ expect(test_run_once("hello")).to eq("hello")
117
+ end
118
+
119
+ it '#run_once(timeout)' do
120
+ test_run_once_timeout # should not block
121
+ end
122
+
123
+ it '#run_once(-timeout)' do
124
+ expect { test_run_once_timeout(-0.1) }.to raise_error(ArgumentError)
125
+ end
126
+
127
+ it '#run(timeout)' do
128
+ expect(test_run_timeout("hello")).to eq("hello")
129
+ end
130
+
131
+ describe "functionaltest" do
132
+ let :loop do
133
+ Coolio::Loop.new
134
+ end
135
+
136
+ let :port do
137
+ unused_port
138
+ end
139
+
140
+ context "#on_connect" do
141
+ class ServerOnConnect < Coolio::Socket
142
+ def initialize(io, cb)
143
+ super(io)
144
+ @cb = cb
145
+ end
146
+ def on_connect
147
+ @cb.call
148
+ end
149
+ end
150
+
151
+ it "connected socket called on_connect" do
152
+ begin
153
+ connected = false
154
+ server = Cool.io::TCPServer.new("localhost", port, ServerOnConnect, proc { connected = true })
155
+ loop.attach server
156
+ s = TCPSocket.open("localhost", port)
157
+ loop.run_once
158
+ s.close
159
+ expect(connected).to eq true
160
+ ensure
161
+ server.detach
162
+ end
163
+ end
164
+ end
165
+
166
+ context "#on_close" do
167
+ class ServerOnClose < Coolio::Socket
168
+ def initialize(io, cb)
169
+ super(io)
170
+ @cb = cb
171
+ end
172
+ def on_close
173
+ @cb.call
174
+ end
175
+ end
176
+
177
+ it "closed socket called on_close" do
178
+ begin
179
+ closed = false
180
+ server = Cool.io::TCPServer.new("localhost", port, ServerOnConnect, proc { closed = true })
181
+ loop.attach server
182
+ s = TCPSocket.open("localhost", port)
183
+ loop.run_once
184
+ s.close
185
+ loop.run_once
186
+ expect(closed).to eq true
187
+ ensure
188
+ server.detach
189
+ end
190
+ end
191
+ end
192
+
193
+ context "#on_read" do
194
+ class Echo < Coolio::Socket
195
+ def initialize(io, cb)
196
+ super(io)
197
+ @cb = cb
198
+ end
199
+ def on_read(data)
200
+ @cb.call data
201
+ size = write(data + "fff")
202
+ end
203
+ end
204
+
205
+ it "server socket received data" do
206
+ begin
207
+ data = "aaa"
208
+ server = Cool.io::TCPServer.new("localhost", port, Echo, proc { |d| data = d })
209
+ loop.attach server
210
+ thread = Thread.new { loop.run }
211
+ s = TCPSocket.open("localhost", port)
212
+ s.write "zzz"
213
+ sleep 0.1
214
+ expect(data).to eq "zzz"
215
+ expect(s.read 6).to eq "zzzfff"
216
+ ensure
217
+ s.close
218
+ loop.stop
219
+ server.detach
220
+ thread.join
221
+ end
222
+ end
223
+ end
224
+ end
225
+ end