polyphony 0.13 → 0.14
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 +4 -4
- data/.gitbook.yaml +5 -0
- data/.gitignore +55 -0
- data/.rubocop.yml +49 -0
- data/CHANGELOG.md +13 -2
- data/Gemfile +3 -0
- data/Gemfile.lock +31 -0
- data/LICENSE +21 -0
- data/README.md +35 -18
- data/Rakefile +20 -0
- data/TODO.md +49 -0
- data/docs/getting-started/getting-started.md +10 -0
- data/docs/getting-started/tutorial.md +2 -0
- data/docs/summary.md +9 -0
- data/examples/core/cancel.rb +10 -0
- data/examples/core/channel_echo.rb +43 -0
- data/examples/core/enumerator.rb +14 -0
- data/examples/core/fork.rb +22 -0
- data/examples/core/genserver.rb +74 -0
- data/examples/core/lock.rb +20 -0
- data/examples/core/move_on.rb +11 -0
- data/examples/core/move_on_twice.rb +17 -0
- data/examples/core/move_on_with_ensure.rb +17 -0
- data/examples/core/multiple_async.rb +17 -0
- data/examples/core/nested_async.rb +18 -0
- data/examples/core/nested_cancel.rb +41 -0
- data/examples/core/nested_multiple_async.rb +19 -0
- data/examples/core/next_tick.rb +13 -0
- data/examples/core/pulse.rb +13 -0
- data/examples/core/resource.rb +29 -0
- data/examples/core/resource_cancel.rb +34 -0
- data/examples/core/resource_delegate.rb +32 -0
- data/examples/core/sleep.rb +9 -0
- data/examples/core/sleep2.rb +13 -0
- data/examples/core/spawn.rb +15 -0
- data/examples/core/spawn_cancel.rb +19 -0
- data/examples/core/spawn_error.rb +28 -0
- data/examples/core/supervisor.rb +22 -0
- data/examples/core/supervisor_with_cancel_scope.rb +24 -0
- data/examples/core/supervisor_with_error.rb +23 -0
- data/examples/core/supervisor_with_manual_move_on.rb +25 -0
- data/examples/core/thread.rb +30 -0
- data/examples/core/thread_cancel.rb +30 -0
- data/examples/core/thread_pool.rb +60 -0
- data/examples/core/throttle.rb +17 -0
- data/examples/fs/read.rb +37 -0
- data/examples/interfaces/pg_client.rb +38 -0
- data/examples/interfaces/pg_pool.rb +37 -0
- data/examples/interfaces/pg_query.rb +32 -0
- data/examples/interfaces/redis_channels.rb +119 -0
- data/examples/interfaces/redis_client.rb +21 -0
- data/examples/interfaces/redis_pubsub.rb +26 -0
- data/examples/interfaces/redis_pubsub_perf.rb +65 -0
- data/examples/io/config.ru +3 -0
- data/examples/io/echo_client.rb +22 -0
- data/examples/io/echo_server.rb +14 -0
- data/examples/io/echo_server_with_timeout.rb +33 -0
- data/examples/io/echo_stdin.rb +15 -0
- data/examples/io/happy_eyeballs.rb +32 -0
- data/examples/io/http_client.rb +19 -0
- data/examples/io/http_server.js +24 -0
- data/examples/io/http_server.rb +16 -0
- data/examples/io/http_server_forked.rb +27 -0
- data/examples/io/http_server_throttled.rb +16 -0
- data/examples/io/http_ws_server.rb +42 -0
- data/examples/io/https_client.rb +17 -0
- data/examples/io/https_server.rb +23 -0
- data/examples/io/https_wss_server.rb +46 -0
- data/examples/io/rack_server.rb +19 -0
- data/examples/io/rack_server_https.rb +24 -0
- data/examples/io/rack_server_https_forked.rb +32 -0
- data/examples/io/websocket_server.rb +33 -0
- data/examples/io/ws_page.html +34 -0
- data/examples/io/wss_page.html +34 -0
- data/examples/performance/perf_multi_snooze.rb +21 -0
- data/examples/performance/perf_snooze.rb +30 -0
- data/examples/performance/thread-vs-fiber/polyphony_server.rb +63 -0
- data/examples/performance/thread-vs-fiber/threaded_server.rb +27 -0
- data/examples/streams/lines.rb +27 -0
- data/examples/streams/stdio.rb +18 -0
- data/ext/ev/async.c +168 -0
- data/ext/ev/child.c +169 -0
- data/ext/ev/ev.h +32 -0
- data/ext/ev/ev_ext.c +20 -0
- data/ext/ev/ev_module.c +222 -0
- data/ext/ev/io.c +405 -0
- data/ext/ev/libev.h +9 -0
- data/ext/ev/signal.c +119 -0
- data/ext/ev/timer.c +197 -0
- data/ext/libev/Changes +513 -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 +5214 -0
- data/ext/libev/ev.h +849 -0
- data/ext/libev/ev_epoll.c +285 -0
- data/ext/libev/ev_kqueue.c +218 -0
- data/ext/libev/ev_poll.c +151 -0
- data/ext/libev/ev_port.c +189 -0
- data/ext/libev/ev_select.c +316 -0
- data/ext/libev/ev_vars.h +204 -0
- data/ext/libev/ev_win32.c +162 -0
- data/ext/libev/ev_wrap.h +200 -0
- data/ext/libev/test_libev_win32.c +123 -0
- data/lib/polyphony.rb +7 -2
- data/lib/polyphony/core.rb +1 -1
- data/lib/polyphony/core/{coroutine.rb → coprocess.rb} +10 -10
- data/lib/polyphony/core/exceptions.rb +5 -5
- data/lib/polyphony/core/supervisor.rb +16 -16
- data/lib/polyphony/core/thread.rb +1 -1
- data/lib/polyphony/extensions/io.rb +43 -42
- data/lib/polyphony/extensions/kernel.rb +10 -34
- data/lib/polyphony/extensions/postgres.rb +3 -2
- data/lib/polyphony/extensions/redis.rb +1 -1
- data/lib/polyphony/extensions/socket.rb +8 -4
- data/lib/polyphony/extensions/ssl.rb +0 -54
- data/lib/polyphony/http/agent.rb +4 -10
- data/lib/polyphony/http/http1.rb +25 -25
- data/lib/polyphony/http/http1_request.rb +38 -26
- data/lib/polyphony/http/http2.rb +4 -5
- data/lib/polyphony/http/http2_request.rb +12 -18
- data/lib/polyphony/http/rack.rb +1 -3
- data/lib/polyphony/http/server.rb +9 -9
- data/lib/polyphony/net.rb +2 -2
- data/lib/polyphony/resource_pool.rb +5 -1
- data/lib/polyphony/version.rb +1 -1
- data/lib/polyphony/websocket.rb +52 -0
- data/polyphony.gemspec +31 -0
- data/test/test_coprocess.rb +131 -0
- data/test/test_core.rb +274 -0
- data/test/test_ev.rb +117 -0
- data/test/test_io.rb +38 -0
- metadata +113 -7
- data/lib/polyphony/core/async.rb +0 -36
- data/lib/polyphony/net_old.rb +0 -299
data/test/test_ev.rb
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
require 'minitest/autorun'
|
|
2
|
+
require 'modulation'
|
|
3
|
+
|
|
4
|
+
module EVTest
|
|
5
|
+
Core = import('../lib/polyphony/core')
|
|
6
|
+
|
|
7
|
+
class RunTest < Minitest::Test
|
|
8
|
+
def setup
|
|
9
|
+
EV.break
|
|
10
|
+
EV.restart
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def test_that_run_loop_returns_immediately_if_no_watchers
|
|
14
|
+
t0 = Time.now
|
|
15
|
+
suspend
|
|
16
|
+
t1 = Time.now
|
|
17
|
+
assert (t1 - t0) < 0.001
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class TimerTest < MiniTest::Test
|
|
22
|
+
def setup
|
|
23
|
+
EV.break
|
|
24
|
+
EV.restart
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def test_that_one_shot_timer_works
|
|
28
|
+
count = 0
|
|
29
|
+
t = EV::Timer.new(0.01, 0)
|
|
30
|
+
t.start { count += 1}
|
|
31
|
+
suspend
|
|
32
|
+
assert_equal(1, count)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def test_that_repeating_timer_works
|
|
36
|
+
count = 0
|
|
37
|
+
t = EV::Timer.new(0.001, 0.001)
|
|
38
|
+
t.start { count += 1; t.stop if count >= 3}
|
|
39
|
+
suspend
|
|
40
|
+
assert_equal(3, count)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
class IOTest < MiniTest::Test
|
|
45
|
+
def setup
|
|
46
|
+
EV.break
|
|
47
|
+
EV.restart
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def test_that_reading_works
|
|
51
|
+
i, o = IO.pipe
|
|
52
|
+
data = +''
|
|
53
|
+
w = EV::IO.new(i, :r)
|
|
54
|
+
w.start do
|
|
55
|
+
i.read_nonblock(8192, data)
|
|
56
|
+
w.stop unless data.empty?
|
|
57
|
+
end
|
|
58
|
+
EV::Timer.new(0, 0).start { o << 'hello' }
|
|
59
|
+
suspend
|
|
60
|
+
assert_equal('hello', data)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
class SignalTest < MiniTest::Test
|
|
65
|
+
def setup
|
|
66
|
+
EV.break
|
|
67
|
+
EV.restart
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def test_that_signal_watcher_receives_signal
|
|
71
|
+
sig = Signal.list['USR1']
|
|
72
|
+
count = 0
|
|
73
|
+
w = EV::Signal.new(sig) { count += 1; w.stop }
|
|
74
|
+
Thread.new { sync_sleep 0.001; Process.kill(:USR1, Process.pid) }
|
|
75
|
+
suspend
|
|
76
|
+
assert_equal(1, count)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def test_that_signal_watcher_receives_signal
|
|
80
|
+
count = 0
|
|
81
|
+
w = Core.trap(:usr1, true) { count += 1; w.stop }
|
|
82
|
+
assert_kind_of(EV::Signal, w)
|
|
83
|
+
Thread.new { sync_sleep 0.001; Process.kill(:USR1, Process.pid) }
|
|
84
|
+
suspend
|
|
85
|
+
assert_equal(1, count)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
class AsyncTest < MiniTest::Test
|
|
90
|
+
def setup
|
|
91
|
+
EV.break
|
|
92
|
+
EV.restart
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def test_that_async_watcher_receives_signal_across_threads
|
|
96
|
+
count = 0
|
|
97
|
+
a = EV::Async.new { count += 1; a.stop }
|
|
98
|
+
Thread.new { sync_sleep 0.001; a.signal! }
|
|
99
|
+
suspend
|
|
100
|
+
assert_equal(1, count)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def test_that_async_watcher_coalesces_signals
|
|
104
|
+
count = 0
|
|
105
|
+
a = EV::Async.new do
|
|
106
|
+
count += 1
|
|
107
|
+
EV::Timer.new(0.01, 0).start { a.stop }
|
|
108
|
+
end
|
|
109
|
+
Thread.new do
|
|
110
|
+
sync_sleep 0.001
|
|
111
|
+
3.times { a.signal! }
|
|
112
|
+
end
|
|
113
|
+
suspend
|
|
114
|
+
assert_equal(1, count)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
data/test/test_io.rb
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
require 'minitest/autorun'
|
|
2
|
+
require 'modulation'
|
|
3
|
+
|
|
4
|
+
module IOTests
|
|
5
|
+
Core = import('../lib/polyphony/core')
|
|
6
|
+
import('../lib/polyphony/extensions/io')
|
|
7
|
+
|
|
8
|
+
class IOTest < MiniTest::Test
|
|
9
|
+
def setup
|
|
10
|
+
EV.rerun
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def test_that_io_does_not_block
|
|
14
|
+
i, o = IO.pipe
|
|
15
|
+
count = 0
|
|
16
|
+
msg = nil
|
|
17
|
+
[
|
|
18
|
+
spawn {
|
|
19
|
+
o.write("hello")
|
|
20
|
+
o.close
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
spawn {
|
|
24
|
+
while count < 5
|
|
25
|
+
sleep 0.01
|
|
26
|
+
count += 1
|
|
27
|
+
end
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
spawn {
|
|
31
|
+
msg = i.read
|
|
32
|
+
}
|
|
33
|
+
].each(&:await)
|
|
34
|
+
assert_equal(5, count)
|
|
35
|
+
assert_equal("hello", msg)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: polyphony
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: '0.
|
|
4
|
+
version: '0.14'
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sharon Rosner
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2019-
|
|
11
|
+
date: 2019-05-18 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: modulation
|
|
@@ -16,14 +16,14 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - '='
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '0.
|
|
19
|
+
version: '0.23'
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - '='
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '0.
|
|
26
|
+
version: '0.23'
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: http_parser.rb
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -102,15 +102,116 @@ extensions:
|
|
|
102
102
|
extra_rdoc_files:
|
|
103
103
|
- README.md
|
|
104
104
|
files:
|
|
105
|
+
- ".gitbook.yaml"
|
|
106
|
+
- ".gitignore"
|
|
107
|
+
- ".rubocop.yml"
|
|
108
|
+
- ".vscode/launch.json"
|
|
105
109
|
- CHANGELOG.md
|
|
110
|
+
- Gemfile
|
|
111
|
+
- Gemfile.lock
|
|
112
|
+
- LICENSE
|
|
106
113
|
- README.md
|
|
114
|
+
- Rakefile
|
|
115
|
+
- TODO.md
|
|
116
|
+
- docs/getting-started/getting-started.md
|
|
117
|
+
- docs/getting-started/tutorial.md
|
|
118
|
+
- docs/summary.md
|
|
119
|
+
- examples/core/cancel.rb
|
|
120
|
+
- examples/core/channel_echo.rb
|
|
121
|
+
- examples/core/enumerator.rb
|
|
122
|
+
- examples/core/fork.rb
|
|
123
|
+
- examples/core/genserver.rb
|
|
124
|
+
- examples/core/lock.rb
|
|
125
|
+
- examples/core/move_on.rb
|
|
126
|
+
- examples/core/move_on_twice.rb
|
|
127
|
+
- examples/core/move_on_with_ensure.rb
|
|
128
|
+
- examples/core/multiple_async.rb
|
|
129
|
+
- examples/core/nested_async.rb
|
|
130
|
+
- examples/core/nested_cancel.rb
|
|
131
|
+
- examples/core/nested_multiple_async.rb
|
|
132
|
+
- examples/core/next_tick.rb
|
|
133
|
+
- examples/core/pulse.rb
|
|
134
|
+
- examples/core/resource.rb
|
|
135
|
+
- examples/core/resource_cancel.rb
|
|
136
|
+
- examples/core/resource_delegate.rb
|
|
137
|
+
- examples/core/sleep.rb
|
|
138
|
+
- examples/core/sleep2.rb
|
|
139
|
+
- examples/core/spawn.rb
|
|
140
|
+
- examples/core/spawn_cancel.rb
|
|
141
|
+
- examples/core/spawn_error.rb
|
|
142
|
+
- examples/core/supervisor.rb
|
|
143
|
+
- examples/core/supervisor_with_cancel_scope.rb
|
|
144
|
+
- examples/core/supervisor_with_error.rb
|
|
145
|
+
- examples/core/supervisor_with_manual_move_on.rb
|
|
146
|
+
- examples/core/thread.rb
|
|
147
|
+
- examples/core/thread_cancel.rb
|
|
148
|
+
- examples/core/thread_pool.rb
|
|
149
|
+
- examples/core/throttle.rb
|
|
150
|
+
- examples/fs/read.rb
|
|
151
|
+
- examples/interfaces/pg_client.rb
|
|
152
|
+
- examples/interfaces/pg_pool.rb
|
|
153
|
+
- examples/interfaces/pg_query.rb
|
|
154
|
+
- examples/interfaces/redis_channels.rb
|
|
155
|
+
- examples/interfaces/redis_client.rb
|
|
156
|
+
- examples/interfaces/redis_pubsub.rb
|
|
157
|
+
- examples/interfaces/redis_pubsub_perf.rb
|
|
158
|
+
- examples/io/config.ru
|
|
159
|
+
- examples/io/echo_client.rb
|
|
160
|
+
- examples/io/echo_server.rb
|
|
161
|
+
- examples/io/echo_server_with_timeout.rb
|
|
162
|
+
- examples/io/echo_stdin.rb
|
|
163
|
+
- examples/io/happy_eyeballs.rb
|
|
164
|
+
- examples/io/http_client.rb
|
|
165
|
+
- examples/io/http_server.js
|
|
166
|
+
- examples/io/http_server.rb
|
|
167
|
+
- examples/io/http_server_forked.rb
|
|
168
|
+
- examples/io/http_server_throttled.rb
|
|
169
|
+
- examples/io/http_ws_server.rb
|
|
170
|
+
- examples/io/https_client.rb
|
|
171
|
+
- examples/io/https_server.rb
|
|
172
|
+
- examples/io/https_wss_server.rb
|
|
173
|
+
- examples/io/rack_server.rb
|
|
174
|
+
- examples/io/rack_server_https.rb
|
|
175
|
+
- examples/io/rack_server_https_forked.rb
|
|
176
|
+
- examples/io/websocket_server.rb
|
|
177
|
+
- examples/io/ws_page.html
|
|
178
|
+
- examples/io/wss_page.html
|
|
179
|
+
- examples/performance/perf_multi_snooze.rb
|
|
180
|
+
- examples/performance/perf_snooze.rb
|
|
181
|
+
- examples/performance/thread-vs-fiber/polyphony_server.rb
|
|
182
|
+
- examples/performance/thread-vs-fiber/threaded_server.rb
|
|
183
|
+
- examples/streams/lines.rb
|
|
184
|
+
- examples/streams/stdio.rb
|
|
185
|
+
- ext/ev/async.c
|
|
186
|
+
- ext/ev/child.c
|
|
187
|
+
- ext/ev/ev.h
|
|
188
|
+
- ext/ev/ev_ext.c
|
|
189
|
+
- ext/ev/ev_module.c
|
|
107
190
|
- ext/ev/extconf.rb
|
|
191
|
+
- ext/ev/io.c
|
|
192
|
+
- ext/ev/libev.h
|
|
193
|
+
- ext/ev/signal.c
|
|
194
|
+
- ext/ev/timer.c
|
|
195
|
+
- ext/libev/Changes
|
|
196
|
+
- ext/libev/LICENSE
|
|
197
|
+
- ext/libev/README
|
|
198
|
+
- ext/libev/README.embed
|
|
199
|
+
- ext/libev/ev.c
|
|
200
|
+
- ext/libev/ev.h
|
|
201
|
+
- ext/libev/ev_epoll.c
|
|
202
|
+
- ext/libev/ev_kqueue.c
|
|
203
|
+
- ext/libev/ev_poll.c
|
|
204
|
+
- ext/libev/ev_port.c
|
|
205
|
+
- ext/libev/ev_select.c
|
|
206
|
+
- ext/libev/ev_vars.h
|
|
207
|
+
- ext/libev/ev_win32.c
|
|
208
|
+
- ext/libev/ev_wrap.h
|
|
209
|
+
- ext/libev/test_libev_win32.c
|
|
108
210
|
- lib/polyphony.rb
|
|
109
211
|
- lib/polyphony/core.rb
|
|
110
|
-
- lib/polyphony/core/async.rb
|
|
111
212
|
- lib/polyphony/core/cancel_scope.rb
|
|
112
213
|
- lib/polyphony/core/channel.rb
|
|
113
|
-
- lib/polyphony/core/
|
|
214
|
+
- lib/polyphony/core/coprocess.rb
|
|
114
215
|
- lib/polyphony/core/exceptions.rb
|
|
115
216
|
- lib/polyphony/core/fiber_pool.rb
|
|
116
217
|
- lib/polyphony/core/supervisor.rb
|
|
@@ -134,11 +235,16 @@ files:
|
|
|
134
235
|
- lib/polyphony/http/server.rb
|
|
135
236
|
- lib/polyphony/line_reader.rb
|
|
136
237
|
- lib/polyphony/net.rb
|
|
137
|
-
- lib/polyphony/net_old.rb
|
|
138
238
|
- lib/polyphony/resource_pool.rb
|
|
139
239
|
- lib/polyphony/server_task.rb
|
|
140
240
|
- lib/polyphony/testing.rb
|
|
141
241
|
- lib/polyphony/version.rb
|
|
242
|
+
- lib/polyphony/websocket.rb
|
|
243
|
+
- polyphony.gemspec
|
|
244
|
+
- test/test_coprocess.rb
|
|
245
|
+
- test/test_core.rb
|
|
246
|
+
- test/test_ev.rb
|
|
247
|
+
- test/test_io.rb
|
|
142
248
|
homepage: http://github.com/digital-fabric/polyphony
|
|
143
249
|
licenses:
|
|
144
250
|
- MIT
|
data/lib/polyphony/core/async.rb
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
export :async_decorate, :call_proc_with_optional_block
|
|
4
|
-
|
|
5
|
-
Coroutine = import('./coroutine')
|
|
6
|
-
FiberPool = import('./fiber_pool')
|
|
7
|
-
|
|
8
|
-
# Converts a regular method into an async method, i.e. a method that returns a
|
|
9
|
-
# proc that eventually executes the original code.
|
|
10
|
-
# @param receiver [Object] object receiving the method call
|
|
11
|
-
# @param sym [Symbol] method name
|
|
12
|
-
# @return [void]
|
|
13
|
-
def async_decorate(receiver, sym)
|
|
14
|
-
sync_sym = :"sync_#{sym}"
|
|
15
|
-
receiver.alias_method(sync_sym, sym)
|
|
16
|
-
receiver.define_method(sym) do |*args, &block|
|
|
17
|
-
Coroutine.new { send(sync_sym, *args, &block) }
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
# Calls a proc with a block if both are given. Otherwise, call the first
|
|
22
|
-
# non-nil proc. This allows syntax such as:
|
|
23
|
-
#
|
|
24
|
-
# # in fact, the call to #nexus returns a proc which takes a block
|
|
25
|
-
# await nexus { ... }
|
|
26
|
-
#
|
|
27
|
-
# @param proc [Proc] proc A
|
|
28
|
-
# @param block [Proc] proc B
|
|
29
|
-
# @return [any] return value of proc invocation
|
|
30
|
-
def call_proc_with_optional_block(proc, block)
|
|
31
|
-
if proc && block
|
|
32
|
-
proc.call(&block)
|
|
33
|
-
else
|
|
34
|
-
(proc || block).call
|
|
35
|
-
end
|
|
36
|
-
end
|
data/lib/polyphony/net_old.rb
DELETED
|
@@ -1,299 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
export :Server, :Socket
|
|
4
|
-
|
|
5
|
-
require 'socket'
|
|
6
|
-
require 'openssl'
|
|
7
|
-
|
|
8
|
-
Core = import('./core')
|
|
9
|
-
IO = import('./io')
|
|
10
|
-
|
|
11
|
-
# Implements a TCP server
|
|
12
|
-
class Server
|
|
13
|
-
# Initializes server
|
|
14
|
-
def initialize(opts = {})
|
|
15
|
-
@opts = opts
|
|
16
|
-
@callbacks = {}
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
# Listens on host and port given in opts
|
|
20
|
-
# @param opts [Hash] options
|
|
21
|
-
# @return [void]
|
|
22
|
-
def listen(opts)
|
|
23
|
-
@secure_context = opts[:secure_context]
|
|
24
|
-
@server = opts[:socket] || create_server_socket(opts)
|
|
25
|
-
@watcher = EV::IO.new(@server, :r, true) { accept_from_socket }
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
# Creates a server socket for listening to incoming connections
|
|
29
|
-
# @param opts [Hash] listening options
|
|
30
|
-
# @return [Net::Socket]
|
|
31
|
-
def create_server_socket(opts)
|
|
32
|
-
socket = TCPServer.new(opts[:host] || '127.0.0.1', opts[:port])
|
|
33
|
-
if @secure_context
|
|
34
|
-
socket = OpenSSL::SSL::SSLServer.new(socket, @secure_context)
|
|
35
|
-
socket.start_immediately = false
|
|
36
|
-
setup_alpn(opts[:alpn_protocols]) if opts[:alpn_protocols]
|
|
37
|
-
end
|
|
38
|
-
socket
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
# Sets up ALPN protocols negotiated during handshake
|
|
42
|
-
# @param server_protocols [Array<String>] protocols supported by server
|
|
43
|
-
# @return [void]
|
|
44
|
-
def setup_alpn(server_protocols)
|
|
45
|
-
@secure_context.alpn_protocols = server_protocols
|
|
46
|
-
@secure_context.alpn_select_cb = proc do |client_protocols|
|
|
47
|
-
# select first common protocol
|
|
48
|
-
(server_protocols & client_protocols).first
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
# Returns true if server is listening
|
|
53
|
-
# @return [Boolean]
|
|
54
|
-
def listening?
|
|
55
|
-
@server
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
# Closes the server socket
|
|
59
|
-
# @return [void]
|
|
60
|
-
def close
|
|
61
|
-
Core.unwatch(@server)
|
|
62
|
-
@server.close
|
|
63
|
-
@server = nil
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
# Accepts an incoming connection, triggers the :connection callback
|
|
67
|
-
# @return [void]
|
|
68
|
-
def accept_from_socket
|
|
69
|
-
socket = @server.accept
|
|
70
|
-
setup_connection(socket) if socket
|
|
71
|
-
rescue StandardError => e
|
|
72
|
-
puts "error in accept_from_socket: #{e.inspect}"
|
|
73
|
-
puts e.backtrace.join("\n")
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
# Sets up an accepted connection
|
|
77
|
-
# @param socket [TCPSocket] accepted socket
|
|
78
|
-
# @return [Net::Socket]
|
|
79
|
-
def setup_connection(socket)
|
|
80
|
-
opts = { connected: true, secure_context: @secure_context }
|
|
81
|
-
if @secure_context
|
|
82
|
-
connection = SecureSocket.new(socket, opts)
|
|
83
|
-
connection.on(:handshake, &@callbacks[:connection])
|
|
84
|
-
else
|
|
85
|
-
connection = Socket.new(socket, opts)
|
|
86
|
-
@callbacks[:connection]&.(connection)
|
|
87
|
-
end
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
# Registers a callback for given event
|
|
91
|
-
# @param event [Symbol] event kind
|
|
92
|
-
# @return [void]
|
|
93
|
-
def on(event, &block)
|
|
94
|
-
@callbacks[event] = block
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
# Returns a promise fulfilled upon the first incoming connection
|
|
98
|
-
# @return [Promise]
|
|
99
|
-
def connection(&block)
|
|
100
|
-
Core.promise(then: block, catch: block) do |p|
|
|
101
|
-
@callbacks[:connection] = p.to_proc
|
|
102
|
-
end
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
# Creates a generator promise, iterating asynchronously over incoming
|
|
106
|
-
# connections
|
|
107
|
-
# @return [void]
|
|
108
|
-
def each_connection(&block)
|
|
109
|
-
Core.promise(recurring: true) do |p|
|
|
110
|
-
@callbacks[:connection] = p.to_proc
|
|
111
|
-
end.each(&block)
|
|
112
|
-
end
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
# Client connection functionality
|
|
116
|
-
module ClientConnection
|
|
117
|
-
# Connects to the given host & port, returning a promise fulfilled once
|
|
118
|
-
# connected. Options can include:
|
|
119
|
-
# :timeout => connection timeout in seconds
|
|
120
|
-
# @param host [String] host domain name or IP address
|
|
121
|
-
# @param port [Integer] port number
|
|
122
|
-
# @param opts [Hash] options
|
|
123
|
-
# @return [Promise] connection promise
|
|
124
|
-
def connect(host, port, opts = {})
|
|
125
|
-
Core.promise do |p|
|
|
126
|
-
socket = ::Socket.new(::Socket::AF_INET, ::Socket::SOCK_STREAM)
|
|
127
|
-
p.timeout(opts[:timeout]) { connect_timeout(socket) } if opts[:timeout]
|
|
128
|
-
connect_async(socket, host, port, p)
|
|
129
|
-
end
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
# Connects asynchronously to a TCP Server
|
|
133
|
-
# @param socket [TCPSocket] socket to use for connection
|
|
134
|
-
# @param host [String] host domain name or ip address
|
|
135
|
-
# @param port [Integer] server port number
|
|
136
|
-
# @param promise [Promise] connection promise
|
|
137
|
-
# @return [void]
|
|
138
|
-
def connect_async(socket, host, port, promise)
|
|
139
|
-
addr = ::Socket.sockaddr_in(port, host)
|
|
140
|
-
result = socket.connect_nonblock addr, exception: false
|
|
141
|
-
handle_connect_result(result, socket, host, port, promise)
|
|
142
|
-
rescue StandardError => e
|
|
143
|
-
promise.reject(e)
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
# Handles result of asynchronous connection
|
|
147
|
-
# @param result [Integer, Symbol, nil] result of call to IO#connect_nonblock
|
|
148
|
-
# @param socket [TCPSocket] socket to use for connection
|
|
149
|
-
# @param host [String] host domain name or ip address
|
|
150
|
-
# @param port [Integer] server port number
|
|
151
|
-
# @param promise [Promise] connection promise
|
|
152
|
-
# @return [void]
|
|
153
|
-
def handle_connect_result(result, socket, host, port, promise)
|
|
154
|
-
case result
|
|
155
|
-
when :wait_writable
|
|
156
|
-
connect_async_pending(socket, host, port, promise)
|
|
157
|
-
when 0
|
|
158
|
-
@connection_pending = false
|
|
159
|
-
connect_success(socket, promise)
|
|
160
|
-
else
|
|
161
|
-
handle_invalid_connect_result(result, socket)
|
|
162
|
-
end
|
|
163
|
-
end
|
|
164
|
-
|
|
165
|
-
# Handles result of asynchronous connection
|
|
166
|
-
# @param result [Integer, Symbol, nil] result of call to IO#connect_nonblock
|
|
167
|
-
# @param socket [TCPSocket] socket to use for connection
|
|
168
|
-
def handle_invalid_connect_result(result, socket)
|
|
169
|
-
invalid_connect_result(result, socket)
|
|
170
|
-
@connection_pending = false
|
|
171
|
-
Core.unwatch(socket)
|
|
172
|
-
@monitor = nil
|
|
173
|
-
raise "Invalid result from connect_nonblock: #{result.inspect}"
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
# Sets connection pending state
|
|
177
|
-
# @param socket [TCPSocket] socket to use for connection
|
|
178
|
-
# @param host [String] host domain name or ip address
|
|
179
|
-
# @param port [Integer] server port number
|
|
180
|
-
# @param promise [Promise] connection promise
|
|
181
|
-
# @return [void]
|
|
182
|
-
def connect_async_pending(socket, host, port, promise)
|
|
183
|
-
create_watcher(socket, true, true)
|
|
184
|
-
@connection_pending = [socket, host, port, promise]
|
|
185
|
-
end
|
|
186
|
-
|
|
187
|
-
# Overrides IO#write_to_io to support async connection
|
|
188
|
-
# @return [void]
|
|
189
|
-
def write_to_io
|
|
190
|
-
@connection_pending ? connect_async(*@connection_pending) : super
|
|
191
|
-
end
|
|
192
|
-
|
|
193
|
-
# Sets socket and connected status on successful connection
|
|
194
|
-
# @param socket [TCPSocket] TCP socket
|
|
195
|
-
# @param promise [Promise] connection promise
|
|
196
|
-
# @return [void]
|
|
197
|
-
def connect_success(socket, promise)
|
|
198
|
-
@io = socket
|
|
199
|
-
@connected = true
|
|
200
|
-
promise.resolve(socket)
|
|
201
|
-
end
|
|
202
|
-
|
|
203
|
-
# Called upon connection timeout, cleans up
|
|
204
|
-
# @param socket [TCPSocket] TCP socket
|
|
205
|
-
# @return [void]
|
|
206
|
-
def connect_timeout(socket)
|
|
207
|
-
@connection_pending = false
|
|
208
|
-
Core.unwatch(socket)
|
|
209
|
-
@monitor = nil
|
|
210
|
-
socket.close
|
|
211
|
-
end
|
|
212
|
-
end
|
|
213
|
-
|
|
214
|
-
# ALPN protocol
|
|
215
|
-
module ALPN
|
|
216
|
-
# returns the ALPN protocol used for the given socket
|
|
217
|
-
# @return [String, nil]
|
|
218
|
-
def alpn_protocol
|
|
219
|
-
secure? && raw_io.alpn_protocol
|
|
220
|
-
end
|
|
221
|
-
end
|
|
222
|
-
|
|
223
|
-
# Encapsulates a TCP socket
|
|
224
|
-
class Socket < IO
|
|
225
|
-
include ClientConnection
|
|
226
|
-
include ALPN
|
|
227
|
-
|
|
228
|
-
# Initializes socket
|
|
229
|
-
def initialize(socket = nil, opts = {})
|
|
230
|
-
super(socket, opts)
|
|
231
|
-
@connected = opts[:connected]
|
|
232
|
-
end
|
|
233
|
-
|
|
234
|
-
# Returns true if socket is connected
|
|
235
|
-
# @return [Boolean]
|
|
236
|
-
def connected?
|
|
237
|
-
@connected
|
|
238
|
-
end
|
|
239
|
-
|
|
240
|
-
# Returns false
|
|
241
|
-
# @return [false]
|
|
242
|
-
def secure?
|
|
243
|
-
false
|
|
244
|
-
end
|
|
245
|
-
|
|
246
|
-
# Sets socket option
|
|
247
|
-
# @return [void]
|
|
248
|
-
def setsockopt(*args)
|
|
249
|
-
@io.setsockopt(*args)
|
|
250
|
-
end
|
|
251
|
-
end
|
|
252
|
-
|
|
253
|
-
# Socket with TLS handshake functionality
|
|
254
|
-
class SecureSocket < Socket
|
|
255
|
-
# Initializes secure socket
|
|
256
|
-
def initialize(socket = nil, opts = {})
|
|
257
|
-
super
|
|
258
|
-
accept_secure_handshake
|
|
259
|
-
end
|
|
260
|
-
|
|
261
|
-
# Returns true
|
|
262
|
-
# @return [true]
|
|
263
|
-
def secure?
|
|
264
|
-
true
|
|
265
|
-
end
|
|
266
|
-
|
|
267
|
-
# accepts secure handshake asynchronously
|
|
268
|
-
# @return [void]
|
|
269
|
-
def accept_secure_handshake
|
|
270
|
-
@pending_secure_handshake = true
|
|
271
|
-
result = @io.accept_nonblock(exception: false)
|
|
272
|
-
handle_accept_secure_handshake_result(result)
|
|
273
|
-
rescue StandardError => e
|
|
274
|
-
close_on_error(e)
|
|
275
|
-
end
|
|
276
|
-
|
|
277
|
-
# Handles result of secure handshake
|
|
278
|
-
# @param result [Integer, any] result of call to accept_nonblock
|
|
279
|
-
# @return [void]
|
|
280
|
-
def handle_accept_secure_handshake_result(result)
|
|
281
|
-
case result
|
|
282
|
-
when :wait_readable
|
|
283
|
-
@watcher_r.start
|
|
284
|
-
when :wait_writable
|
|
285
|
-
@watcher_w.start
|
|
286
|
-
else
|
|
287
|
-
@pending_secure_handshake = false
|
|
288
|
-
@watcher_r.start
|
|
289
|
-
@watcher_w.stop
|
|
290
|
-
@callbacks[:handshake]&.(self)
|
|
291
|
-
end
|
|
292
|
-
end
|
|
293
|
-
|
|
294
|
-
# Overrides read_from_io to accept secure handshake
|
|
295
|
-
# @return [void]
|
|
296
|
-
def read_from_io
|
|
297
|
-
@pending_secure_handshake ? accept_secure_handshake : super
|
|
298
|
-
end
|
|
299
|
-
end
|