polyphony 0.17 → 0.19
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/CHANGELOG.md +15 -0
- data/Gemfile.lock +11 -3
- data/README.md +18 -18
- data/TODO.md +5 -21
- data/examples/core/channel_echo.rb +3 -3
- data/examples/core/enumerator.rb +1 -1
- data/examples/core/fork.rb +1 -1
- data/examples/core/genserver.rb +1 -1
- data/examples/core/lock.rb +3 -3
- data/examples/core/multiple_spawn.rb +2 -2
- data/examples/core/nested_async.rb +1 -1
- data/examples/core/nested_multiple_spawn.rb +3 -3
- data/examples/core/resource_cancel.rb +1 -1
- data/examples/core/sleep_spawn.rb +2 -2
- data/examples/core/spawn.rb +1 -1
- data/examples/core/spawn_cancel.rb +1 -1
- data/examples/core/spawn_error.rb +4 -4
- data/examples/core/supervisor.rb +1 -1
- data/examples/core/supervisor_with_error.rb +1 -1
- data/examples/core/supervisor_with_manual_move_on.rb +1 -1
- data/examples/core/thread.rb +2 -2
- data/examples/core/thread_cancel.rb +2 -2
- data/examples/core/thread_pool.rb +1 -1
- data/examples/core/throttle.rb +3 -3
- data/examples/core/timeout.rb +10 -0
- data/examples/fs/read.rb +1 -1
- data/examples/http/http_client.rb +1 -1
- data/examples/http/http_get.rb +7 -0
- data/examples/http/http_parse_experiment.rb +118 -0
- data/examples/http/http_proxy.rb +81 -0
- data/examples/http/http_server.rb +15 -4
- data/examples/http/http_server_forked.rb +2 -2
- data/examples/http/http_server_throttled.rb +1 -1
- data/examples/http/http_ws_server.rb +2 -2
- data/examples/http/https_server.rb +5 -1
- data/examples/http/https_wss_server.rb +1 -1
- data/examples/http/rack_server_https_forked.rb +1 -1
- data/examples/interfaces/pg_client.rb +1 -1
- data/examples/interfaces/pg_pool.rb +1 -1
- data/examples/interfaces/redis_channels.rb +5 -5
- data/examples/interfaces/redis_pubsub.rb +2 -2
- data/examples/interfaces/redis_pubsub_perf.rb +3 -3
- data/examples/io/echo_client.rb +2 -2
- data/examples/io/echo_pipe.rb +17 -0
- data/examples/io/echo_server.rb +1 -1
- data/examples/io/echo_server_with_timeout.rb +1 -1
- data/examples/io/httparty.rb +10 -0
- data/examples/io/httparty_multi.rb +29 -0
- data/examples/io/httparty_threaded.rb +25 -0
- data/examples/io/irb.rb +15 -0
- data/examples/io/net-http.rb +15 -0
- data/examples/io/system.rb +1 -1
- data/examples/io/tcpsocket.rb +18 -0
- data/examples/performance/perf_multi_snooze.rb +2 -2
- data/examples/performance/perf_snooze.rb +17 -20
- data/examples/performance/thread-vs-fiber/polyphony_server.rb +1 -1
- data/ext/ev/ev.h +9 -1
- data/ext/ev/ev_ext.c +4 -1
- data/ext/ev/ev_module.c +36 -22
- data/ext/ev/extconf.rb +1 -1
- data/ext/ev/io.c +23 -23
- data/ext/ev/signal.c +1 -1
- data/ext/ev/socket.c +161 -0
- data/lib/polyphony/core/coprocess.rb +1 -1
- data/lib/polyphony/core/fiber_pool.rb +2 -2
- data/lib/polyphony/core/supervisor.rb +2 -18
- data/lib/polyphony/extensions/io.rb +19 -6
- data/lib/polyphony/extensions/kernel.rb +17 -5
- data/lib/polyphony/extensions/socket.rb +40 -1
- data/lib/polyphony/http/agent.rb +56 -25
- data/lib/polyphony/http/http1_adapter.rb +254 -0
- data/lib/polyphony/http/http2_adapter.rb +157 -0
- data/lib/polyphony/http/{http2_request.rb → request.rb} +25 -22
- data/lib/polyphony/http/server.rb +19 -11
- data/lib/polyphony/net.rb +10 -6
- data/lib/polyphony/version.rb +1 -1
- data/polyphony.gemspec +6 -5
- data/test/test_coprocess.rb +9 -9
- data/test/test_core.rb +14 -14
- data/test/test_io.rb +4 -4
- data/test/test_kernel.rb +1 -1
- metadata +48 -23
- data/lib/polyphony/http/http1.rb +0 -124
- data/lib/polyphony/http/http1_request.rb +0 -83
- data/lib/polyphony/http/http2.rb +0 -65
data/test/test_io.rb
CHANGED
@@ -15,19 +15,19 @@ class IOTest < MiniTest::Test
|
|
15
15
|
count = 0
|
16
16
|
msg = nil
|
17
17
|
[
|
18
|
-
|
18
|
+
spin {
|
19
19
|
@o.write("hello")
|
20
20
|
@o.close
|
21
21
|
},
|
22
22
|
|
23
|
-
|
23
|
+
spin {
|
24
24
|
while count < 5
|
25
25
|
sleep 0.01
|
26
26
|
count += 1
|
27
27
|
end
|
28
28
|
},
|
29
29
|
|
30
|
-
|
30
|
+
spin {
|
31
31
|
msg = @i.read
|
32
32
|
}
|
33
33
|
].each(&:await)
|
@@ -118,7 +118,7 @@ class IOClassMethodsTest < MiniTest::Test
|
|
118
118
|
|
119
119
|
def test_popen
|
120
120
|
counter = 0
|
121
|
-
timer =
|
121
|
+
timer = spin {
|
122
122
|
throttled_loop(200) { counter += 1 }
|
123
123
|
}
|
124
124
|
|
data/test/test_kernel.rb
CHANGED
metadata
CHANGED
@@ -1,29 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: polyphony
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.19'
|
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-06-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: modulation
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '0.
|
19
|
+
version: '0.25'
|
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.25'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: http_parser.rb
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -53,33 +53,33 @@ dependencies:
|
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: 0.10.0
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: hiredis
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - '='
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
61
|
+
version: 0.6.3
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - '='
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
68
|
+
version: 0.6.3
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
70
|
+
name: httparty
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - '='
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version:
|
75
|
+
version: 0.17.0
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - '='
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version:
|
82
|
+
version: 0.17.0
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: localhost
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -95,19 +95,19 @@ dependencies:
|
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: 1.1.4
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
98
|
+
name: minitest
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
101
|
- - '='
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version:
|
103
|
+
version: 5.11.3
|
104
104
|
type: :development
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
108
|
- - '='
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version:
|
110
|
+
version: 5.11.3
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
112
|
name: pg
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -122,6 +122,20 @@ dependencies:
|
|
122
122
|
- - '='
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: 1.1.3
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: rake-compiler
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - '='
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: 1.0.5
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - '='
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: 1.0.5
|
125
139
|
- !ruby/object:Gem::Dependency
|
126
140
|
name: redis
|
127
141
|
requirement: !ruby/object:Gem::Requirement
|
@@ -137,19 +151,19 @@ dependencies:
|
|
137
151
|
- !ruby/object:Gem::Version
|
138
152
|
version: 4.1.0
|
139
153
|
- !ruby/object:Gem::Dependency
|
140
|
-
name:
|
154
|
+
name: websocket
|
141
155
|
requirement: !ruby/object:Gem::Requirement
|
142
156
|
requirements:
|
143
157
|
- - '='
|
144
158
|
- !ruby/object:Gem::Version
|
145
|
-
version:
|
159
|
+
version: 1.2.8
|
146
160
|
type: :development
|
147
161
|
prerelease: false
|
148
162
|
version_requirements: !ruby/object:Gem::Requirement
|
149
163
|
requirements:
|
150
164
|
- - '='
|
151
165
|
- !ruby/object:Gem::Version
|
152
|
-
version:
|
166
|
+
version: 1.2.8
|
153
167
|
description:
|
154
168
|
email: ciconia@gmail.com
|
155
169
|
executables: []
|
@@ -203,10 +217,14 @@ files:
|
|
203
217
|
- examples/core/thread_cancel.rb
|
204
218
|
- examples/core/thread_pool.rb
|
205
219
|
- examples/core/throttle.rb
|
220
|
+
- examples/core/timeout.rb
|
206
221
|
- examples/fs/read.rb
|
207
222
|
- examples/http/config.ru
|
208
223
|
- examples/http/happy_eyeballs.rb
|
209
224
|
- examples/http/http_client.rb
|
225
|
+
- examples/http/http_get.rb
|
226
|
+
- examples/http/http_parse_experiment.rb
|
227
|
+
- examples/http/http_proxy.rb
|
210
228
|
- examples/http/http_server.js
|
211
229
|
- examples/http/http_server.rb
|
212
230
|
- examples/http/http_server_forked.rb
|
@@ -231,11 +249,18 @@ files:
|
|
231
249
|
- examples/interfaces/redis_pubsub_perf.rb
|
232
250
|
- examples/io/cat.rb
|
233
251
|
- examples/io/echo_client.rb
|
252
|
+
- examples/io/echo_pipe.rb
|
234
253
|
- examples/io/echo_server.rb
|
235
254
|
- examples/io/echo_server_with_timeout.rb
|
236
255
|
- examples/io/echo_stdin.rb
|
256
|
+
- examples/io/httparty.rb
|
257
|
+
- examples/io/httparty_multi.rb
|
258
|
+
- examples/io/httparty_threaded.rb
|
237
259
|
- examples/io/io_read.rb
|
260
|
+
- examples/io/irb.rb
|
261
|
+
- examples/io/net-http.rb
|
238
262
|
- examples/io/system.rb
|
263
|
+
- examples/io/tcpsocket.rb
|
239
264
|
- examples/performance/perf_multi_snooze.rb
|
240
265
|
- examples/performance/perf_snooze.rb
|
241
266
|
- examples/performance/thread-vs-fiber/polyphony_server.rb
|
@@ -249,6 +274,7 @@ files:
|
|
249
274
|
- ext/ev/io.c
|
250
275
|
- ext/ev/libev.h
|
251
276
|
- ext/ev/signal.c
|
277
|
+
- ext/ev/socket.c
|
252
278
|
- ext/ev/timer.c
|
253
279
|
- ext/libev/Changes
|
254
280
|
- ext/libev/LICENSE
|
@@ -284,11 +310,10 @@ files:
|
|
284
310
|
- lib/polyphony/fs.rb
|
285
311
|
- lib/polyphony/http.rb
|
286
312
|
- lib/polyphony/http/agent.rb
|
287
|
-
- lib/polyphony/http/
|
288
|
-
- lib/polyphony/http/
|
289
|
-
- lib/polyphony/http/http2.rb
|
290
|
-
- lib/polyphony/http/http2_request.rb
|
313
|
+
- lib/polyphony/http/http1_adapter.rb
|
314
|
+
- lib/polyphony/http/http2_adapter.rb
|
291
315
|
- lib/polyphony/http/rack.rb
|
316
|
+
- lib/polyphony/http/request.rb
|
292
317
|
- lib/polyphony/http/server.rb
|
293
318
|
- lib/polyphony/line_reader.rb
|
294
319
|
- lib/polyphony/net.rb
|
@@ -326,7 +351,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
326
351
|
- !ruby/object:Gem::Version
|
327
352
|
version: '0'
|
328
353
|
requirements: []
|
329
|
-
rubygems_version: 3.0.
|
354
|
+
rubygems_version: 3.0.3
|
330
355
|
signing_key:
|
331
356
|
specification_version: 4
|
332
357
|
summary: 'Polyphony: Fiber-based Concurrency for Ruby'
|
data/lib/polyphony/http/http1.rb
DELETED
@@ -1,124 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
export :call
|
4
|
-
|
5
|
-
require 'http/parser'
|
6
|
-
|
7
|
-
Request = import('./http1_request')
|
8
|
-
HTTP2 = import('./http2')
|
9
|
-
|
10
|
-
class Http::Parser
|
11
|
-
def async!
|
12
|
-
self.on_message_complete = proc { @request_complete = true }
|
13
|
-
self
|
14
|
-
end
|
15
|
-
|
16
|
-
def parse(data)
|
17
|
-
self << data
|
18
|
-
return nil unless @request_complete
|
19
|
-
|
20
|
-
@request_complete = nil
|
21
|
-
self
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
# Sets up parsing and handling of request/response cycle
|
26
|
-
# @param socket [Net::Socket] socket
|
27
|
-
# @param handler [Proc] request handler
|
28
|
-
# @return [void]
|
29
|
-
def call(socket, opts, &handler)
|
30
|
-
ctx = connection_context(socket, opts, handler)
|
31
|
-
ctx[:parser].on_body = proc { |chunk| handle_body_chunk(ctx, chunk) }
|
32
|
-
|
33
|
-
loop do
|
34
|
-
data = socket.readpartial(8192)
|
35
|
-
break unless data
|
36
|
-
if ctx[:parser].parse(data)
|
37
|
-
break unless handle_request(ctx)
|
38
|
-
snooze
|
39
|
-
end
|
40
|
-
end
|
41
|
-
rescue IOError, SystemCallError => e
|
42
|
-
# do nothing
|
43
|
-
ensure
|
44
|
-
socket.close
|
45
|
-
end
|
46
|
-
|
47
|
-
# Returns a context hash for the given socket. This hash contains references
|
48
|
-
# related to the connection and its current state
|
49
|
-
# @param socket [Net::Socket] socket
|
50
|
-
# @param handler [Proc] request handler
|
51
|
-
# @return [Hash]
|
52
|
-
def connection_context(socket, opts, handler)
|
53
|
-
{
|
54
|
-
can_upgrade: true,
|
55
|
-
upgrade: opts[:upgrade],
|
56
|
-
count: 0,
|
57
|
-
socket: socket,
|
58
|
-
handler: handler,
|
59
|
-
parser: Http::Parser.new.async!,
|
60
|
-
body: nil
|
61
|
-
}
|
62
|
-
end
|
63
|
-
|
64
|
-
# Adds given chunk to request body
|
65
|
-
# @param ctx [Hash] connection context
|
66
|
-
# @return [void]
|
67
|
-
def handle_body_chunk(context, chunk)
|
68
|
-
|
69
|
-
context[:body] ||= +''
|
70
|
-
context[:body] << chunk
|
71
|
-
end
|
72
|
-
|
73
|
-
# Handles request, upgrading the connection if possible
|
74
|
-
# @param ctx [Hash] connection context
|
75
|
-
# @return [boolean] true if HTTP 1 loop should continue handling socket
|
76
|
-
def handle_request(ctx)
|
77
|
-
return nil if ctx[:can_upgrade] && upgrade_connection(ctx)
|
78
|
-
|
79
|
-
# allow upgrading the connection only on first request
|
80
|
-
ctx[:can_upgrade] = false
|
81
|
-
request = Request.new(ctx[:socket], ctx[:parser], ctx[:body])
|
82
|
-
ctx[:handler].(request)
|
83
|
-
|
84
|
-
if ctx[:parser].keep_alive?
|
85
|
-
ctx[:body] = nil
|
86
|
-
true
|
87
|
-
else
|
88
|
-
nil
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
# Upgrades an HTTP 1 connection to HTTP/2 or other protocol on client request
|
93
|
-
# @param ctx [Hash] connection context
|
94
|
-
# @return [Boolean] true if connection was upgraded
|
95
|
-
def upgrade_connection(ctx)
|
96
|
-
upgrade_protocol = ctx[:parser].headers['Upgrade']
|
97
|
-
return false unless upgrade_protocol
|
98
|
-
|
99
|
-
if ctx[:upgrade] && ctx[:upgrade][upgrade_protocol.to_sym]
|
100
|
-
ctx[:upgrade][upgrade_protocol.to_sym].(ctx[:socket], ctx[:parser].headers)
|
101
|
-
return true
|
102
|
-
end
|
103
|
-
|
104
|
-
return false unless upgrade_protocol == 'h2c'
|
105
|
-
|
106
|
-
# upgrade to HTTP/2
|
107
|
-
request = http2_upgraded_request(ctx)
|
108
|
-
body = ctx[:body] || ''
|
109
|
-
HTTP2.upgrade(ctx[:socket], ctx[:handler], request, body)
|
110
|
-
true
|
111
|
-
end
|
112
|
-
|
113
|
-
# Returns a request hash for handling by upgraded HTTP 2 connection
|
114
|
-
# @param ctx [Hash] connection context
|
115
|
-
# @return [Hash]
|
116
|
-
def http2_upgraded_request(ctx)
|
117
|
-
headers = ctx[:parser].headers
|
118
|
-
headers.merge(
|
119
|
-
':scheme' => 'http',
|
120
|
-
':method' => ctx[:parser].http_method,
|
121
|
-
':authority' => headers['Host'],
|
122
|
-
':path' => ctx[:parser].request_url
|
123
|
-
)
|
124
|
-
end
|
@@ -1,83 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
export_default :Request
|
4
|
-
|
5
|
-
require 'uri'
|
6
|
-
|
7
|
-
class Request
|
8
|
-
def initialize(conn, parser, body)
|
9
|
-
@conn = conn
|
10
|
-
@parser = parser
|
11
|
-
@method = parser.http_method
|
12
|
-
@request_url = parser.request_url
|
13
|
-
@body = body
|
14
|
-
end
|
15
|
-
|
16
|
-
def http_version
|
17
|
-
1
|
18
|
-
end
|
19
|
-
|
20
|
-
def method
|
21
|
-
@method ||= @parser.http_method
|
22
|
-
end
|
23
|
-
|
24
|
-
def path
|
25
|
-
@uri ||= URI.parse(@parser.request_url || '')
|
26
|
-
@path ||= @uri.path
|
27
|
-
end
|
28
|
-
|
29
|
-
def query
|
30
|
-
@uri ||= URI.parse(@parser.request_url || '')
|
31
|
-
return @query if @query
|
32
|
-
|
33
|
-
if (q = @uri.query)
|
34
|
-
@query = q.split('&').each_with_object({}) do |kv, h|
|
35
|
-
k, v = kv.split('=')
|
36
|
-
h[k.to_sym] = URI.decode_www_form_component(v)
|
37
|
-
end
|
38
|
-
else
|
39
|
-
@query = {}
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
def headers
|
44
|
-
@headers ||= @parser.headers
|
45
|
-
end
|
46
|
-
|
47
|
-
EMPTY_HASH = {}
|
48
|
-
|
49
|
-
def respond(chunk, headers = EMPTY_HASH)
|
50
|
-
status = headers.delete(':status') || 200
|
51
|
-
data = format_head(headers)
|
52
|
-
if chunk
|
53
|
-
data << "#{chunk.bytesize.to_s(16)}\r\n#{chunk}\r\n0\r\n\r\n"
|
54
|
-
end
|
55
|
-
@conn << data
|
56
|
-
end
|
57
|
-
|
58
|
-
def format_head(headers)
|
59
|
-
status = headers[':status'] || 200
|
60
|
-
data = +"HTTP/1.1 #{status}\r\nTransfer-Encoding: chunked\r\n"
|
61
|
-
headers.each do |k, v|
|
62
|
-
next if k =~ /^:/
|
63
|
-
if v.is_a?(Array)
|
64
|
-
v.each { |o| data << "#{k}: #{o}\r\n" }
|
65
|
-
else
|
66
|
-
data << "#{k}: #{v}\r\n"
|
67
|
-
end
|
68
|
-
end
|
69
|
-
data << "\r\n"
|
70
|
-
end
|
71
|
-
|
72
|
-
def write_head(headers = EMPTY_HASH)
|
73
|
-
@conn << format_head(headers)
|
74
|
-
end
|
75
|
-
|
76
|
-
def write(chunk)
|
77
|
-
data = +"#{chunk.bytesize.to_s(16)}\r\n#{chunk}\r\n"
|
78
|
-
end
|
79
|
-
|
80
|
-
def finish
|
81
|
-
@conn << "0\r\n\r\n"
|
82
|
-
end
|
83
|
-
end
|