grpc_kit 0.3.7 → 0.5.1
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 +17 -0
- data/README.md +4 -4
- 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 +17 -10
- data/lib/grpc_kit/grpc/dsl.rb +13 -11
- data/lib/grpc_kit/method_config.rb +5 -5
- data/lib/grpc_kit/rpc_desc.rb +19 -2
- data/lib/grpc_kit/rpcs.rb +3 -0
- data/lib/grpc_kit/server.rb +13 -4
- 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/drain_controller.rb +1 -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/send_buffer.rb +4 -0
- 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 +13 -7
- 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 +5 -6
- data/.ruby-version +0 -1
- 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: 854c24c4b4668ac76230b5c262893ae24f66ea7c644e1b46389e2546783004f6
|
4
|
+
data.tar.gz: 5b3cc154a4fbed3ebabffc62611b328a26f8d4006f692b15c397ce292880171f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ba44498c88cb8040c2b6cc9bbc183f7d452f72b47496cdde90c26ae3e72a60496ee47ec2c2fc5659c44ca5ab0ab32bd2f4fa9ecd727cf2ea3096b708d15eefcc
|
7
|
+
data.tar.gz: b9ef8c8d03788525a4927d93ed38c66afffc58eaef70865c152a04f0dbe5936d1edabde8357dfb8db8b70b38a0e31b0edc7d2321882eb2a2290f726a607cbfd7
|
@@ -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,17 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
## v0.5.1 (2021-05-19)
|
4
|
+
|
5
|
+
- improve: Set END_STREAM flag on the last DATA frame on request stream ([#34](https://github.com/cookpad/grpc_kit/pull/34))
|
6
|
+
|
7
|
+
## v0.5.0 (2021-04-22)
|
8
|
+
|
9
|
+
- improve: Configurable max_receive_message_size and max_send_message_size ([#33](https://github.com/cookpad/grpc_kit/pull/33))
|
10
|
+
|
11
|
+
## v0.4.0 (2020-10-23)
|
12
|
+
|
13
|
+
- bug: Fix Ruby 2.7 keyword argument separation warnings ([#27](https://github.com/cookpad/grpc_kit/pull/27))
|
14
|
+
- bug: HTTP/2 Trailer (grpc-status) might not be sent due to race condition ([#30](https://github.com/cookpad/grpc_kit/pull/30))
|
15
|
+
- improve: Reduce number of select(2) calls by adding pipe(2) to wake blocking threads ([#28](https://github.com/cookpad/grpc_kit/pull/28))
|
16
|
+
- improve: Improved performance when receiving streaming messages by blocking queue. ([#31](https://github.com/cookpad/grpc_kit/pull/31))
|
17
|
+
|
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.
|
@@ -27,7 +27,7 @@ $ gem install grpc_kit
|
|
27
27
|
|
28
28
|
## Usage
|
29
29
|
|
30
|
-
More Details in [examples directory](https://github.com/
|
30
|
+
More Details in [examples directory](https://github.com/cookpad/grpc_kit/tree/master/examples).
|
31
31
|
|
32
32
|
##### Server
|
33
33
|
|
@@ -59,11 +59,11 @@ $ bundle install
|
|
59
59
|
|
60
60
|
## Projects using grpc_kit
|
61
61
|
|
62
|
-
* [griffin](https://github.com/
|
62
|
+
* [griffin](https://github.com/cookpad/griffin) Multi process gRPC server in Ruby
|
63
63
|
|
64
64
|
## Contributing
|
65
65
|
|
66
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
66
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/cookpad/grpc_kit.
|
67
67
|
|
68
68
|
## License
|
69
69
|
|
@@ -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
@@ -12,7 +12,9 @@ module GrpcKit
|
|
12
12
|
# @param authority [nil, String]
|
13
13
|
# @param interceptors [Array<GrpcKit::Grpc::ClientInterceptor>] list of interceptors
|
14
14
|
# @param timeout [nil, Integer, String]
|
15
|
-
|
15
|
+
# @param max_receive_message_size [Integer, nil] Specify the default maximum size of inbound message in bytes. Default to 4MB.
|
16
|
+
# @param max_send_message_size [Integer, nil] Specify the default maximum size of outbound message in bytes. Default to 4MB.
|
17
|
+
def initialize(sock, authority: nil, interceptors: [], timeout: nil, max_receive_message_size: nil, max_send_message_size: nil)
|
16
18
|
@sock = sock
|
17
19
|
@authority =
|
18
20
|
if authority
|
@@ -24,38 +26,43 @@ module GrpcKit
|
|
24
26
|
|
25
27
|
@timeout = timeout && GrpcKit::GrpcTime.new(timeout)
|
26
28
|
|
27
|
-
|
29
|
+
# Defined at GrpcKit::Grpc::Dsl#.rpc_stub_class
|
30
|
+
build_rpcs(
|
31
|
+
interceptors,
|
32
|
+
max_receive_message_size: max_receive_message_size,
|
33
|
+
max_send_message_size: max_send_message_size,
|
34
|
+
)
|
28
35
|
end
|
29
36
|
|
30
37
|
# @param rpc [GrpcKit::Rpcs::Client::RequestResponse]
|
31
38
|
# @param request [Object]
|
32
39
|
# @param opts [Hash]
|
33
|
-
def request_response(rpc, request, opts
|
40
|
+
def request_response(rpc, request, **opts)
|
34
41
|
GrpcKit.logger.debug('Calling request_respose')
|
35
|
-
do_request(rpc, request, opts)
|
42
|
+
do_request(rpc, request, **opts)
|
36
43
|
end
|
37
44
|
|
38
45
|
# @param rpc [GrpcKit::Rpcs::Client::ClientStreamer]
|
39
46
|
# @param opts [Hash]
|
40
|
-
def client_streamer(rpc, opts
|
47
|
+
def client_streamer(rpc, **opts)
|
41
48
|
GrpcKit.logger.debug('Calling client_streamer')
|
42
|
-
do_request(rpc, nil, opts)
|
49
|
+
do_request(rpc, nil, **opts)
|
43
50
|
end
|
44
51
|
|
45
52
|
# @param rpc [GrpcKit::Rpcs::Client::ServerStreamer]
|
46
53
|
# @param request [Object]
|
47
54
|
# @param opts [Hash]
|
48
|
-
def server_streamer(rpc, request, opts
|
55
|
+
def server_streamer(rpc, request, **opts)
|
49
56
|
GrpcKit.logger.debug('Calling server_streamer')
|
50
|
-
do_request(rpc, request, opts)
|
57
|
+
do_request(rpc, request, **opts)
|
51
58
|
end
|
52
59
|
|
53
60
|
# @param rpc [GrpcKit::Rpcs::Client::ServerStreamer]
|
54
61
|
# @param _requests [Object] it's for compatibility, no use
|
55
62
|
# @param opts [Hash]
|
56
|
-
def bidi_streamer(rpc, _requests, opts
|
63
|
+
def bidi_streamer(rpc, _requests, **opts)
|
57
64
|
GrpcKit.logger.debug('Calling bidi_streamer')
|
58
|
-
do_request(rpc, nil, opts)
|
65
|
+
do_request(rpc, nil, **opts)
|
59
66
|
end
|
60
67
|
|
61
68
|
private
|
data/lib/grpc_kit/grpc/dsl.rb
CHANGED
@@ -7,6 +7,8 @@ require 'grpc_kit/grpc/stream'
|
|
7
7
|
|
8
8
|
module GrpcKit
|
9
9
|
module Grpc
|
10
|
+
# Methods under GrpcKit::Grpc::Dsl is available for classes include GRPC::GenericService.
|
11
|
+
# See also: GrpcKit::Grpc::GenericService
|
10
12
|
module Dsl
|
11
13
|
# @param value [String]
|
12
14
|
attr_accessor :service_name
|
@@ -68,34 +70,34 @@ module GrpcKit
|
|
68
70
|
end
|
69
71
|
|
70
72
|
Class.new(GrpcKit::Client) do
|
71
|
-
def initialize(
|
73
|
+
def initialize(*, **)
|
72
74
|
@rpcs = {}
|
73
75
|
super
|
74
76
|
end
|
75
77
|
|
76
|
-
define_method(:build_rpcs) do |interceptors|
|
78
|
+
define_method(:build_rpcs) do |interceptors, **options|
|
77
79
|
rpc_descs_.each do |method_name, rpc_desc|
|
78
|
-
@rpcs[method_name] = rpc_desc.build_client(interceptors: interceptors)
|
80
|
+
@rpcs[method_name] = rpc_desc.build_client(interceptors: interceptors, **options)
|
79
81
|
end
|
80
82
|
end
|
81
83
|
private :build_rpcs
|
82
84
|
|
83
85
|
rpc_descs_.each do |method_name, rpc_desc|
|
84
86
|
if rpc_desc.request_response?
|
85
|
-
define_method(method_name) do |request, opts
|
86
|
-
request_response(@rpcs.fetch(method_name), request, opts)
|
87
|
+
define_method(method_name) do |request, **opts|
|
88
|
+
request_response(@rpcs.fetch(method_name), request, **opts)
|
87
89
|
end
|
88
90
|
elsif rpc_desc.client_streamer?
|
89
|
-
define_method(method_name) do
|
90
|
-
client_streamer(@rpcs.fetch(method_name), opts)
|
91
|
+
define_method(method_name) do |**opts|
|
92
|
+
client_streamer(@rpcs.fetch(method_name), **opts)
|
91
93
|
end
|
92
94
|
elsif rpc_desc.server_streamer?
|
93
|
-
define_method(method_name) do |request, opts
|
94
|
-
server_streamer(@rpcs.fetch(method_name), request, opts)
|
95
|
+
define_method(method_name) do |request, **opts|
|
96
|
+
server_streamer(@rpcs.fetch(method_name), request, **opts)
|
95
97
|
end
|
96
98
|
elsif rpc_desc.bidi_streamer?
|
97
|
-
define_method(method_name) do |requests, opts
|
98
|
-
bidi_streamer(@rpcs.fetch(method_name), requests, opts, &blk)
|
99
|
+
define_method(method_name) do |requests, **opts, &blk|
|
100
|
+
bidi_streamer(@rpcs.fetch(method_name), requests, **opts, &blk)
|
99
101
|
end
|
100
102
|
else
|
101
103
|
raise "unknown #{rpc_desc}"
|
@@ -1,6 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module GrpcKit
|
4
|
+
MAX_SERVER_RECEIVE_MESSAGE_SIZE = 1024 * 1024 * 4
|
5
|
+
MAX_SERVER_SEND_MESSAGE_SIZE = 1024 * 1024 * 4
|
6
|
+
MAX_CLIENT_RECEIVE_MESSAGE_SIZE = 1024 * 1024 * 4
|
7
|
+
MAX_CLIENT_SEND_MESSAGE_SIZE = 1024 * 1024 * 4
|
8
|
+
|
4
9
|
MethodConfig = Struct.new(
|
5
10
|
:path,
|
6
11
|
:ruby_style_method_name,
|
@@ -12,11 +17,6 @@ module GrpcKit
|
|
12
17
|
:max_send_message_size,
|
13
18
|
:compressor_type,
|
14
19
|
) do
|
15
|
-
MAX_SERVER_RECEIVE_MESSAGE_SIZE = 1024 * 1024 * 4
|
16
|
-
MAX_SERVER_SEND_MESSAGE_SIZE = 1024 * 1024 * 4
|
17
|
-
MAX_CLIENT_RECEIVE_MESSAGE_SIZE = 1024 * 1024 * 4
|
18
|
-
MAX_CLIENT_SEND_MESSAGE_SIZE = 1024 * 1024 * 4
|
19
|
-
|
20
20
|
def self.build_for_server(
|
21
21
|
path:, ruby_style_method_name:, codec:, service_name:, method_name:, interceptor:,
|
22
22
|
max_receive_message_size: MAX_SERVER_RECEIVE_MESSAGE_SIZE, max_send_message_size: MAX_SERVER_SEND_MESSAGE_SIZE, compressor_type: ''
|
data/lib/grpc_kit/rpc_desc.rb
CHANGED
@@ -38,10 +38,21 @@ module GrpcKit
|
|
38
38
|
@service_name = service_name
|
39
39
|
end
|
40
40
|
|
41
|
+
# @return [String]
|
42
|
+
attr_reader :name, :service_name
|
43
|
+
|
44
|
+
# @return [Class, GrpcKit::Grpc::Stream]
|
45
|
+
attr_reader :marshal, :unmarshal
|
46
|
+
|
47
|
+
# @return [Symbol]
|
48
|
+
attr_reader :marshal_method, :unmarshal_method
|
49
|
+
|
41
50
|
# @param handler [GrpcKit::Grpc::GenericService]
|
42
51
|
# @param interceptors [Array<GrpcKit::Grpc::ServerInterceptor>]
|
52
|
+
# @param max_receive_message_size [Integer, nil]
|
53
|
+
# @param max_send_message_size [Integer, nil]
|
43
54
|
# @return [#invoke] Server version of rpc class
|
44
|
-
def build_server(handler, interceptors: [])
|
55
|
+
def build_server(handler, interceptors: [], max_send_message_size: nil, max_receive_message_size: nil)
|
45
56
|
inter = interceptors.empty? ? nil : server_interceptor.new(interceptors)
|
46
57
|
|
47
58
|
config = GrpcKit::MethodConfig.build_for_server(
|
@@ -51,13 +62,17 @@ module GrpcKit
|
|
51
62
|
service_name: @service_name,
|
52
63
|
method_name: @name,
|
53
64
|
interceptor: inter,
|
65
|
+
max_receive_message_size: max_receive_message_size || GrpcKit::MAX_SERVER_RECEIVE_MESSAGE_SIZE,
|
66
|
+
max_send_message_size: max_send_message_size || GrpcKit::MAX_SERVER_SEND_MESSAGE_SIZE,
|
54
67
|
)
|
55
68
|
server.new(handler, config)
|
56
69
|
end
|
57
70
|
|
58
71
|
# @param interceptors [Array<GrpcKit::Grpc::ClientInterceptor>]
|
72
|
+
# @param max_receive_message_size [Integer, nil]
|
73
|
+
# @param max_send_message_size [Integer, nil]
|
59
74
|
# @return [#invoke] Client version of rpc class
|
60
|
-
def build_client(interceptors: [])
|
75
|
+
def build_client(interceptors: [], max_send_message_size: nil, max_receive_message_size: nil)
|
61
76
|
inter = interceptors.empty? ? nil : client_interceptor.new(interceptors)
|
62
77
|
|
63
78
|
config = GrpcKit::MethodConfig.build_for_client(
|
@@ -67,6 +82,8 @@ module GrpcKit
|
|
67
82
|
service_name: @service_name,
|
68
83
|
method_name: @name,
|
69
84
|
interceptor: inter,
|
85
|
+
max_receive_message_size: max_receive_message_size || GrpcKit::MAX_CLIENT_RECEIVE_MESSAGE_SIZE,
|
86
|
+
max_send_message_size: max_send_message_size || GrpcKit::MAX_CLIENT_SEND_MESSAGE_SIZE,
|
70
87
|
)
|
71
88
|
client.new(config)
|
72
89
|
end
|
data/lib/grpc_kit/rpcs.rb
CHANGED
data/lib/grpc_kit/server.rb
CHANGED
@@ -10,15 +10,20 @@ module GrpcKit
|
|
10
10
|
# @param shutdown_timeout [Integer] Number of seconds to wait for the server shutdown
|
11
11
|
# @param min_pool_size [Integer] A mininum thread pool size
|
12
12
|
# @param max_pool_size [Integer] A maximum thread pool size
|
13
|
-
|
13
|
+
# @param max_receive_message_size [Integer, nil] Specify the maximum size of inbound message in bytes. Default to 4MB.
|
14
|
+
# @param max_send_message_size [Integer, nil] Specify the maximum size of outbound message in bytes. Default to 4MB.
|
15
|
+
def initialize(interceptors: [], shutdown_timeout: 30, min_pool_size: nil, max_pool_size: nil, settings: [], max_receive_message_size: nil, max_send_message_size: nil)
|
14
16
|
@interceptors = interceptors
|
15
17
|
@shutdown_timeout = shutdown_timeout
|
16
18
|
@min_pool_size = min_pool_size || GrpcKit::RpcDispatcher::DEFAULT_MIN
|
17
19
|
@max_pool_size = max_pool_size || GrpcKit::RpcDispatcher::DEFAULT_MAX
|
20
|
+
@max_receive_message_size = max_receive_message_size
|
21
|
+
@max_send_message_size = max_send_message_size
|
18
22
|
@sessions = []
|
19
23
|
@rpc_descs = {}
|
20
24
|
@mutex = Mutex.new
|
21
25
|
@stopping = false
|
26
|
+
@settings = settings
|
22
27
|
|
23
28
|
GrpcKit.logger.debug("Launched grpc_kit(v#{GrpcKit::VERSION})")
|
24
29
|
end
|
@@ -36,8 +41,12 @@ module GrpcKit
|
|
36
41
|
raise "Duplicated method registered #{path}, class: #{klass}"
|
37
42
|
end
|
38
43
|
|
39
|
-
|
40
|
-
|
44
|
+
@rpc_descs[path] = rpc_desc.build_server(
|
45
|
+
handler.is_a?(Class) ? handler.new : handler,
|
46
|
+
interceptors: @interceptors,
|
47
|
+
max_receive_message_size: @max_receive_message_size,
|
48
|
+
max_send_message_size: @max_send_message_size,
|
49
|
+
)
|
41
50
|
end
|
42
51
|
end
|
43
52
|
|
@@ -47,7 +56,7 @@ module GrpcKit
|
|
47
56
|
raise 'Stopping server' if @stopping
|
48
57
|
|
49
58
|
establish_session(conn) do |s|
|
50
|
-
s.submit_settings(
|
59
|
+
s.submit_settings(@settings)
|
51
60
|
s.start
|
52
61
|
end
|
53
62
|
end
|
@@ -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
|
@@ -15,10 +15,10 @@ module GrpcKit
|
|
15
15
|
def invoke(rpc)
|
16
16
|
rpc.invoke(self, metadata: @transport.recv_headers.metadata)
|
17
17
|
rescue GrpcKit::Errors::BadStatus => e
|
18
|
-
GrpcKit.logger.
|
18
|
+
GrpcKit.logger.warn(e)
|
19
19
|
send_status(status: e.code, msg: e.reason, metadata: {}) # TODO: metadata should be set
|
20
20
|
rescue StandardError => e
|
21
|
-
GrpcKit.logger.
|
21
|
+
GrpcKit.logger.warn(e)
|
22
22
|
send_status(status: GrpcKit::StatusCodes::UNKNOWN, msg: e.message, metadata: {})
|
23
23
|
end
|
24
24
|
|
@@ -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.5.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yuta Iwama
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-05-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ds9
|
@@ -143,11 +143,11 @@ 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
|
-
- ".travis.yml"
|
150
|
+
- CHANGELOG.md
|
151
151
|
- Gemfile
|
152
152
|
- LICENSE.txt
|
153
153
|
- README.md
|
@@ -252,8 +252,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
252
252
|
- !ruby/object:Gem::Version
|
253
253
|
version: '0'
|
254
254
|
requirements: []
|
255
|
-
|
256
|
-
rubygems_version: 2.7.6
|
255
|
+
rubygems_version: 3.1.4
|
257
256
|
signing_key:
|
258
257
|
specification_version: 4
|
259
258
|
summary: A kit for creating gRPC server/client
|
data/.ruby-version
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
2.5
|