cool.io 1.0.0 → 1.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 (83) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/test.yaml +54 -0
  3. data/.gitignore +30 -0
  4. data/.rspec +3 -0
  5. data/{CHANGES → CHANGES.md} +175 -79
  6. data/{README.markdown → README.md} +43 -49
  7. data/Rakefile +44 -69
  8. data/appveyor.yml +18 -0
  9. data/cool.io.gemspec +18 -131
  10. data/examples/callbacked_echo_server.rb +24 -0
  11. data/ext/cool.io/buffer.c +781 -0
  12. data/ext/cool.io/cool.io.h +13 -0
  13. data/ext/cool.io/cool.io_ext.c +1 -0
  14. data/ext/cool.io/ev_wrap.h +4 -2
  15. data/ext/cool.io/extconf.rb +27 -16
  16. data/ext/cool.io/iowatcher.c +17 -8
  17. data/ext/cool.io/loop.c +36 -84
  18. data/ext/cool.io/stat_watcher.c +103 -25
  19. data/ext/cool.io/timer_watcher.c +3 -3
  20. data/ext/cool.io/utils.c +5 -0
  21. data/ext/cool.io/watcher.h +1 -1
  22. data/ext/libev/Changes +152 -3
  23. data/ext/libev/LICENSE +2 -1
  24. data/ext/libev/README +8 -8
  25. data/ext/libev/ev.c +1719 -348
  26. data/ext/libev/ev.h +146 -118
  27. data/ext/libev/ev_epoll.c +71 -20
  28. data/ext/libev/ev_kqueue.c +36 -16
  29. data/ext/libev/ev_poll.c +14 -11
  30. data/ext/libev/ev_port.c +43 -18
  31. data/ext/libev/ev_select.c +82 -25
  32. data/ext/libev/ev_vars.h +28 -21
  33. data/ext/libev/ev_win32.c +19 -10
  34. data/ext/libev/ev_wrap.h +166 -152
  35. data/ext/libev/test_libev_win32.c +1 -1
  36. data/gems.rb +9 -0
  37. data/lib/cool.io/async_watcher.rb +1 -1
  38. data/lib/cool.io/custom_require.rb +9 -0
  39. data/lib/cool.io/dns_resolver.rb +30 -22
  40. data/lib/cool.io/dsl.rb +32 -28
  41. data/lib/cool.io/io.rb +55 -35
  42. data/lib/cool.io/iowatcher.rb +1 -1
  43. data/lib/cool.io/listener.rb +35 -17
  44. data/lib/cool.io/loop.rb +17 -25
  45. data/lib/cool.io/meta.rb +6 -5
  46. data/lib/cool.io/server.rb +5 -4
  47. data/lib/cool.io/socket.rb +41 -30
  48. data/lib/cool.io/timer_watcher.rb +1 -1
  49. data/lib/cool.io/version.rb +7 -0
  50. data/lib/cool.io.rb +11 -10
  51. data/lib/coolio.rb +1 -1
  52. data/libev_ruby_gil.diff +175 -0
  53. data/libev_win_select.diff +130 -0
  54. data/spec/async_watcher_spec.rb +5 -5
  55. data/spec/dns_spec.rb +27 -8
  56. data/spec/iobuffer_spec.rb +147 -0
  57. data/spec/spec_helper.rb +15 -1
  58. data/spec/stat_watcher_spec.rb +81 -0
  59. data/spec/tcp_server_spec.rb +225 -0
  60. data/spec/tcp_socket_spec.rb +185 -0
  61. data/spec/timer_watcher_spec.rb +23 -19
  62. data/spec/udp_socket_spec.rb +58 -0
  63. data/spec/unix_listener_spec.rb +8 -8
  64. data/spec/unix_server_spec.rb +10 -8
  65. metadata +109 -112
  66. data/VERSION +0 -1
  67. data/examples/httpclient.rb +0 -38
  68. data/ext/http11_client/.gitignore +0 -5
  69. data/ext/http11_client/LICENSE +0 -31
  70. data/ext/http11_client/ext_help.h +0 -14
  71. data/ext/http11_client/extconf.rb +0 -6
  72. data/ext/http11_client/http11_client.c +0 -300
  73. data/ext/http11_client/http11_parser.c +0 -403
  74. data/ext/http11_client/http11_parser.h +0 -48
  75. data/ext/http11_client/http11_parser.rl +0 -173
  76. data/lib/cool.io/eventmachine.rb +0 -234
  77. data/lib/cool.io/http_client.rb +0 -419
  78. data/lib/rev.rb +0 -4
  79. data/spec/possible_tests/schedules_other_threads.rb +0 -48
  80. data/spec/possible_tests/test_on_resolve_failed.rb +0 -9
  81. data/spec/possible_tests/test_resolves.rb +0 -27
  82. data/spec/possible_tests/test_write_during_resolve.rb +0 -27
  83. data/spec/possible_tests/works_straight.rb +0 -71
@@ -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
+ @data = ""
76
+ reactor = Coolio::Loop.new
77
+ server = Cool.io::TCPServer.new(HOST, PORT, MyConnection, method(:on_message))
78
+ reactor.attach(server)
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
@@ -0,0 +1,185 @@
1
+ require File.expand_path('../spec_helper', __FILE__)
2
+
3
+ describe Coolio::TCPSocket do
4
+ let :loop do
5
+ Coolio::Loop.new
6
+ end
7
+
8
+ before :each do
9
+ @echo = TCPServer.new("127.0.0.1", 0)
10
+ @host = @echo.addr[3]
11
+ @port = @echo.addr[1]
12
+ @running = true
13
+ @echo_thread = Thread.new do
14
+ socks = [@echo]
15
+ begin
16
+ serv socks
17
+ ensure
18
+ socks.each do |s|
19
+ s.close
20
+ end
21
+ end
22
+ Thread.pass
23
+ end
24
+ end
25
+
26
+ def serv(socks)
27
+ while @running
28
+ selected = select(socks, [], [], 0.1)
29
+ next if selected.nil?
30
+ selected[0].each do |s|
31
+ if s == @echo
32
+ socks.push s.accept
33
+ next
34
+ end
35
+ begin
36
+ unless s.eof?
37
+ s.write(s.read_nonblock 1)
38
+ end
39
+ rescue SystemCallError, EOFError, IOError, SocketError
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ def shutdown
46
+ if @running
47
+ @running = false
48
+ @echo_thread.join
49
+ end
50
+ end
51
+
52
+ after :each do
53
+ shutdown
54
+ end
55
+
56
+ context "#close" do
57
+ it "detaches all watchers on #close before loop#run" do
58
+ client = Coolio::TCPSocket.connect(@host, @port)
59
+ loop.attach client
60
+ client.close
61
+ expect(loop.watchers.size).to eq 0
62
+ end
63
+ end
64
+
65
+ context "#on_connect" do
66
+ class OnConnect < Cool.io::TCPSocket
67
+ attr :connected
68
+ def on_connect
69
+ @connected = true
70
+ end
71
+ end
72
+
73
+ it "connected client called on_connect" do
74
+ begin
75
+ c = OnConnect.connect(@host, @port)
76
+ loop.attach c
77
+ loop.run_once
78
+ expect(c.connected).to eq true
79
+ ensure
80
+ c.close
81
+ end
82
+ end
83
+ end
84
+
85
+ context "#on_connect_failed" do
86
+ class OnConnectFailed < Cool.io::TCPSocket
87
+ attr :connect_failed
88
+ def on_connect_failed
89
+ @connect_failed = true
90
+ end
91
+ end
92
+
93
+ it "try to connect dead host" do
94
+ serv = TCPServer.new(0)
95
+ dead_host = serv.addr[3]
96
+ dead_port = serv.addr[1]
97
+ serv.close
98
+
99
+ c = OnConnectFailed.connect(dead_host, dead_port)
100
+ loop.attach c
101
+ loop.run_once # on_connect_failed
102
+ expect(c.connect_failed).to eq true
103
+ end
104
+ end
105
+
106
+ context "#on_close" do
107
+ class Closed < StandardError; end
108
+ class OnClose < Cool.io::TCPSocket
109
+ def on_close
110
+ raise Closed
111
+ end
112
+ end
113
+
114
+ let :client do
115
+ OnClose.connect(@host, @port)
116
+ end
117
+
118
+ before :each do
119
+ loop.attach client
120
+ loop.run_once # on_connect
121
+ client.write "0"
122
+ end
123
+
124
+ it "disconnect from client" do
125
+ expect { client.close }.to raise_error(Closed)
126
+ end
127
+
128
+ it "disconnect from server" do
129
+ shutdown
130
+ expect { loop.run }.to raise_error(Closed)
131
+ end
132
+ end
133
+
134
+ context "#on_read" do
135
+ class Finished < StandardError; end
136
+ class OnRead < Cool.io::TCPSocket
137
+ attr :read_data, :times
138
+ def on_connect
139
+ @read_data = ""
140
+ @times = 0
141
+ end
142
+ def on_read(data)
143
+ @read_data += data
144
+ @times += 1
145
+ if @times < 5
146
+ write "#{@times}"
147
+ else
148
+ close
149
+ raise Finished
150
+ end
151
+ end
152
+ end
153
+
154
+ it "receive 5 times" do
155
+ c = OnRead.connect(@host, @port)
156
+ loop.attach c
157
+ loop.run_once # on_connect
158
+ c.write "0"
159
+ expect { loop.run }.to raise_error(Finished)
160
+
161
+ expect(c.times).to eq 5
162
+ expect(c.read_data).to eq "01234"
163
+ end
164
+ end
165
+
166
+ context "#on_write_complete" do
167
+ class WriteComplete < StandardError; end
168
+ class OnWriteComplete < Cool.io::TCPSocket
169
+ attr :called
170
+ def on_write_complete
171
+ @called = true
172
+ close
173
+ raise WriteComplete
174
+ end
175
+ end
176
+
177
+ it "on_write_complete is called" do
178
+ c = OnWriteComplete.connect(@host, @port)
179
+ loop.attach c
180
+ loop.run_once # on_connect
181
+ c.write "aaa"
182
+ expect { loop.run }.to raise_error(WriteComplete)
183
+ end
184
+ end
185
+ end
@@ -4,15 +4,19 @@ describe Cool.io::TimerWatcher do
4
4
 
5
5
  interval = 0.010
6
6
 
7
+ let :loop do
8
+ Cool.io::Loop.new
9
+ end
10
+
7
11
  it "can have the on_timer callback defined after creation" do
8
12
  @watcher = Cool.io::TimerWatcher.new(interval, true)
9
13
  nr = '0'
10
- @watcher.on_timer { nr.succ! }.should == nil
11
- @watcher.attach(Cool.io::Loop.default).should == @watcher
12
- nr.should == '0'
14
+ expect(@watcher.on_timer { nr.succ! }).to be_nil
15
+ expect(@watcher.attach(loop)).to eq(@watcher)
16
+ expect(nr).to eq('0')
13
17
  sleep interval
14
- Cool.io::Loop.default.run_once
15
- nr.should == '1'
18
+ loop.run_once
19
+ expect(nr).to eq('1')
16
20
  end
17
21
 
18
22
  it "can be subclassed" do
@@ -24,29 +28,29 @@ describe Cool.io::TimerWatcher do
24
28
  end
25
29
  end
26
30
  @watcher = MyTimerWatcher.new(interval, true)
27
- @watcher.attach(Cool.io::Loop.default).should == @watcher
28
- MyTimerWatcher::TMP.should == '0'
31
+ expect(@watcher.attach(loop)).to eq(@watcher)
32
+ expect(MyTimerWatcher::TMP).to eq('0')
29
33
  sleep interval
30
- Cool.io::Loop.default.run_once
31
- MyTimerWatcher::TMP.should == '1'
34
+ loop.run_once
35
+ expect(MyTimerWatcher::TMP).to eq('1')
32
36
  end
33
37
 
34
38
  it "can have the on_timer callback redefined between runs" do
35
39
  @watcher = Cool.io::TimerWatcher.new(interval, true)
36
40
  nr = '0'
37
- @watcher.on_timer { nr.succ! }.should == nil
38
- @watcher.attach(Cool.io::Loop.default).should == @watcher
39
- nr.should == '0'
41
+ expect(@watcher.on_timer { nr.succ! }).to be_nil
42
+ expect(@watcher.attach(loop)).to eq(@watcher)
43
+ expect(nr).to eq('0')
40
44
  sleep interval
41
- Cool.io::Loop.default.run_once
42
- nr.should == '1'
45
+ loop.run_once
46
+ expect(nr).to eq('1')
43
47
  @watcher.detach
44
- @watcher.on_timer { nr = :foo }.should == nil
45
- @watcher.attach(Cool.io::Loop.default).should == @watcher
46
- nr.should == '1'
48
+ expect(@watcher.on_timer { nr = :foo }).to be_nil
49
+ expect(@watcher.attach(loop)).to eq(@watcher)
50
+ expect(nr).to eq('1')
47
51
  sleep interval
48
- Cool.io::Loop.default.run_once
49
- nr.should == :foo
52
+ loop.run_once
53
+ expect(nr).to eq(:foo)
50
54
  end
51
55
 
52
56
  after :each do
@@ -0,0 +1,58 @@
1
+ require File.expand_path('../spec_helper', __FILE__)
2
+
3
+ describe "Coolio::UDPSocket" do
4
+ let :loop do
5
+ Coolio::Loop.new
6
+ end
7
+
8
+ before :each do
9
+ @echo = UDPSocket.open
10
+ @echo.bind nil, 0
11
+ @port = @echo.addr[1]
12
+
13
+ @running = true
14
+ @echo_thread = Thread.new do
15
+ while @running
16
+ begin
17
+ msg, sender = @echo.recvfrom_nonblock(3)
18
+ @echo.send(msg + "bbb", 0, sender[3], sender[1])
19
+ rescue IO::WaitReadable
20
+ end
21
+ Thread.pass
22
+ end
23
+ end
24
+ end
25
+
26
+ after :each do
27
+ @running = false
28
+ @echo_thread.join
29
+ @echo.close
30
+ end
31
+
32
+ class Readable < Cool.io::IOWatcher
33
+ attr :socket, :received
34
+ def initialize
35
+ @socket = UDPSocket.new
36
+ super(@socket)
37
+ end
38
+
39
+ def on_readable
40
+ @received = @socket.recvfrom_nonblock(6).first
41
+ end
42
+ end
43
+
44
+ it "receive message #on_readable 5 times" do
45
+ 5.times do
46
+ begin
47
+ r = Readable.new
48
+ r.socket.send "aaa", 0, "localhost", @port
49
+
50
+ loop.attach r
51
+ loop.run_once
52
+ expect(r.received).to eq "aaabbb"
53
+ ensure
54
+ r.detach
55
+ end
56
+ end
57
+ end
58
+ end
@@ -1,25 +1,25 @@
1
1
  require File.expand_path('../spec_helper', __FILE__)
2
2
  require 'tempfile'
3
3
 
4
- describe Cool.io::UNIXListener do
4
+ describe Cool.io::UNIXListener, :env => :exclude_win do
5
5
 
6
6
  before :each do
7
7
  @tmp = Tempfile.new('coolio_unix_listener_spec')
8
- File.unlink(@tmp.path).should == 1
9
- File.exist?(@tmp.path).should == false
8
+ expect(File.unlink(@tmp.path)).to eq(1)
9
+ expect(File.exist?(@tmp.path)).to eq(false)
10
10
  end
11
11
 
12
12
  it "creates a new UNIXListener" do
13
- listener = Cool.io::UNIXListener.new(@tmp.path)
14
- File.socket?(@tmp.path).should == true
13
+ _listener = Cool.io::UNIXListener.new(@tmp.path)
14
+ expect(File.socket?(@tmp.path)).to eq(true)
15
15
  end
16
16
 
17
17
  it "builds off an existing UNIXServer" do
18
18
  unix_server = UNIXServer.new(@tmp.path)
19
- File.socket?(@tmp.path).should == true
19
+ expect(File.socket?(@tmp.path)).to eq(true)
20
20
  listener = Cool.io::UNIXListener.new(unix_server)
21
- File.socket?(@tmp.path).should == true
22
- listener.fileno.should == unix_server.fileno
21
+ expect(File.socket?(@tmp.path)).to eq(true)
22
+ expect(listener.fileno).to eq(unix_server.fileno)
23
23
  end
24
24
 
25
25
  end
@@ -1,25 +1,27 @@
1
1
  require File.expand_path('../spec_helper', __FILE__)
2
2
  require 'tempfile'
3
3
 
4
- describe Cool.io::UNIXServer do
4
+ describe Cool.io::UNIXServer, :env => :exclude_win do
5
5
 
6
6
  before :each do
7
7
  @tmp = Tempfile.new('coolio_unix_server_spec')
8
- File.unlink(@tmp.path).should == 1
9
- File.exist?(@tmp.path).should == false
8
+ expect(File.unlink(@tmp.path)).to eq(1)
9
+ expect(File.exist?(@tmp.path)).to eq(false)
10
10
  end
11
11
 
12
12
  it "creates a new Cool.io::UNIXServer" do
13
13
  listener = Cool.io::UNIXListener.new(@tmp.path)
14
- File.socket?(@tmp.path).should == true
14
+ listener.listen(24)
15
+ expect(File.socket?(@tmp.path)).to eq(true)
15
16
  end
16
17
 
17
18
  it "builds off an existing ::UNIXServer" do
18
19
  unix_server = ::UNIXServer.new(@tmp.path)
19
- File.socket?(@tmp.path).should == true
20
- listener = Cool.io::UNIXServer.new(unix_server)
21
- File.socket?(@tmp.path).should == true
22
- listener.fileno.should == unix_server.fileno
20
+ expect(File.socket?(@tmp.path)).to eq(true)
21
+ listener = Cool.io::UNIXServer.new(unix_server, Coolio::UNIXSocket)
22
+ listener.listen(24)
23
+ expect(File.socket?(@tmp.path)).to eq(true)
24
+ expect(listener.fileno).to eq(unix_server.fileno)
23
25
  end
24
26
 
25
27
  end