grpc_kit 0.3.9 → 0.4.0
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/.github/workflows/ci.yml +25 -0
- data/CHANGELOG.md +9 -0
- data/README.md +1 -1
- data/lib/grpc_kit/calls/client_bidi_streamer.rb +17 -20
- data/lib/grpc_kit/calls/client_server_streamer.rb +1 -0
- data/lib/grpc_kit/calls/server_bidi_streamer.rb +2 -1
- data/lib/grpc_kit/calls/server_client_streamer.rb +2 -1
- data/lib/grpc_kit/calls/server_request_response.rb +1 -1
- data/lib/grpc_kit/calls/server_server_streamer.rb +1 -1
- data/lib/grpc_kit/client.rb +8 -8
- data/lib/grpc_kit/grpc/dsl.rb +9 -9
- data/lib/grpc_kit/session/client_session.rb +27 -21
- data/lib/grpc_kit/session/control_queue.rb +5 -1
- data/lib/grpc_kit/session/io.rb +16 -2
- data/lib/grpc_kit/session/recv_buffer.rb +51 -17
- data/lib/grpc_kit/session/server_session.rb +5 -2
- data/lib/grpc_kit/session/stream.rb +15 -3
- data/lib/grpc_kit/stream/client_stream.rb +1 -0
- data/lib/grpc_kit/stream/server_stream.rb +11 -5
- data/lib/grpc_kit/transport/client_transport.rb +28 -24
- data/lib/grpc_kit/transport/packable.rb +11 -8
- data/lib/grpc_kit/transport/server_transport.rb +10 -13
- data/lib/grpc_kit/version.rb +1 -1
- metadata +7 -6
- data/.travis.yml +0 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4a609dafaf12831fc04a1ffbf84f38b1551a408aeeeeb0fc22ec59eae33ab607
|
4
|
+
data.tar.gz: b2623749a3c1ec772963e081fafb4ca249fd0afba6e682fe61ccec68dd8e1c6a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bbf6bad8922e0058979e7e1962aed08ff7625f24691ae55cd62fc5a9ae6c53a6a68b1938050d17320b972b861e37edf1435e48497d63186863ed8e452c736eaa
|
7
|
+
data.tar.gz: aabd06f81259f04e8ea051466d88b5160cc062422f070a1bb6cb4945db43747ae488cbbe6d2f4af0bd97dfcf159c6705d52ceaaa7a6b916f7fc31b2f1378abf0
|
@@ -0,0 +1,25 @@
|
|
1
|
+
name: ci
|
2
|
+
on:
|
3
|
+
push:
|
4
|
+
branches:
|
5
|
+
- master
|
6
|
+
pull_request: {}
|
7
|
+
|
8
|
+
jobs:
|
9
|
+
ruby:
|
10
|
+
name: ruby
|
11
|
+
runs-on: ubuntu-latest
|
12
|
+
strategy:
|
13
|
+
matrix:
|
14
|
+
ruby_version:
|
15
|
+
- 2.5
|
16
|
+
- 2.6
|
17
|
+
- 2.7
|
18
|
+
steps:
|
19
|
+
- uses: actions/checkout@v2
|
20
|
+
- uses: ruby/setup-ruby@v1
|
21
|
+
with:
|
22
|
+
ruby-version: '${{ matrix.ruby_version }}'
|
23
|
+
bundler-cache: true
|
24
|
+
|
25
|
+
- run: 'bundle exec rake'
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
## v0.4.0 (2020-10-23)
|
4
|
+
|
5
|
+
- bug: Fix Ruby 2.7 keyword argument separation warnings ([#27](https://github.com/cookpad/grpc_kit/pull/27))
|
6
|
+
- bug: HTTP/2 Trailer (grpc-status) might not be sent due to race condition ([#30](https://github.com/cookpad/grpc_kit/pull/30))
|
7
|
+
- improve: Reduce number of select(2) calls by adding pipe(2) to wake blocking threads ([#28](https://github.com/cookpad/grpc_kit/pull/28))
|
8
|
+
- improve: Improved performance when receiving streaming messages by blocking queue. ([#31](https://github.com/cookpad/grpc_kit/pull/31))
|
9
|
+
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# GrpcKit
|
2
2
|
|
3
|
-
[](https://github.com/cookpad/grpc_kit/actions)
|
4
4
|
[](https://badge.fury.io/rb/grpc_kit)
|
5
5
|
|
6
6
|
A kit for creating [gRPC](https://grpc.io/) server/client in Ruby.
|
@@ -10,10 +10,13 @@ module GrpcKit
|
|
10
10
|
|
11
11
|
alias outgoing_metadata metadata
|
12
12
|
|
13
|
-
def initialize(
|
13
|
+
def initialize(**)
|
14
14
|
super
|
15
|
-
@
|
15
|
+
@recv_mutex = Mutex.new
|
16
|
+
|
16
17
|
@send = false
|
18
|
+
@send_cv = Thread::ConditionVariable.new
|
19
|
+
@send_mutex = Mutex.new
|
17
20
|
end
|
18
21
|
|
19
22
|
# @param data [Object] request message
|
@@ -23,29 +26,21 @@ module GrpcKit
|
|
23
26
|
raise "Upstream returns an error status: #{@reason}"
|
24
27
|
end
|
25
28
|
|
26
|
-
@
|
29
|
+
@send_mutex.synchronize do
|
27
30
|
@stream.send_msg(data, metadata: outgoing_metadata)
|
31
|
+
@send = true
|
32
|
+
@send_cv.broadcast
|
28
33
|
end
|
29
|
-
|
30
|
-
@send = true
|
31
34
|
end
|
32
35
|
|
33
|
-
# This method not
|
34
|
-
#
|
36
|
+
# Receive a message from peer. This method is not thread safe, never call from multiple threads at once.
|
35
37
|
# @return [Object] response object
|
38
|
+
# @raise [StopIteration]
|
36
39
|
def recv
|
37
|
-
|
38
|
-
|
39
|
-
loop do
|
40
|
-
msg = @mutex.synchronize do
|
41
|
-
@stream.recv_msg(blocking: false)
|
42
|
-
end
|
43
|
-
|
44
|
-
unless msg == :wait_readable
|
45
|
-
return msg
|
46
|
-
end
|
47
|
-
end
|
40
|
+
@send_mutex.synchronize { @send_cv.wait(@send_mutex) until @send } unless @send
|
48
41
|
|
42
|
+
msg = @stream.recv_msg(blocking: true)
|
43
|
+
return msg if msg
|
49
44
|
raise StopIteration
|
50
45
|
rescue GrpcKit::Errors::BadStatus => e
|
51
46
|
@reason = e
|
@@ -53,14 +48,16 @@ module GrpcKit
|
|
53
48
|
end
|
54
49
|
|
55
50
|
def close_and_send
|
56
|
-
@
|
51
|
+
@send_mutex.synchronize do
|
57
52
|
@stream.close_and_send
|
58
53
|
end
|
59
54
|
end
|
60
55
|
|
61
56
|
# @yieldparam response [Object] each response object of bidi streaming RPC
|
62
57
|
def each
|
63
|
-
|
58
|
+
@recv_mutex.synchronize do
|
59
|
+
loop { yield(recv) }
|
60
|
+
end
|
64
61
|
end
|
65
62
|
end
|
66
63
|
end
|
@@ -11,7 +11,7 @@ module GrpcKit
|
|
11
11
|
attr_reader :outgoing_initial_metadata, :outgoing_trailing_metadata
|
12
12
|
alias incoming_metadata metadata
|
13
13
|
|
14
|
-
def initialize(
|
14
|
+
def initialize(**)
|
15
15
|
super
|
16
16
|
|
17
17
|
@outgoing_initial_metadata = {}
|
@@ -30,6 +30,7 @@ module GrpcKit
|
|
30
30
|
)
|
31
31
|
end
|
32
32
|
|
33
|
+
# This method is not thread safe, never call from multiple threads at once.
|
33
34
|
# @return [Object] response object
|
34
35
|
def recv
|
35
36
|
@stream.recv_msg(@codec, limit_size: @config.max_receive_message_size)
|
@@ -11,7 +11,7 @@ module GrpcKit
|
|
11
11
|
attr_reader :outgoing_initial_metadata, :outgoing_trailing_metadata
|
12
12
|
alias incoming_metadata metadata
|
13
13
|
|
14
|
-
def initialize(
|
14
|
+
def initialize(**)
|
15
15
|
super
|
16
16
|
|
17
17
|
@outgoing_initial_metadata = {}
|
@@ -31,6 +31,7 @@ module GrpcKit
|
|
31
31
|
)
|
32
32
|
end
|
33
33
|
|
34
|
+
# This method is not thread safe, never call from multiple threads at once.
|
34
35
|
# @return [Object] response object
|
35
36
|
def recv
|
36
37
|
@stream.recv_msg(@codec, limit_size: @config.max_receive_message_size)
|
data/lib/grpc_kit/client.rb
CHANGED
@@ -30,32 +30,32 @@ module GrpcKit
|
|
30
30
|
# @param rpc [GrpcKit::Rpcs::Client::RequestResponse]
|
31
31
|
# @param request [Object]
|
32
32
|
# @param opts [Hash]
|
33
|
-
def request_response(rpc, request, opts
|
33
|
+
def request_response(rpc, request, **opts)
|
34
34
|
GrpcKit.logger.debug('Calling request_respose')
|
35
|
-
do_request(rpc, request, opts)
|
35
|
+
do_request(rpc, request, **opts)
|
36
36
|
end
|
37
37
|
|
38
38
|
# @param rpc [GrpcKit::Rpcs::Client::ClientStreamer]
|
39
39
|
# @param opts [Hash]
|
40
|
-
def client_streamer(rpc, opts
|
40
|
+
def client_streamer(rpc, **opts)
|
41
41
|
GrpcKit.logger.debug('Calling client_streamer')
|
42
|
-
do_request(rpc, nil, opts)
|
42
|
+
do_request(rpc, nil, **opts)
|
43
43
|
end
|
44
44
|
|
45
45
|
# @param rpc [GrpcKit::Rpcs::Client::ServerStreamer]
|
46
46
|
# @param request [Object]
|
47
47
|
# @param opts [Hash]
|
48
|
-
def server_streamer(rpc, request, opts
|
48
|
+
def server_streamer(rpc, request, **opts)
|
49
49
|
GrpcKit.logger.debug('Calling server_streamer')
|
50
|
-
do_request(rpc, request, opts)
|
50
|
+
do_request(rpc, request, **opts)
|
51
51
|
end
|
52
52
|
|
53
53
|
# @param rpc [GrpcKit::Rpcs::Client::ServerStreamer]
|
54
54
|
# @param _requests [Object] it's for compatibility, no use
|
55
55
|
# @param opts [Hash]
|
56
|
-
def bidi_streamer(rpc, _requests, opts
|
56
|
+
def bidi_streamer(rpc, _requests, **opts)
|
57
57
|
GrpcKit.logger.debug('Calling bidi_streamer')
|
58
|
-
do_request(rpc, nil, opts)
|
58
|
+
do_request(rpc, nil, **opts)
|
59
59
|
end
|
60
60
|
|
61
61
|
private
|
data/lib/grpc_kit/grpc/dsl.rb
CHANGED
@@ -68,7 +68,7 @@ module GrpcKit
|
|
68
68
|
end
|
69
69
|
|
70
70
|
Class.new(GrpcKit::Client) do
|
71
|
-
def initialize(
|
71
|
+
def initialize(*, **)
|
72
72
|
@rpcs = {}
|
73
73
|
super
|
74
74
|
end
|
@@ -82,20 +82,20 @@ module GrpcKit
|
|
82
82
|
|
83
83
|
rpc_descs_.each do |method_name, rpc_desc|
|
84
84
|
if rpc_desc.request_response?
|
85
|
-
define_method(method_name) do |request, opts
|
86
|
-
request_response(@rpcs.fetch(method_name), request, opts)
|
85
|
+
define_method(method_name) do |request, **opts|
|
86
|
+
request_response(@rpcs.fetch(method_name), request, **opts)
|
87
87
|
end
|
88
88
|
elsif rpc_desc.client_streamer?
|
89
|
-
define_method(method_name) do
|
90
|
-
client_streamer(@rpcs.fetch(method_name), opts)
|
89
|
+
define_method(method_name) do |**opts|
|
90
|
+
client_streamer(@rpcs.fetch(method_name), **opts)
|
91
91
|
end
|
92
92
|
elsif rpc_desc.server_streamer?
|
93
|
-
define_method(method_name) do |request, opts
|
94
|
-
server_streamer(@rpcs.fetch(method_name), request, opts)
|
93
|
+
define_method(method_name) do |request, **opts|
|
94
|
+
server_streamer(@rpcs.fetch(method_name), request, **opts)
|
95
95
|
end
|
96
96
|
elsif rpc_desc.bidi_streamer?
|
97
|
-
define_method(method_name) do |requests, opts
|
98
|
-
bidi_streamer(@rpcs.fetch(method_name), requests, opts, &blk)
|
97
|
+
define_method(method_name) do |requests, **opts, &blk|
|
98
|
+
bidi_streamer(@rpcs.fetch(method_name), requests, **opts, &blk)
|
99
99
|
end
|
100
100
|
else
|
101
101
|
raise "unknown #{rpc_desc}"
|
@@ -18,7 +18,7 @@ module GrpcKit
|
|
18
18
|
|
19
19
|
# @param io [GrpcKit::Session::IO]
|
20
20
|
# @param opts [Hash]
|
21
|
-
def initialize(io, opts
|
21
|
+
def initialize(io, **opts)
|
22
22
|
super() # initialize DS9::Session
|
23
23
|
|
24
24
|
@io = io
|
@@ -27,6 +27,7 @@ module GrpcKit
|
|
27
27
|
@draining = false
|
28
28
|
@stop = false
|
29
29
|
@no_write_data = false
|
30
|
+
@mutex = Mutex.new
|
30
31
|
end
|
31
32
|
|
32
33
|
# @param headers [Hash<String,String>]
|
@@ -47,7 +48,8 @@ module GrpcKit
|
|
47
48
|
# @param stream_id [Integer]
|
48
49
|
# @return [void]
|
49
50
|
def start(stream_id)
|
50
|
-
stream = @streams
|
51
|
+
stream = @streams[stream_id]
|
52
|
+
return unless stream # stream might have already close
|
51
53
|
|
52
54
|
loop do
|
53
55
|
if (!want_read? && !want_write?) || stream.close?
|
@@ -63,26 +65,28 @@ module GrpcKit
|
|
63
65
|
|
64
66
|
# @return [void]
|
65
67
|
def run_once
|
66
|
-
|
67
|
-
|
68
|
-
if @draining && @drain_time < Time.now
|
69
|
-
raise 'trasport is closing'
|
70
|
-
end
|
71
|
-
|
72
|
-
if @no_write_data
|
73
|
-
@io.wait_readable
|
68
|
+
@mutex.synchronize do
|
69
|
+
return if @stop
|
74
70
|
|
75
|
-
if
|
76
|
-
|
77
|
-
end
|
78
|
-
else
|
79
|
-
rs, ws = @io.select
|
80
|
-
if !rs.empty? && want_read?
|
81
|
-
do_read
|
71
|
+
if @draining && @drain_time < Time.now
|
72
|
+
raise 'trasport is closing'
|
82
73
|
end
|
83
74
|
|
84
|
-
if
|
85
|
-
|
75
|
+
if @no_write_data && !@streams.empty?
|
76
|
+
@io.wait_readable
|
77
|
+
|
78
|
+
if want_read?
|
79
|
+
do_read
|
80
|
+
end
|
81
|
+
else
|
82
|
+
rs, ws = @io.select
|
83
|
+
if !rs.empty? && want_read?
|
84
|
+
do_read
|
85
|
+
end
|
86
|
+
|
87
|
+
if !ws.empty? && want_write?
|
88
|
+
send
|
89
|
+
end
|
86
90
|
end
|
87
91
|
end
|
88
92
|
end
|
@@ -155,8 +159,10 @@ module GrpcKit
|
|
155
159
|
def on_stream_close(stream_id, error_code)
|
156
160
|
GrpcKit.logger.debug("on_stream_close stream_id=#{stream_id}, error_code=#{error_code}")
|
157
161
|
stream = @streams.delete(stream_id)
|
158
|
-
|
159
|
-
|
162
|
+
unless stream
|
163
|
+
GrpcKit.logger.warn("on_stream_close stream_id=#{stream_id} not remain on ClientSession")
|
164
|
+
return
|
165
|
+
end
|
160
166
|
stream.close
|
161
167
|
end
|
162
168
|
|
@@ -3,8 +3,9 @@
|
|
3
3
|
module GrpcKit
|
4
4
|
module Session
|
5
5
|
class ControlQueue
|
6
|
-
def initialize
|
6
|
+
def initialize(waker: proc { })
|
7
7
|
@event_stream = Queue.new
|
8
|
+
@waker = waker
|
8
9
|
end
|
9
10
|
|
10
11
|
# Be nonblocking
|
@@ -20,14 +21,17 @@ module GrpcKit
|
|
20
21
|
|
21
22
|
def submit_response(id, headers)
|
22
23
|
@event_stream.push([:submit_response, id, headers])
|
24
|
+
@waker.call(:submit_response)
|
23
25
|
end
|
24
26
|
|
25
27
|
def submit_headers(id, headers)
|
26
28
|
@event_stream.push([:submit_headers, id, headers])
|
29
|
+
@waker.call(:submit_headers)
|
27
30
|
end
|
28
31
|
|
29
32
|
def resume_data(id)
|
30
33
|
@event_stream.push([:resume_data, id])
|
34
|
+
@waker.call(:submit_response)
|
31
35
|
end
|
32
36
|
end
|
33
37
|
end
|
data/lib/grpc_kit/session/io.rb
CHANGED
@@ -7,9 +7,12 @@ module GrpcKit
|
|
7
7
|
class IO
|
8
8
|
def initialize(io)
|
9
9
|
@io = io
|
10
|
+
@wake_o, @wake_i = ::IO.pipe
|
10
11
|
end
|
11
12
|
|
12
13
|
def close
|
14
|
+
@wake_i.close
|
15
|
+
@wake_o.close
|
13
16
|
@io.close
|
14
17
|
end
|
15
18
|
|
@@ -52,8 +55,19 @@ module GrpcKit
|
|
52
55
|
|
53
56
|
# Blocking until io object is readable or writable
|
54
57
|
# @return [void]
|
55
|
-
def select(timeout
|
56
|
-
::IO.select([@io], [@io], [], timeout)
|
58
|
+
def select(timeout: 1, write: true)
|
59
|
+
rs, ws = ::IO.select([@io, @wake_o], write ? [@io] : [], [], timeout)
|
60
|
+
@wake_o.read(@wake_o.stat.size) if rs&.delete(@wake_o) && !@wake_o.closed?
|
61
|
+
[rs || [], ws || []]
|
62
|
+
end
|
63
|
+
|
64
|
+
# Wake thread blocked at #select method
|
65
|
+
# @param [Symbol] Indicate what event needed to invoke blocking thread. This argument is for debugging purpose.
|
66
|
+
def wake!(memo = nil)
|
67
|
+
@wake_i.write_nonblock(?\0, exception: false)
|
68
|
+
rescue Errno::EPIPE
|
69
|
+
rescue IOError
|
70
|
+
raise unless @wake_i.closed?
|
57
71
|
end
|
58
72
|
|
59
73
|
# @return [void]
|
@@ -3,35 +3,69 @@
|
|
3
3
|
module GrpcKit
|
4
4
|
module Session
|
5
5
|
class RecvBuffer
|
6
|
+
class Closed < Exception; end
|
7
|
+
|
6
8
|
def initialize
|
7
9
|
@buffer = +''.b
|
8
10
|
@end = false
|
9
|
-
@
|
11
|
+
@queue = Queue.new
|
10
12
|
end
|
11
13
|
|
12
14
|
# @param data [String]
|
13
15
|
# @return [void]
|
14
16
|
def write(data)
|
15
|
-
@
|
17
|
+
@queue << data
|
18
|
+
rescue ClosedQueueError
|
19
|
+
raise Closed, "[BUG] write to closed queue"
|
20
|
+
end
|
21
|
+
|
22
|
+
# @return [Boolean]
|
23
|
+
def empty?
|
24
|
+
@queue.empty?
|
25
|
+
end
|
26
|
+
|
27
|
+
# @return [Boolean]
|
28
|
+
def closed?
|
29
|
+
@queue.closed?
|
16
30
|
end
|
17
31
|
|
32
|
+
# @return [void]
|
33
|
+
def close
|
34
|
+
@queue.close
|
35
|
+
end
|
36
|
+
|
37
|
+
# This method is not thread safe (as RecvBuffer is designed to be a multi-producer/single-consumer)
|
18
38
|
# @param size [Integer,nil]
|
19
39
|
# @param last [Boolean]
|
20
|
-
# @
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
if
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
40
|
+
# @param blocking [Boolean]
|
41
|
+
# @return [String,Symbol,nil]
|
42
|
+
def read(size = nil, last: false, blocking:)
|
43
|
+
if @buffer.empty?
|
44
|
+
return nil if empty? && closed?
|
45
|
+
return :wait_readable if empty? && !blocking
|
46
|
+
|
47
|
+
# Consume existing data as much as possible to continue (important on clients where single-threaded)
|
48
|
+
loop do
|
49
|
+
begin
|
50
|
+
data = @queue.shift(!blocking)
|
51
|
+
@buffer << data if data
|
52
|
+
rescue ThreadError
|
53
|
+
break
|
54
|
+
end
|
55
|
+
|
56
|
+
break if empty?
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
buf = if size.nil? || @buffer.bytesize < size
|
61
|
+
rbuf = @buffer
|
62
|
+
@buffer = ''.b
|
63
|
+
rbuf
|
64
|
+
else
|
65
|
+
@buffer.freeze
|
66
|
+
rbuf = @buffer.byteslice(0, size)
|
67
|
+
@buffer = @buffer.byteslice(size, @buffer.bytesize)
|
68
|
+
rbuf
|
35
69
|
end
|
36
70
|
|
37
71
|
end_read if last
|
@@ -29,7 +29,7 @@ module GrpcKit
|
|
29
29
|
@stop = false
|
30
30
|
@inflights = []
|
31
31
|
@drain_controller = GrpcKit::Session::DrainController.new
|
32
|
-
@control_queue = GrpcKit::Session::ControlQueue.new
|
32
|
+
@control_queue = GrpcKit::Session::ControlQueue.new(waker: @io.method(:wake!))
|
33
33
|
@dispatcher = dispatcher
|
34
34
|
end
|
35
35
|
|
@@ -64,7 +64,7 @@ module GrpcKit
|
|
64
64
|
@drain_controller.next(self)
|
65
65
|
end
|
66
66
|
|
67
|
-
rs, ws = @io.select
|
67
|
+
rs, ws = @io.select(timeout: 5, write: want_write?)
|
68
68
|
|
69
69
|
if !rs.empty? && want_read?
|
70
70
|
do_read
|
@@ -148,6 +148,8 @@ module GrpcKit
|
|
148
148
|
data = @streams[stream_id].pending_send_data.read(length)
|
149
149
|
if data.nil?
|
150
150
|
submit_trailer(stream_id, stream.trailer_data)
|
151
|
+
@io.wake!(:submit_trailer)
|
152
|
+
|
151
153
|
# trailer header
|
152
154
|
false
|
153
155
|
else
|
@@ -180,6 +182,7 @@ module GrpcKit
|
|
180
182
|
stream.inflight = true
|
181
183
|
@dispatcher.schedule([stream, @control_queue])
|
182
184
|
end
|
185
|
+
|
183
186
|
when DS9::Frames::Headers
|
184
187
|
if frame.end_stream?
|
185
188
|
stream = @streams[frame.stream_id]
|
@@ -13,7 +13,6 @@ module GrpcKit
|
|
13
13
|
|
14
14
|
delegate %i[end_write?] => :@pending_send_data
|
15
15
|
delegate %i[end_read?] => :@pending_recv_data
|
16
|
-
delegate %i[close close_remote close_local close? close_remote? close_local?] => :@status
|
17
16
|
|
18
17
|
attr_reader :headers, :pending_send_data, :pending_recv_data, :trailer_data, :status
|
19
18
|
attr_accessor :inflight, :stream_id
|
@@ -59,9 +58,10 @@ module GrpcKit
|
|
59
58
|
end
|
60
59
|
|
61
60
|
# @param last [Boolean]
|
61
|
+
# @param blocking [Boolean]
|
62
62
|
# @return [void]
|
63
|
-
def read_recv_data(last: false)
|
64
|
-
@pending_recv_data.read(last: last)
|
63
|
+
def read_recv_data(last: false, blocking:)
|
64
|
+
@pending_recv_data.read(last: last, blocking: blocking)
|
65
65
|
end
|
66
66
|
|
67
67
|
# @param name [String]
|
@@ -70,6 +70,18 @@ module GrpcKit
|
|
70
70
|
def add_header(name, value)
|
71
71
|
@headers.add(name, value)
|
72
72
|
end
|
73
|
+
|
74
|
+
delegate %i[close_local close? close_remote? close_local?] => :@status
|
75
|
+
|
76
|
+
def close
|
77
|
+
status.close
|
78
|
+
pending_recv_data.close
|
79
|
+
end
|
80
|
+
|
81
|
+
def close_remote
|
82
|
+
status.close_remote
|
83
|
+
pending_recv_data.close
|
84
|
+
end
|
73
85
|
end
|
74
86
|
end
|
75
87
|
end
|
@@ -50,6 +50,7 @@ module GrpcKit
|
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
53
|
+
# This method is not thread safe, never call from multiple threads at once.
|
53
54
|
# @raise [StopIteration] when recving message finished
|
54
55
|
# @param codec [GrpcKit::Codec]
|
55
56
|
# @param last [Boolean]
|
@@ -89,13 +90,18 @@ module GrpcKit
|
|
89
90
|
t = build_trailers(status, msg, metadata)
|
90
91
|
@transport.write_data(data, last: true) if data
|
91
92
|
|
92
|
-
@
|
93
|
-
if @started
|
93
|
+
if @started # Complete stream
|
94
94
|
@transport.write_trailers(t)
|
95
|
-
|
95
|
+
@transport.end_write
|
96
|
+
|
97
|
+
elsif data # Complete stream with a data
|
96
98
|
@transport.write_trailers(t)
|
97
|
-
|
98
|
-
|
99
|
+
@transport.end_write
|
100
|
+
|
101
|
+
start_response # will send queued data and trailer.
|
102
|
+
|
103
|
+
else # return status (likely non-200) and immediately complete stream.
|
104
|
+
@transport.end_write
|
99
105
|
send_headers(trailers: t)
|
100
106
|
end
|
101
107
|
end
|
@@ -41,20 +41,31 @@ module GrpcKit
|
|
41
41
|
end
|
42
42
|
|
43
43
|
# @param last [Boolean]
|
44
|
-
# @return [nil,String]
|
44
|
+
# @return [nil,Array<Boolean,Integer,String>] nil when closed, tuple of Length-Prefixed-Message
|
45
45
|
def read_data(last: false)
|
46
|
-
unpack(
|
46
|
+
data_in_buffer = unpack(nil)
|
47
|
+
return data_in_buffer if data_in_buffer
|
48
|
+
loop do
|
49
|
+
data = recv_data(last: last)
|
50
|
+
return unpack(nil) unless data
|
51
|
+
message = unpack(data)
|
52
|
+
return message if message
|
53
|
+
end
|
47
54
|
end
|
48
55
|
|
49
56
|
# @param last [Boolean]
|
50
|
-
# @return [nil,String]
|
57
|
+
# @return [nil,Array<Boolean,Integer,String>,Symbol] nil when closed, tuple of Length-Prefixed-Message, or :wait_readable
|
51
58
|
def read_data_nonblock(last: false)
|
59
|
+
data_in_buffer = unpack(nil)
|
60
|
+
return data_in_buffer if data_in_buffer
|
61
|
+
|
52
62
|
data = nonblock_recv_data(last: last)
|
53
63
|
if data == :wait_readable
|
54
|
-
unpack(nil) # nil is needed read buffered data
|
55
64
|
:wait_readable
|
65
|
+
elsif data == nil
|
66
|
+
return unpack(nil)
|
56
67
|
else
|
57
|
-
unpack(data)
|
68
|
+
unpack(data) || :wait_readable
|
58
69
|
end
|
59
70
|
end
|
60
71
|
|
@@ -78,33 +89,26 @@ module GrpcKit
|
|
78
89
|
end
|
79
90
|
|
80
91
|
def nonblock_recv_data(last: false)
|
81
|
-
data = @stream.read_recv_data(last: last)
|
82
|
-
return data
|
83
|
-
|
84
|
-
if @stream.close_remote?
|
85
|
-
return nil
|
86
|
-
end
|
87
|
-
|
88
|
-
@session.run_once
|
92
|
+
data = @stream.read_recv_data(last: last, blocking: false)
|
93
|
+
return data if data.is_a?(String)
|
94
|
+
return nil unless data
|
89
95
|
|
90
96
|
:wait_readable
|
91
97
|
end
|
92
98
|
|
93
99
|
def recv_data(last: false)
|
94
100
|
loop do
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
101
|
+
# FIXME: GrpcKit::Client isn't threaded, this cannot be blocked to trigger ClientSession#run_once appropriately
|
102
|
+
# but run_once would block while no outbound requests. Could be problematic on BiDi calls.
|
103
|
+
data = @stream.read_recv_data(last: last, blocking: false)
|
104
|
+
case data
|
105
|
+
when :wait_readable
|
106
|
+
@session.run_once
|
107
|
+
when String
|
108
|
+
return data
|
109
|
+
when nil
|
104
110
|
return nil
|
105
111
|
end
|
106
|
-
|
107
|
-
@session.run_once
|
108
112
|
end
|
109
113
|
end
|
110
114
|
|
@@ -24,8 +24,8 @@ module GrpcKit
|
|
24
24
|
end
|
25
25
|
|
26
26
|
class Unpacker
|
27
|
-
# Compressed
|
28
|
-
|
27
|
+
# Compressed-Flag (1 byte) + Message-Length (4 Bytes)
|
28
|
+
PREFIX_SIZE = 5
|
29
29
|
|
30
30
|
def initialize
|
31
31
|
@data = +''.b
|
@@ -44,14 +44,17 @@ module GrpcKit
|
|
44
44
|
|
45
45
|
# @return [nil, Array<Boolean, Integer, String>]
|
46
46
|
def read
|
47
|
-
return nil if @data.
|
47
|
+
return nil if @data.bytesize < PREFIX_SIZE
|
48
|
+
|
49
|
+
prefix = @data.byteslice(0, PREFIX_SIZE)
|
50
|
+
compressed, length = prefix.unpack('CN')
|
51
|
+
|
52
|
+
return nil if (@data.bytesize-PREFIX_SIZE) < length
|
48
53
|
|
49
54
|
d = @data.freeze
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
@data = @data.byteslice(METADATA_SIZE + size, @data.bytesize)
|
54
|
-
[c != 0, size, data]
|
55
|
+
data = d.byteslice(PREFIX_SIZE, length)
|
56
|
+
@data = d.byteslice(PREFIX_SIZE + length, d.bytesize)
|
57
|
+
[compressed == 1, length, data]
|
55
58
|
end
|
56
59
|
end
|
57
60
|
end
|
@@ -36,9 +36,16 @@ module GrpcKit
|
|
36
36
|
end
|
37
37
|
|
38
38
|
# @param last [Boolean]
|
39
|
-
# @return [nil,String]
|
39
|
+
# @return [nil,Array<Boolean,Integer,String>] nil when closed, tuple of Length-Prefixed-Message
|
40
40
|
def read_data(last: false)
|
41
|
-
unpack(
|
41
|
+
data_in_buffer = unpack(nil)
|
42
|
+
return data_in_buffer if data_in_buffer
|
43
|
+
loop do
|
44
|
+
data = recv_data(last: last)
|
45
|
+
return nil unless data
|
46
|
+
message = unpack(data)
|
47
|
+
return message if message
|
48
|
+
end
|
42
49
|
end
|
43
50
|
|
44
51
|
# @param trailer [Hash<String, String>]
|
@@ -61,17 +68,7 @@ module GrpcKit
|
|
61
68
|
private
|
62
69
|
|
63
70
|
def recv_data(last: false)
|
64
|
-
|
65
|
-
data = @stream.read_recv_data(last: last)
|
66
|
-
return data if data
|
67
|
-
|
68
|
-
if @stream.close_remote?
|
69
|
-
# Call @stream.read_recv_data after checking @stream.close_remote?
|
70
|
-
# because of the order of nghttp2 callbacks which calls a callback receiving data before a callback receiving END_STREAM flag
|
71
|
-
data = @stream.read_recv_data(last: last)
|
72
|
-
return data
|
73
|
-
end
|
74
|
-
end
|
71
|
+
@stream.read_recv_data(last: last, blocking: true)
|
75
72
|
end
|
76
73
|
|
77
74
|
def send_data
|
data/lib/grpc_kit/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: grpc_kit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yuta Iwama
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-10-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ds9
|
@@ -143,11 +143,12 @@ executables: []
|
|
143
143
|
extensions: []
|
144
144
|
extra_rdoc_files: []
|
145
145
|
files:
|
146
|
+
- ".github/workflows/ci.yml"
|
146
147
|
- ".gitignore"
|
147
148
|
- ".rspec"
|
148
149
|
- ".rubocop.yml"
|
149
150
|
- ".ruby-version"
|
150
|
-
-
|
151
|
+
- CHANGELOG.md
|
151
152
|
- Gemfile
|
152
153
|
- LICENSE.txt
|
153
154
|
- README.md
|
@@ -237,7 +238,7 @@ homepage: https://github.com/ganmacs/grpc_kit
|
|
237
238
|
licenses:
|
238
239
|
- MIT
|
239
240
|
metadata: {}
|
240
|
-
post_install_message:
|
241
|
+
post_install_message:
|
241
242
|
rdoc_options: []
|
242
243
|
require_paths:
|
243
244
|
- lib
|
@@ -253,7 +254,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
253
254
|
version: '0'
|
254
255
|
requirements: []
|
255
256
|
rubygems_version: 3.1.2
|
256
|
-
signing_key:
|
257
|
+
signing_key:
|
257
258
|
specification_version: 4
|
258
259
|
summary: A kit for creating gRPC server/client
|
259
260
|
test_files: []
|