plum 0.2.8 → 0.2.9
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/bin/plum +1 -1
- data/lib/plum/rack.rb +2 -0
- data/lib/plum/rack/cli.rb +5 -0
- data/lib/plum/rack/config.rb +2 -1
- data/lib/plum/rack/dsl.rb +4 -0
- data/lib/plum/rack/legacy_session.rb +36 -0
- data/lib/plum/rack/listener.rb +57 -8
- data/lib/plum/rack/server.rb +18 -67
- data/lib/plum/rack/session.rb +25 -14
- data/lib/plum/rack/thread_pool.rb +35 -0
- data/lib/plum/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8e0e1f798c885a09557e220ac446f6c0aa048efe
|
4
|
+
data.tar.gz: dd31a046c57dd142b85940f4b970e90441d2f490
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7d19e7d4c6f8b868d6c12824d62848f1532fe34af9dd04bc7128396a51533bbfe6c9c68bb36456c63a90e5fc2a337994d76b89ce0aa32bccb03616edf878a91e
|
7
|
+
data.tar.gz: aae1851fa3169ffb041a7efd3563dceee256e7e2aed653e6a8ffe1d8b3163bb7a32977abe02bee7ef86a5d91e792d555d1dd19bac40b72820549fa11c07d154e
|
data/bin/plum
CHANGED
data/lib/plum/rack.rb
CHANGED
data/lib/plum/rack/cli.rb
CHANGED
@@ -45,6 +45,7 @@ module Plum
|
|
45
45
|
config[:debug] = @options[:debug] unless @options[:debug].nil?
|
46
46
|
config[:server_push] = @options[:server_push] unless @options[:server_push].nil?
|
47
47
|
config[:threaded] = @options[:threaded] unless @options[:threaded].nil?
|
48
|
+
config[:threadpool_size] = @options[:threadpool_size] unless @options[:threadpool_size].nil?
|
48
49
|
|
49
50
|
if @options[:fallback_legacy]
|
50
51
|
h, p = @options[:fallback_legacy].split(":")
|
@@ -124,6 +125,10 @@ module Plum
|
|
124
125
|
@options[:threaded] = true
|
125
126
|
end
|
126
127
|
|
128
|
+
o.on "--threadpool-size SIZE", "Set the size of thread pool" do |arg|
|
129
|
+
@options[:threadpool_size] = arg.to_i
|
130
|
+
end
|
131
|
+
|
127
132
|
o.on "--fallback-legacy HOST:PORT", "Fallbacks if the client doesn't support HTTP/2" do |arg|
|
128
133
|
@options[:fallback_legacy] = arg
|
129
134
|
end
|
data/lib/plum/rack/config.rb
CHANGED
data/lib/plum/rack/dsl.rb
CHANGED
@@ -0,0 +1,36 @@
|
|
1
|
+
# -*- frozen-string-literal: true -*-
|
2
|
+
using Plum::BinaryString
|
3
|
+
|
4
|
+
module Plum
|
5
|
+
module Rack
|
6
|
+
class LegacySession
|
7
|
+
def initialize(svc, e, sock)
|
8
|
+
@svc = svc
|
9
|
+
@e = e
|
10
|
+
@sock = sock
|
11
|
+
@config = svc.config
|
12
|
+
end
|
13
|
+
|
14
|
+
def run
|
15
|
+
if @config[:fallback_legacy_host]
|
16
|
+
@logger.info "legacy HTTP: fallbacking to: #{@config[:fallback_legacy_host]}:#{@config[:fallback_legacy_port]}"
|
17
|
+
upstream = TCPSocket.open(@config[:fallback_legacy_host], @config[:fallback_legacy_port])
|
18
|
+
upstream.write(@e.buf) if @e.buf
|
19
|
+
loop do
|
20
|
+
ret = IO.select([@sock, upstream])
|
21
|
+
ret[0].each { |s|
|
22
|
+
a = s.readpartial(65536)
|
23
|
+
if s == upstream
|
24
|
+
@sock.write(a)
|
25
|
+
else
|
26
|
+
upstream.write(a)
|
27
|
+
end
|
28
|
+
}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
ensure
|
32
|
+
upstream.close if upstream
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/plum/rack/listener.rb
CHANGED
@@ -10,6 +10,10 @@ module Plum
|
|
10
10
|
raise "not implemented"
|
11
11
|
end
|
12
12
|
|
13
|
+
def accept(svc)
|
14
|
+
raise "not implemented"
|
15
|
+
end
|
16
|
+
|
13
17
|
def method_missing(name, *args)
|
14
18
|
@server.__send__(name, *args)
|
15
19
|
end
|
@@ -24,8 +28,24 @@ module Plum
|
|
24
28
|
@server.to_io
|
25
29
|
end
|
26
30
|
|
27
|
-
def
|
28
|
-
|
31
|
+
def accept(svc)
|
32
|
+
sock = @server.accept
|
33
|
+
Thread.start {
|
34
|
+
begin
|
35
|
+
plum = ::Plum::HTTPServerConnection.new(sock.method(:write))
|
36
|
+
sess = Session.new(svc, sock, plum)
|
37
|
+
sess.run
|
38
|
+
rescue Errno::ECONNRESET, EOFError # closed
|
39
|
+
rescue ::Plum::LegacyHTTPError => e
|
40
|
+
@logger.info "legacy HTTP client: #{e}"
|
41
|
+
sess = LegacySession.new(svc, e, sock)
|
42
|
+
sess.run
|
43
|
+
rescue => e
|
44
|
+
svc.log_exception(e)
|
45
|
+
ensure
|
46
|
+
sock.close
|
47
|
+
end
|
48
|
+
}
|
29
49
|
end
|
30
50
|
end
|
31
51
|
|
@@ -57,7 +77,7 @@ module Plum
|
|
57
77
|
}
|
58
78
|
tcp_server = ::TCPServer.new(lc[:hostname], lc[:port])
|
59
79
|
@server = OpenSSL::SSL::SSLServer.new(tcp_server, ctx)
|
60
|
-
@server.start_immediately = false
|
80
|
+
@server.start_immediately = false # call socket#accept twice: [tcp, tls]
|
61
81
|
end
|
62
82
|
|
63
83
|
def parse_chained_cert(str)
|
@@ -68,9 +88,26 @@ module Plum
|
|
68
88
|
@server.to_io
|
69
89
|
end
|
70
90
|
|
71
|
-
def
|
72
|
-
|
73
|
-
|
91
|
+
def accept(svc)
|
92
|
+
sock = @server.accept
|
93
|
+
Thread.start {
|
94
|
+
begin
|
95
|
+
sock = sock.accept
|
96
|
+
raise ::Plum::LegacyHTTPError.new("client didn't offer h2 with ALPN", nil) unless sock.alpn_protocol == "h2"
|
97
|
+
plum = ::Plum::ServerConnection.new(sock.method(:write))
|
98
|
+
sess = Session.new(svc, sock, plum)
|
99
|
+
sess.run
|
100
|
+
rescue Errno::ECONNRESET, EOFError # closed
|
101
|
+
rescue ::Plum::LegacyHTTPError => e
|
102
|
+
@logger.info "legacy HTTP client: #{e}"
|
103
|
+
sess = LegacySession.new(svc, e, sock)
|
104
|
+
sess.run
|
105
|
+
rescue => e
|
106
|
+
svc.log_exception(e)
|
107
|
+
ensure
|
108
|
+
sock.close if sock
|
109
|
+
end
|
110
|
+
}
|
74
111
|
end
|
75
112
|
|
76
113
|
private
|
@@ -126,8 +163,20 @@ module Plum
|
|
126
163
|
@server.to_io
|
127
164
|
end
|
128
165
|
|
129
|
-
def
|
130
|
-
|
166
|
+
def accept(svc)
|
167
|
+
sock = @server.accept
|
168
|
+
Thread.start {
|
169
|
+
begin
|
170
|
+
plum = ::Plum::ServerConnection.new(sock.method(:write))
|
171
|
+
sess = Session.new(svc, sock, plum)
|
172
|
+
sess.run
|
173
|
+
rescue Errno::ECONNRESET, EOFError # closed
|
174
|
+
rescue => e
|
175
|
+
svc.log_exception(e)
|
176
|
+
ensure
|
177
|
+
sock.close if sock
|
178
|
+
end
|
179
|
+
}
|
131
180
|
end
|
132
181
|
end
|
133
182
|
end
|
data/lib/plum/rack/server.rb
CHANGED
@@ -2,18 +2,15 @@
|
|
2
2
|
module Plum
|
3
3
|
module Rack
|
4
4
|
class Server
|
5
|
-
attr_reader :config
|
5
|
+
attr_reader :config, :app, :logger, :threadpool
|
6
6
|
|
7
7
|
def initialize(app, config)
|
8
8
|
@config = config
|
9
9
|
@state = :null
|
10
10
|
@app = config[:debug] ? ::Rack::CommonLogger.new(app) : app
|
11
|
-
@logger = Logger.new(config[:log] || $stdout).tap { |l|
|
12
|
-
|
13
|
-
|
14
|
-
@listeners = config[:listeners].map { |lc|
|
15
|
-
lc[:listener].new(lc)
|
16
|
-
}
|
11
|
+
@logger = Logger.new(config[:log] || $stdout).tap { |l| l.level = config[:debug] ? Logger::DEBUG : Logger::INFO }
|
12
|
+
@listeners = config[:listeners].map { |lc| lc[:listener].new(lc) }
|
13
|
+
@threadpool = ThreadPool.new(@config[:threadpool_size]) if @config[:threaded]
|
17
14
|
|
18
15
|
@logger.info("Plum #{::Plum::VERSION}")
|
19
16
|
@logger.info("Config: #{config}")
|
@@ -24,87 +21,41 @@ module Plum
|
|
24
21
|
end
|
25
22
|
|
26
23
|
def start
|
24
|
+
#trap(:INT) { @state = :ee }
|
25
|
+
#require "lineprof"
|
26
|
+
#Lineprof.profile(//){
|
27
27
|
@state = :running
|
28
|
-
while @state == :running
|
29
|
-
break if @listeners.empty?
|
28
|
+
while @state == :running && !@listeners.empty?
|
30
29
|
begin
|
31
30
|
if ss = IO.select(@listeners, nil, nil, 2.0)
|
32
31
|
ss[0].each { |svr|
|
33
|
-
|
32
|
+
begin
|
33
|
+
svr.accept(self)
|
34
|
+
rescue Errno::ECONNRESET, Errno::ECONNABORTED # closed
|
35
|
+
rescue => e
|
36
|
+
log_exception(e)
|
37
|
+
end
|
34
38
|
}
|
35
39
|
end
|
36
|
-
rescue Errno::EBADF
|
37
|
-
rescue
|
40
|
+
rescue Errno::EBADF # closed
|
41
|
+
rescue => e
|
38
42
|
log_exception(e)
|
39
43
|
end
|
40
44
|
end
|
45
|
+
#}
|
41
46
|
end
|
42
47
|
|
43
48
|
def stop
|
44
49
|
@state = :stop
|
45
50
|
@listeners.map(&:stop)
|
46
|
-
# TODO: gracefully shutdown connections
|
51
|
+
# TODO: gracefully shutdown connections (wait threadpool?)
|
47
52
|
end
|
48
53
|
|
49
54
|
private
|
50
|
-
def new_con(svr)
|
51
|
-
sock = svr.accept
|
52
|
-
Thread.new {
|
53
|
-
begin
|
54
|
-
begin
|
55
|
-
sock = sock.accept if sock.respond_to?(:accept)
|
56
|
-
plum = svr.plum(sock)
|
57
|
-
|
58
|
-
con = Session.new(app: @app,
|
59
|
-
plum: plum,
|
60
|
-
sock: sock,
|
61
|
-
logger: @logger,
|
62
|
-
config: @config,
|
63
|
-
remote_addr: sock.peeraddr.last)
|
64
|
-
con.run
|
65
|
-
rescue ::Plum::LegacyHTTPError => e
|
66
|
-
@logger.info "legacy HTTP client: #{e}"
|
67
|
-
handle_legacy(e, sock)
|
68
|
-
end
|
69
|
-
rescue Errno::ECONNRESET, Errno::EPROTO, Errno::EINVAL, EOFError => e # closed
|
70
|
-
rescue StandardError => e
|
71
|
-
log_exception(e)
|
72
|
-
ensure
|
73
|
-
sock.close if sock
|
74
|
-
end
|
75
|
-
}
|
76
|
-
rescue Errno::ECONNRESET, Errno::ECONNABORTED, Errno::EPROTO, Errno::EINVAL => e # closed
|
77
|
-
sock.close if sock
|
78
|
-
rescue StandardError => e
|
79
|
-
log_exception(e)
|
80
|
-
sock.close if sock
|
81
|
-
end
|
82
|
-
|
83
55
|
def log_exception(e)
|
84
56
|
@logger.error("#{e.class}: #{e.message}\n#{e.backtrace.map { |b| "\t#{b}" }.join("\n")}")
|
85
57
|
end
|
86
58
|
|
87
|
-
def handle_legacy(e, sock)
|
88
|
-
if @config[:fallback_legacy_host]
|
89
|
-
@logger.info "legacy HTTP: fallbacking to: #{@config[:fallback_legacy_host]}:#{@config[:fallback_legacy_port]}"
|
90
|
-
upstream = TCPSocket.open(@config[:fallback_legacy_host], @config[:fallback_legacy_port])
|
91
|
-
upstream.write(e.buf) if e.buf
|
92
|
-
loop do
|
93
|
-
ret = IO.select([sock, upstream])
|
94
|
-
ret[0].each { |s|
|
95
|
-
a = s.readpartial(65536)
|
96
|
-
if s == upstream
|
97
|
-
sock.write(a)
|
98
|
-
else
|
99
|
-
upstream.write(a)
|
100
|
-
end
|
101
|
-
}
|
102
|
-
end
|
103
|
-
end
|
104
|
-
ensure
|
105
|
-
upstream.close if upstream
|
106
|
-
end
|
107
|
-
|
108
59
|
def drop_privileges
|
109
60
|
begin
|
110
61
|
user = @config[:user]
|
data/lib/plum/rack/session.rb
CHANGED
@@ -8,14 +8,15 @@ module Plum
|
|
8
8
|
class Session
|
9
9
|
attr_reader :app, :plum
|
10
10
|
|
11
|
-
def initialize(
|
12
|
-
@
|
13
|
-
@
|
11
|
+
def initialize(svc, sock, plum)
|
12
|
+
@svc = svc
|
13
|
+
@app = svc.app
|
14
14
|
@sock = sock
|
15
|
-
@
|
16
|
-
@
|
17
|
-
@
|
18
|
-
@
|
15
|
+
@plum = plum
|
16
|
+
@logger = svc.logger
|
17
|
+
@config = svc.config
|
18
|
+
@remote_addr = sock.peeraddr.last
|
19
|
+
@threadpool = svc.threadpool
|
19
20
|
|
20
21
|
setup_plum
|
21
22
|
end
|
@@ -24,12 +25,15 @@ module Plum
|
|
24
25
|
@plum.close
|
25
26
|
end
|
26
27
|
|
28
|
+
def to_io
|
29
|
+
@sock.to_io
|
30
|
+
end
|
31
|
+
|
27
32
|
def run
|
28
33
|
while !@sock.closed? && !@sock.eof?
|
29
34
|
@plum << @sock.readpartial(1024)
|
30
35
|
end
|
31
36
|
ensure
|
32
|
-
@request_thread.each { |stream, thread| thread.kill }
|
33
37
|
stop
|
34
38
|
end
|
35
39
|
|
@@ -57,12 +61,21 @@ module Plum
|
|
57
61
|
}
|
58
62
|
|
59
63
|
@plum.on(:end_stream) { |stream|
|
60
|
-
|
61
|
-
|
62
|
-
|
64
|
+
req = reqs.delete(stream)
|
65
|
+
err = proc { |err|
|
66
|
+
stream.send_headers({ ":status" => 500 }, end_stream: true)
|
67
|
+
@logger.error(err)
|
68
|
+
}
|
69
|
+
if @threadpool
|
70
|
+
@threadpool.acquire(err) {
|
71
|
+
handle_request(stream, req[:headers], req[:data])
|
63
72
|
}
|
64
73
|
else
|
65
|
-
|
74
|
+
begin
|
75
|
+
handle_request(stream, req[:headers], req[:data])
|
76
|
+
rescue
|
77
|
+
err.call($!)
|
78
|
+
end
|
66
79
|
end
|
67
80
|
}
|
68
81
|
end
|
@@ -138,8 +151,6 @@ module Plum
|
|
138
151
|
send_body(st, p_body) unless pno_body
|
139
152
|
}
|
140
153
|
end
|
141
|
-
|
142
|
-
@request_thread.delete(stream)
|
143
154
|
end
|
144
155
|
|
145
156
|
def new_env(h, data)
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# -*- frozen-string-literal: true -*-
|
2
|
+
module Plum
|
3
|
+
module Rack
|
4
|
+
class ThreadPool
|
5
|
+
def initialize(size = 20)
|
6
|
+
@workers = Set.new
|
7
|
+
@jobs = Queue.new
|
8
|
+
|
9
|
+
size.times { |i|
|
10
|
+
spawn_worker
|
11
|
+
}
|
12
|
+
end
|
13
|
+
|
14
|
+
# returns cancel token
|
15
|
+
def acquire(tag = nil, err = nil, &blk)
|
16
|
+
@jobs << [blk, err]
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
def spawn_worker
|
21
|
+
t = Thread.new {
|
22
|
+
while true
|
23
|
+
job, err = @jobs.pop
|
24
|
+
begin
|
25
|
+
job.call
|
26
|
+
rescue => e
|
27
|
+
err << e if err
|
28
|
+
end
|
29
|
+
end
|
30
|
+
}
|
31
|
+
@workers << t
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/plum/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: plum
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- rhenium
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-01-
|
11
|
+
date: 2016-01-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -200,9 +200,11 @@ files:
|
|
200
200
|
- lib/plum/rack/cli.rb
|
201
201
|
- lib/plum/rack/config.rb
|
202
202
|
- lib/plum/rack/dsl.rb
|
203
|
+
- lib/plum/rack/legacy_session.rb
|
203
204
|
- lib/plum/rack/listener.rb
|
204
205
|
- lib/plum/rack/server.rb
|
205
206
|
- lib/plum/rack/session.rb
|
207
|
+
- lib/plum/rack/thread_pool.rb
|
206
208
|
- lib/plum/server/connection.rb
|
207
209
|
- lib/plum/server/http_connection.rb
|
208
210
|
- lib/plum/server/ssl_socket_connection.rb
|