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/examples/core/thread.rb
CHANGED
@@ -18,8 +18,8 @@ end
|
|
18
18
|
|
19
19
|
def threaded
|
20
20
|
t0 = Time.now
|
21
|
-
data = Polyphony::Thread.
|
22
|
-
X.times { Polyphony::Thread.
|
21
|
+
data = Polyphony::Thread.spin { lengthy_op }.await
|
22
|
+
X.times { Polyphony::Thread.spin { lengthy_op }.await }
|
23
23
|
puts "read threaded #{data.bytesize} bytes (#{Time.now - t0}s)"
|
24
24
|
end
|
25
25
|
|
@@ -13,10 +13,10 @@ def lengthy_op
|
|
13
13
|
acc / count
|
14
14
|
end
|
15
15
|
|
16
|
-
|
16
|
+
spin do
|
17
17
|
t0 = Time.now
|
18
18
|
cancel_after(0.01) do
|
19
|
-
data = Polyphony::Thread.
|
19
|
+
data = Polyphony::Thread.spin { lengthy_op }.await
|
20
20
|
puts "read #{data.bytesize} bytes (#{Time.now - t0}s)"
|
21
21
|
end
|
22
22
|
rescue Exception => e
|
data/examples/core/throttle.rb
CHANGED
@@ -3,14 +3,14 @@
|
|
3
3
|
require 'bundler/setup'
|
4
4
|
require 'polyphony'
|
5
5
|
|
6
|
-
|
6
|
+
spin {
|
7
7
|
throttled_loop(3) { STDOUT << '.' }
|
8
8
|
}
|
9
9
|
|
10
|
-
|
10
|
+
spin {
|
11
11
|
throttled_loop(rate: 2) { STDOUT << '?' }
|
12
12
|
}
|
13
13
|
|
14
|
-
|
14
|
+
spin {
|
15
15
|
throttled_loop(interval: 1) { STDOUT << '*' }
|
16
16
|
}
|
data/examples/fs/read.rb
CHANGED
@@ -11,7 +11,7 @@ X = 10
|
|
11
11
|
puts "Making #{X} requests..."
|
12
12
|
t0 = Time.now
|
13
13
|
supervise do |s|
|
14
|
-
X.times { s.
|
14
|
+
X.times { s.spin { get_server_time } }
|
15
15
|
end
|
16
16
|
elapsed = Time.now - t0
|
17
17
|
puts "count: #{X} elapsed: #{elapsed} rate: #{X / elapsed} reqs/s"
|
@@ -0,0 +1,118 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'polyphony'
|
5
|
+
|
6
|
+
require 'http/parser'
|
7
|
+
require 'fiber'
|
8
|
+
|
9
|
+
i, o = IO.pipe
|
10
|
+
|
11
|
+
class ParseLoop
|
12
|
+
def initialize(conn)
|
13
|
+
@parser = HTTP::Parser.new(self)
|
14
|
+
@conn = conn
|
15
|
+
@parse_fiber = Fiber.new do
|
16
|
+
while (data = conn.readpartial(8192))
|
17
|
+
@parser << data
|
18
|
+
snooze
|
19
|
+
end
|
20
|
+
rescue => e
|
21
|
+
conn.close
|
22
|
+
e
|
23
|
+
ensure
|
24
|
+
@message_in_train = nil
|
25
|
+
end
|
26
|
+
@state = nil
|
27
|
+
end
|
28
|
+
|
29
|
+
def on_headers_complete(headers)
|
30
|
+
@calling_fiber.transfer(headers)
|
31
|
+
end
|
32
|
+
|
33
|
+
def on_body(chunk)
|
34
|
+
@calling_fiber.transfer(chunk) if @read_body
|
35
|
+
end
|
36
|
+
|
37
|
+
def on_message_begin
|
38
|
+
@message_in_train = true
|
39
|
+
end
|
40
|
+
|
41
|
+
def on_message_complete
|
42
|
+
@message_in_train = nil
|
43
|
+
@calling_fiber.transfer nil
|
44
|
+
end
|
45
|
+
|
46
|
+
def parse_headers
|
47
|
+
@calling_fiber = Fiber.current
|
48
|
+
@parse_fiber.safe_transfer
|
49
|
+
end
|
50
|
+
|
51
|
+
def parse_body_chunk
|
52
|
+
@calling_fiber = Fiber.current
|
53
|
+
@read_body = true
|
54
|
+
@parse_fiber.safe_transfer
|
55
|
+
end
|
56
|
+
|
57
|
+
def consume_request
|
58
|
+
return unless @message_in_train
|
59
|
+
|
60
|
+
@calling_fiber = Fiber.current
|
61
|
+
@read_body = false
|
62
|
+
@parse_fiber.safe_transfer while @message_in_train
|
63
|
+
end
|
64
|
+
|
65
|
+
def alive?
|
66
|
+
@parse_fiber.alive?
|
67
|
+
end
|
68
|
+
|
69
|
+
def busy?
|
70
|
+
@message_in_train
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def handle(parser)
|
75
|
+
headers = parser.parse_headers
|
76
|
+
return unless headers
|
77
|
+
puts "headers: #{headers.inspect}"
|
78
|
+
content_length = headers['Content-Length']
|
79
|
+
# if content_length && (content_length.to_i < 1000)
|
80
|
+
while chunk = parser.parse_body_chunk
|
81
|
+
puts "chunk: #{chunk.inspect}"
|
82
|
+
end
|
83
|
+
# else
|
84
|
+
# parser.consume_request
|
85
|
+
# end
|
86
|
+
puts "end of request"
|
87
|
+
rescue => e
|
88
|
+
puts "error: #{e.inspect}"
|
89
|
+
raise e
|
90
|
+
end
|
91
|
+
|
92
|
+
writer = spin {
|
93
|
+
o << "POST / HTTP/1.1\r\nHost: example.com\r\nContent-Length: 6\r\n\r\n"
|
94
|
+
o << "Hello!"
|
95
|
+
|
96
|
+
o << "POST / HTTP/1.1\r\nHost: example.com\r\n\r\n"
|
97
|
+
|
98
|
+
# o << "POST / HTTP/1.1\r\nHost: example.com\r\nContent-Length: 8192\r\n\r\n"
|
99
|
+
# o << ("Bye!" * 2048)
|
100
|
+
|
101
|
+
o << "POST / HTTP/1.1\r\nHost: example.com\r\nContent-Length: 4\r\n\r\n"
|
102
|
+
o << "Bye!"
|
103
|
+
|
104
|
+
(o << ("BLAH" * 100000)) rescue nil
|
105
|
+
o.close
|
106
|
+
}
|
107
|
+
|
108
|
+
begin
|
109
|
+
parse_loop = ParseLoop.new(i)
|
110
|
+
while parse_loop.alive?
|
111
|
+
puts "*" * 40
|
112
|
+
handle(parse_loop)
|
113
|
+
end
|
114
|
+
rescue => e
|
115
|
+
writer.stop
|
116
|
+
puts "#{e.class}: #{e.message}"
|
117
|
+
puts e.backtrace
|
118
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'polyphony/http'
|
5
|
+
require 'localhost/authority'
|
6
|
+
|
7
|
+
# p Polyphony::HTTP::Agent.get('https://ui.realiteq.net/', q: :time)
|
8
|
+
|
9
|
+
BASE_URL = 'http://realiteq.net'
|
10
|
+
|
11
|
+
CACHE = {}
|
12
|
+
|
13
|
+
def proxy(uri, opts)
|
14
|
+
now = Time.now
|
15
|
+
uri = BASE_URL + uri
|
16
|
+
entry = CACHE[uri]
|
17
|
+
if entry && entry[:expires] >= now
|
18
|
+
return entry[:response]
|
19
|
+
else
|
20
|
+
puts "proxy => #{uri} (#{opts.inspect})"
|
21
|
+
response = Polyphony::HTTP::Agent.get(uri, opts)
|
22
|
+
# CACHE[uri] = {
|
23
|
+
# expires: now + 60,
|
24
|
+
# response: response
|
25
|
+
# }
|
26
|
+
response
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
HEADERS_BLACK_LIST = %w{
|
31
|
+
Transfer-Encoding Date Server Connection Content-Length Cache-Control
|
32
|
+
:method :authority :scheme :path
|
33
|
+
}
|
34
|
+
|
35
|
+
def sanitize_headers(headers)
|
36
|
+
headers.reject { |k, v| HEADERS_BLACK_LIST.include?(k) }
|
37
|
+
end
|
38
|
+
|
39
|
+
def sanitize_html(html)
|
40
|
+
# html.gsub('http://martigny-le-comte.fr/', '/')
|
41
|
+
end
|
42
|
+
|
43
|
+
# authority = Localhost::Authority.fetch
|
44
|
+
|
45
|
+
rsa_cert = OpenSSL::X509::Certificate.new(IO.read('../reality/aws/config/ssl/full_chain.pem'))
|
46
|
+
rsa_pkey = OpenSSL::PKey.read(IO.read('../reality/aws/config/ssl/private_key.pem'))
|
47
|
+
ctx = OpenSSL::SSL::SSLContext.new
|
48
|
+
ctx.add_certificate(rsa_cert, rsa_pkey)
|
49
|
+
|
50
|
+
opts = {
|
51
|
+
reuse_addr: true,
|
52
|
+
dont_linger: true,
|
53
|
+
secure_context: ctx#authority.server_context
|
54
|
+
}
|
55
|
+
|
56
|
+
spin {
|
57
|
+
Polyphony::HTTP::Server.serve('0.0.0.0', 1234, opts) do |req|
|
58
|
+
puts "#{req.method} #{req.uri.to_s}"
|
59
|
+
puts "headers <: #{req.headers.inspect}"
|
60
|
+
# h = {
|
61
|
+
# uri: req.uri.to_s,
|
62
|
+
# protocol: req.protocol,
|
63
|
+
# headers: req.headers,
|
64
|
+
# body: req.body,
|
65
|
+
# }
|
66
|
+
response = proxy(req.uri.to_s, headers: sanitize_headers(req.headers))
|
67
|
+
headers = sanitize_headers(response[:headers])
|
68
|
+
body = response[:body]
|
69
|
+
# puts "body class: #{body.class}"
|
70
|
+
puts "headers >: #{response[:headers].inspect}"
|
71
|
+
# body = sanitize_html(body) if headers['Content-Type'] =~ /text\/html/
|
72
|
+
req.respond(body, headers)
|
73
|
+
rescue => e
|
74
|
+
puts "error"
|
75
|
+
p e
|
76
|
+
puts e.backtrace.join("\n")
|
77
|
+
end
|
78
|
+
}
|
79
|
+
|
80
|
+
puts "pid: #{Process.pid}"
|
81
|
+
puts "Listening on port 1234..."
|
@@ -3,12 +3,23 @@
|
|
3
3
|
require 'bundler/setup'
|
4
4
|
require 'polyphony/http'
|
5
5
|
|
6
|
-
opts = {
|
7
|
-
|
6
|
+
opts = {
|
7
|
+
reuse_addr: true,
|
8
|
+
dont_linger: true
|
9
|
+
}
|
10
|
+
|
11
|
+
spin do
|
8
12
|
Polyphony::HTTP::Server.serve('0.0.0.0', 1234, opts) do |req|
|
9
|
-
req.respond(
|
13
|
+
req.respond('Hello world!')
|
14
|
+
# req.send_headers
|
15
|
+
# req.send_body_chunk("Method: #{req.method}\n")
|
16
|
+
# req.send_body_chunk("Path: #{req.path}\n")
|
17
|
+
# req.send_body_chunk("Query: #{req.query.inspect}\n", done: true)
|
10
18
|
end
|
11
|
-
|
19
|
+
rescue Exception => e
|
20
|
+
puts "*" * 40
|
21
|
+
p e
|
22
|
+
end
|
12
23
|
|
13
24
|
puts "pid: #{Process.pid}"
|
14
25
|
puts "Listening on port 1234..."
|
@@ -16,8 +16,8 @@ child_pids = []
|
|
16
16
|
4.times do
|
17
17
|
child_pids << Polyphony.fork do
|
18
18
|
puts "forked pid: #{Process.pid}"
|
19
|
-
|
20
|
-
req.respond("Hello world
|
19
|
+
server.each do |req|
|
20
|
+
req.respond("Hello world! from pid: #{Process.pid}\n")
|
21
21
|
end
|
22
22
|
rescue Interrupt
|
23
23
|
end
|
@@ -7,7 +7,7 @@ require 'polyphony/http'
|
|
7
7
|
require 'polyphony/websocket'
|
8
8
|
|
9
9
|
def ws_handler(conn)
|
10
|
-
timer =
|
10
|
+
timer = spin {
|
11
11
|
throttled_loop(1) {
|
12
12
|
conn << Time.now.to_s
|
13
13
|
}
|
@@ -29,7 +29,7 @@ opts = {
|
|
29
29
|
|
30
30
|
HTML = IO.read(File.join(__dir__, 'ws_page.html'))
|
31
31
|
|
32
|
-
|
32
|
+
spin {
|
33
33
|
server = Polyphony::HTTP::Server.serve('0.0.0.0', 1234, opts) do |req|
|
34
34
|
req.respond(HTML, 'Content-Type' => 'text/html')
|
35
35
|
end
|
@@ -14,5 +14,9 @@ opts = {
|
|
14
14
|
puts "pid: #{Process.pid}"
|
15
15
|
puts "Listening on port 1234..."
|
16
16
|
Polyphony::HTTP::Server.serve('0.0.0.0', 1234, opts) do |req|
|
17
|
-
req.respond(
|
17
|
+
req.respond('Hello world!')
|
18
|
+
# req.send_headers
|
19
|
+
# req.send_body_chunk("Method: #{req.method}\n")
|
20
|
+
# req.send_body_chunk("Path: #{req.path}\n")
|
21
|
+
# req.send_body_chunk("Query: #{req.query.inspect}\n", done: true)
|
18
22
|
end
|
@@ -28,7 +28,7 @@ DBPOOL.preheat!
|
|
28
28
|
t0 = Time.now
|
29
29
|
count = 0
|
30
30
|
coprocs = CONCURRENCY.times.map {
|
31
|
-
|
31
|
+
spin { loop { DBPOOL.acquire { |db| get_records(db); count += 1 } } }
|
32
32
|
}
|
33
33
|
sleep 5
|
34
34
|
puts "count: #{count} query rate: #{count / (Time.now - t0)} queries/s"
|
@@ -16,7 +16,7 @@ class RedisChannel < Polyphony::Channel
|
|
16
16
|
|
17
17
|
def self.start_monitor
|
18
18
|
@channels = {}
|
19
|
-
@monitor =
|
19
|
+
@monitor = spin do
|
20
20
|
subscribe_connection.subscribe(CHANNEL_MASTER_TOPIC) do |on|
|
21
21
|
on.message do |topic, message|
|
22
22
|
message = Marshal.load(message)
|
@@ -47,7 +47,7 @@ class RedisChannel < Polyphony::Channel
|
|
47
47
|
|
48
48
|
def self.watch(channel)
|
49
49
|
@channels[channel.topic] = channel
|
50
|
-
|
50
|
+
spin do
|
51
51
|
publish_connection.publish(CHANNEL_MASTER_TOPIC, Marshal.dump({
|
52
52
|
kind: :subscribe,
|
53
53
|
topic: channel.topic
|
@@ -57,7 +57,7 @@ class RedisChannel < Polyphony::Channel
|
|
57
57
|
|
58
58
|
def self.unwatch(channel)
|
59
59
|
@channels.delete(channel.topic)
|
60
|
-
|
60
|
+
spin do
|
61
61
|
publish_connection.publish(CHANNEL_MASTER_TOPIC, Marshal.dump({
|
62
62
|
kind: :unsubscribe,
|
63
63
|
topic: channel.topic
|
@@ -99,14 +99,14 @@ end
|
|
99
99
|
RedisChannel.start_monitor
|
100
100
|
channel = RedisChannel.new('channel1')
|
101
101
|
|
102
|
-
|
102
|
+
spin do
|
103
103
|
loop do
|
104
104
|
message = channel.receive
|
105
105
|
puts "got message: #{message}"
|
106
106
|
end
|
107
107
|
end
|
108
108
|
|
109
|
-
|
109
|
+
spin do
|
110
110
|
move_on_after(3) do
|
111
111
|
throttled_loop(1) do
|
112
112
|
channel << Time.now
|