polyphony 0.17 → 0.19
Sign up to get free protection for your applications and to get access to all the features.
- 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
|