grpc 1.30.2-x86-mingw32
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of grpc might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/etc/roots.pem +4644 -0
- data/grpc_c.32.ruby +0 -0
- data/grpc_c.64.ruby +0 -0
- data/src/ruby/bin/math_client.rb +140 -0
- data/src/ruby/bin/math_pb.rb +34 -0
- data/src/ruby/bin/math_server.rb +191 -0
- data/src/ruby/bin/math_services_pb.rb +51 -0
- data/src/ruby/bin/noproto_client.rb +93 -0
- data/src/ruby/bin/noproto_server.rb +97 -0
- data/src/ruby/ext/grpc/ext-export.clang +1 -0
- data/src/ruby/ext/grpc/ext-export.gcc +6 -0
- data/src/ruby/ext/grpc/extconf.rb +107 -0
- data/src/ruby/ext/grpc/rb_byte_buffer.c +64 -0
- data/src/ruby/ext/grpc/rb_byte_buffer.h +35 -0
- data/src/ruby/ext/grpc/rb_call.c +1050 -0
- data/src/ruby/ext/grpc/rb_call.h +53 -0
- data/src/ruby/ext/grpc/rb_call_credentials.c +297 -0
- data/src/ruby/ext/grpc/rb_call_credentials.h +31 -0
- data/src/ruby/ext/grpc/rb_channel.c +835 -0
- data/src/ruby/ext/grpc/rb_channel.h +34 -0
- data/src/ruby/ext/grpc/rb_channel_args.c +155 -0
- data/src/ruby/ext/grpc/rb_channel_args.h +38 -0
- data/src/ruby/ext/grpc/rb_channel_credentials.c +267 -0
- data/src/ruby/ext/grpc/rb_channel_credentials.h +32 -0
- data/src/ruby/ext/grpc/rb_completion_queue.c +100 -0
- data/src/ruby/ext/grpc/rb_completion_queue.h +36 -0
- data/src/ruby/ext/grpc/rb_compression_options.c +470 -0
- data/src/ruby/ext/grpc/rb_compression_options.h +29 -0
- data/src/ruby/ext/grpc/rb_enable_cpp.cc +22 -0
- data/src/ruby/ext/grpc/rb_event_thread.c +143 -0
- data/src/ruby/ext/grpc/rb_event_thread.h +21 -0
- data/src/ruby/ext/grpc/rb_grpc.c +328 -0
- data/src/ruby/ext/grpc/rb_grpc.h +76 -0
- data/src/ruby/ext/grpc/rb_grpc_imports.generated.c +573 -0
- data/src/ruby/ext/grpc/rb_grpc_imports.generated.h +865 -0
- data/src/ruby/ext/grpc/rb_loader.c +57 -0
- data/src/ruby/ext/grpc/rb_loader.h +25 -0
- data/src/ruby/ext/grpc/rb_server.c +372 -0
- data/src/ruby/ext/grpc/rb_server.h +32 -0
- data/src/ruby/ext/grpc/rb_server_credentials.c +243 -0
- data/src/ruby/ext/grpc/rb_server_credentials.h +32 -0
- data/src/ruby/lib/grpc.rb +37 -0
- data/src/ruby/lib/grpc/2.3/grpc_c.so +0 -0
- data/src/ruby/lib/grpc/2.4/grpc_c.so +0 -0
- data/src/ruby/lib/grpc/2.5/grpc_c.so +0 -0
- data/src/ruby/lib/grpc/2.6/grpc_c.so +0 -0
- data/src/ruby/lib/grpc/2.7/grpc_c.so +0 -0
- data/src/ruby/lib/grpc/core/status_codes.rb +135 -0
- data/src/ruby/lib/grpc/core/time_consts.rb +56 -0
- data/src/ruby/lib/grpc/errors.rb +277 -0
- data/src/ruby/lib/grpc/generic/active_call.rb +669 -0
- data/src/ruby/lib/grpc/generic/bidi_call.rb +233 -0
- data/src/ruby/lib/grpc/generic/client_stub.rb +501 -0
- data/src/ruby/lib/grpc/generic/interceptor_registry.rb +53 -0
- data/src/ruby/lib/grpc/generic/interceptors.rb +186 -0
- data/src/ruby/lib/grpc/generic/rpc_desc.rb +204 -0
- data/src/ruby/lib/grpc/generic/rpc_server.rb +551 -0
- data/src/ruby/lib/grpc/generic/service.rb +211 -0
- data/src/ruby/lib/grpc/google_rpc_status_utils.rb +40 -0
- data/src/ruby/lib/grpc/grpc.rb +24 -0
- data/src/ruby/lib/grpc/logconfig.rb +44 -0
- data/src/ruby/lib/grpc/notifier.rb +45 -0
- data/src/ruby/lib/grpc/structs.rb +15 -0
- data/src/ruby/lib/grpc/version.rb +18 -0
- data/src/ruby/pb/README.md +42 -0
- data/src/ruby/pb/generate_proto_ruby.sh +51 -0
- data/src/ruby/pb/grpc/health/checker.rb +75 -0
- data/src/ruby/pb/grpc/health/v1/health_pb.rb +31 -0
- data/src/ruby/pb/grpc/health/v1/health_services_pb.rb +62 -0
- data/src/ruby/pb/grpc/testing/duplicate/echo_duplicate_services_pb.rb +44 -0
- data/src/ruby/pb/grpc/testing/metrics_pb.rb +28 -0
- data/src/ruby/pb/grpc/testing/metrics_services_pb.rb +49 -0
- data/src/ruby/pb/src/proto/grpc/testing/empty_pb.rb +17 -0
- data/src/ruby/pb/src/proto/grpc/testing/messages_pb.rb +105 -0
- data/src/ruby/pb/src/proto/grpc/testing/test_pb.rb +16 -0
- data/src/ruby/pb/src/proto/grpc/testing/test_services_pb.rb +118 -0
- data/src/ruby/pb/test/client.rb +769 -0
- data/src/ruby/pb/test/server.rb +252 -0
- data/src/ruby/pb/test/xds_client.rb +213 -0
- data/src/ruby/spec/call_credentials_spec.rb +42 -0
- data/src/ruby/spec/call_spec.rb +180 -0
- data/src/ruby/spec/channel_connection_spec.rb +126 -0
- data/src/ruby/spec/channel_credentials_spec.rb +82 -0
- data/src/ruby/spec/channel_spec.rb +234 -0
- data/src/ruby/spec/client_auth_spec.rb +126 -0
- data/src/ruby/spec/client_server_spec.rb +664 -0
- data/src/ruby/spec/compression_options_spec.rb +149 -0
- data/src/ruby/spec/debug_message_spec.rb +134 -0
- data/src/ruby/spec/error_sanity_spec.rb +49 -0
- data/src/ruby/spec/errors_spec.rb +142 -0
- data/src/ruby/spec/generic/active_call_spec.rb +672 -0
- data/src/ruby/spec/generic/client_interceptors_spec.rb +153 -0
- data/src/ruby/spec/generic/client_stub_spec.rb +1083 -0
- data/src/ruby/spec/generic/interceptor_registry_spec.rb +65 -0
- data/src/ruby/spec/generic/rpc_desc_spec.rb +374 -0
- data/src/ruby/spec/generic/rpc_server_pool_spec.rb +127 -0
- data/src/ruby/spec/generic/rpc_server_spec.rb +748 -0
- data/src/ruby/spec/generic/server_interceptors_spec.rb +218 -0
- data/src/ruby/spec/generic/service_spec.rb +263 -0
- data/src/ruby/spec/google_rpc_status_utils_spec.rb +282 -0
- data/src/ruby/spec/pb/codegen/grpc/testing/package_options.proto +28 -0
- data/src/ruby/spec/pb/codegen/grpc/testing/package_options_import.proto +22 -0
- data/src/ruby/spec/pb/codegen/grpc/testing/package_options_import2.proto +23 -0
- data/src/ruby/spec/pb/codegen/grpc/testing/package_options_ruby_style.proto +41 -0
- data/src/ruby/spec/pb/codegen/package_option_spec.rb +82 -0
- data/src/ruby/spec/pb/duplicate/codegen_spec.rb +57 -0
- data/src/ruby/spec/pb/health/checker_spec.rb +236 -0
- data/src/ruby/spec/server_credentials_spec.rb +79 -0
- data/src/ruby/spec/server_spec.rb +209 -0
- data/src/ruby/spec/spec_helper.rb +61 -0
- data/src/ruby/spec/support/helpers.rb +107 -0
- data/src/ruby/spec/support/services.rb +160 -0
- data/src/ruby/spec/testdata/README +1 -0
- data/src/ruby/spec/testdata/ca.pem +20 -0
- data/src/ruby/spec/testdata/client.key +28 -0
- data/src/ruby/spec/testdata/client.pem +20 -0
- data/src/ruby/spec/testdata/server1.key +28 -0
- data/src/ruby/spec/testdata/server1.pem +22 -0
- data/src/ruby/spec/time_consts_spec.rb +74 -0
- metadata +394 -0
@@ -0,0 +1,53 @@
|
|
1
|
+
# Copyright 2017 gRPC authors.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
# GRPC contains the General RPC module.
|
16
|
+
module GRPC
|
17
|
+
##
|
18
|
+
# Represents a registry of added interceptors available for enumeration.
|
19
|
+
# The registry can be used for both server and client interceptors.
|
20
|
+
# This class is internal to gRPC and not meant for public usage.
|
21
|
+
#
|
22
|
+
class InterceptorRegistry
|
23
|
+
##
|
24
|
+
# An error raised when an interceptor is attempted to be added
|
25
|
+
# that does not extend GRPC::Interceptor
|
26
|
+
#
|
27
|
+
class DescendantError < StandardError; end
|
28
|
+
|
29
|
+
##
|
30
|
+
# Initialize the registry with an empty interceptor list
|
31
|
+
# This is an EXPERIMENTAL API.
|
32
|
+
#
|
33
|
+
def initialize(interceptors = [])
|
34
|
+
@interceptors = []
|
35
|
+
interceptors.each do |i|
|
36
|
+
base = GRPC::Interceptor
|
37
|
+
unless i.class.ancestors.include?(base)
|
38
|
+
fail DescendantError, "Interceptors must descend from #{base}"
|
39
|
+
end
|
40
|
+
@interceptors << i
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
##
|
45
|
+
# Builds an interception context from this registry
|
46
|
+
#
|
47
|
+
# @return [InterceptionContext]
|
48
|
+
#
|
49
|
+
def build_context
|
50
|
+
InterceptionContext.new(@interceptors)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,186 @@
|
|
1
|
+
# Copyright 2017 gRPC authors.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
require_relative 'interceptor_registry'
|
15
|
+
|
16
|
+
# GRPC contains the General RPC module.
|
17
|
+
module GRPC
|
18
|
+
##
|
19
|
+
# Base class for interception in GRPC
|
20
|
+
#
|
21
|
+
class Interceptor
|
22
|
+
##
|
23
|
+
# @param [Hash] options A hash of options that will be used
|
24
|
+
# by the interceptor. This is an EXPERIMENTAL API.
|
25
|
+
#
|
26
|
+
def initialize(options = {})
|
27
|
+
@options = options || {}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
##
|
32
|
+
# ClientInterceptor allows for wrapping outbound gRPC client stub requests.
|
33
|
+
# This is an EXPERIMENTAL API.
|
34
|
+
#
|
35
|
+
class ClientInterceptor < Interceptor
|
36
|
+
##
|
37
|
+
# Intercept a unary request response call
|
38
|
+
#
|
39
|
+
# @param [Object] request
|
40
|
+
# @param [GRPC::ActiveCall] call
|
41
|
+
# @param [String] method
|
42
|
+
# @param [Hash] metadata
|
43
|
+
#
|
44
|
+
def request_response(request: nil, call: nil, method: nil, metadata: nil)
|
45
|
+
GRPC.logger.debug "Intercepting request response method #{method}" \
|
46
|
+
" for request #{request} with call #{call} and metadata: #{metadata}"
|
47
|
+
yield
|
48
|
+
end
|
49
|
+
|
50
|
+
##
|
51
|
+
# Intercept a client streaming call
|
52
|
+
#
|
53
|
+
# @param [Enumerable] requests
|
54
|
+
# @param [GRPC::ActiveCall] call
|
55
|
+
# @param [String] method
|
56
|
+
# @param [Hash] metadata
|
57
|
+
#
|
58
|
+
def client_streamer(requests: nil, call: nil, method: nil, metadata: nil)
|
59
|
+
GRPC.logger.debug "Intercepting client streamer method #{method}" \
|
60
|
+
" for requests #{requests} with call #{call} and metadata: #{metadata}"
|
61
|
+
yield
|
62
|
+
end
|
63
|
+
|
64
|
+
##
|
65
|
+
# Intercept a server streaming call
|
66
|
+
#
|
67
|
+
# @param [Object] request
|
68
|
+
# @param [GRPC::ActiveCall] call
|
69
|
+
# @param [String] method
|
70
|
+
# @param [Hash] metadata
|
71
|
+
#
|
72
|
+
def server_streamer(request: nil, call: nil, method: nil, metadata: nil)
|
73
|
+
GRPC.logger.debug "Intercepting server streamer method #{method}" \
|
74
|
+
" for request #{request} with call #{call} and metadata: #{metadata}"
|
75
|
+
yield
|
76
|
+
end
|
77
|
+
|
78
|
+
##
|
79
|
+
# Intercept a BiDi streaming call
|
80
|
+
#
|
81
|
+
# @param [Enumerable] requests
|
82
|
+
# @param [GRPC::ActiveCall] call
|
83
|
+
# @param [String] method
|
84
|
+
# @param [Hash] metadata
|
85
|
+
#
|
86
|
+
def bidi_streamer(requests: nil, call: nil, method: nil, metadata: nil)
|
87
|
+
GRPC.logger.debug "Intercepting bidi streamer method #{method}" \
|
88
|
+
" for requests #{requests} with call #{call} and metadata: #{metadata}"
|
89
|
+
yield
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
##
|
94
|
+
# ServerInterceptor allows for wrapping gRPC server execution handling.
|
95
|
+
# This is an EXPERIMENTAL API.
|
96
|
+
#
|
97
|
+
class ServerInterceptor < Interceptor
|
98
|
+
##
|
99
|
+
# Intercept a unary request response call.
|
100
|
+
#
|
101
|
+
# @param [Object] request
|
102
|
+
# @param [GRPC::ActiveCall::SingleReqView] call
|
103
|
+
# @param [Method] method
|
104
|
+
#
|
105
|
+
def request_response(request: nil, call: nil, method: nil)
|
106
|
+
GRPC.logger.debug "Intercepting request response method #{method}" \
|
107
|
+
" for request #{request} with call #{call}"
|
108
|
+
yield
|
109
|
+
end
|
110
|
+
|
111
|
+
##
|
112
|
+
# Intercept a client streaming call
|
113
|
+
#
|
114
|
+
# @param [GRPC::ActiveCall::MultiReqView] call
|
115
|
+
# @param [Method] method
|
116
|
+
#
|
117
|
+
def client_streamer(call: nil, method: nil)
|
118
|
+
GRPC.logger.debug "Intercepting client streamer method #{method}" \
|
119
|
+
" with call #{call}"
|
120
|
+
yield
|
121
|
+
end
|
122
|
+
|
123
|
+
##
|
124
|
+
# Intercept a server streaming call
|
125
|
+
#
|
126
|
+
# @param [Object] request
|
127
|
+
# @param [GRPC::ActiveCall::SingleReqView] call
|
128
|
+
# @param [Method] method
|
129
|
+
#
|
130
|
+
def server_streamer(request: nil, call: nil, method: nil)
|
131
|
+
GRPC.logger.debug "Intercepting server streamer method #{method}" \
|
132
|
+
" for request #{request} with call #{call}"
|
133
|
+
yield
|
134
|
+
end
|
135
|
+
|
136
|
+
##
|
137
|
+
# Intercept a BiDi streaming call
|
138
|
+
#
|
139
|
+
# @param [Enumerable<Object>] requests
|
140
|
+
# @param [GRPC::ActiveCall::MultiReqView] call
|
141
|
+
# @param [Method] method
|
142
|
+
#
|
143
|
+
def bidi_streamer(requests: nil, call: nil, method: nil)
|
144
|
+
GRPC.logger.debug "Intercepting bidi streamer method #{method}" \
|
145
|
+
" for requests #{requests} with call #{call}"
|
146
|
+
yield
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
##
|
151
|
+
# Represents the context in which an interceptor runs. Used to provide an
|
152
|
+
# injectable mechanism for handling interception. This is an EXPERIMENTAL API.
|
153
|
+
#
|
154
|
+
class InterceptionContext
|
155
|
+
##
|
156
|
+
# @param interceptors [Array<GRPC::Interceptor>]
|
157
|
+
#
|
158
|
+
def initialize(interceptors = [])
|
159
|
+
@interceptors = interceptors.dup
|
160
|
+
end
|
161
|
+
|
162
|
+
##
|
163
|
+
# Intercept the call and fire out to interceptors in a FIFO execution.
|
164
|
+
# This is an EXPERIMENTAL API.
|
165
|
+
#
|
166
|
+
# @param [Symbol] type The request type
|
167
|
+
# @param [Hash] args The arguments for the call
|
168
|
+
#
|
169
|
+
def intercept!(type, args = {})
|
170
|
+
return yield if @interceptors.none?
|
171
|
+
|
172
|
+
i = @interceptors.pop
|
173
|
+
return yield unless i
|
174
|
+
|
175
|
+
i.send(type, args) do
|
176
|
+
if @interceptors.any?
|
177
|
+
intercept!(type, args) do
|
178
|
+
yield
|
179
|
+
end
|
180
|
+
else
|
181
|
+
yield
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
@@ -0,0 +1,204 @@
|
|
1
|
+
# Copyright 2015 gRPC authors.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
require_relative '../grpc'
|
16
|
+
|
17
|
+
# GRPC contains the General RPC module.
|
18
|
+
module GRPC
|
19
|
+
# RpcDesc is a Descriptor of an RPC method.
|
20
|
+
class RpcDesc < Struct.new(:name, :input, :output, :marshal_method,
|
21
|
+
:unmarshal_method)
|
22
|
+
include Core::StatusCodes
|
23
|
+
|
24
|
+
# Used to wrap a message class to indicate that it needs to be streamed.
|
25
|
+
class Stream
|
26
|
+
attr_accessor :type
|
27
|
+
|
28
|
+
def initialize(type)
|
29
|
+
@type = type
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# @return [Proc] { |instance| marshalled(instance) }
|
34
|
+
def marshal_proc
|
35
|
+
proc { |o| o.class.send(marshal_method, o).to_s }
|
36
|
+
end
|
37
|
+
|
38
|
+
# @param [:input, :output] target determines whether to produce the an
|
39
|
+
# unmarshal Proc for the rpc input parameter or
|
40
|
+
# its output parameter
|
41
|
+
#
|
42
|
+
# @return [Proc] An unmarshal proc { |marshalled(instance)| instance }
|
43
|
+
def unmarshal_proc(target)
|
44
|
+
fail ArgumentError unless [:input, :output].include?(target)
|
45
|
+
unmarshal_class = send(target)
|
46
|
+
unmarshal_class = unmarshal_class.type if unmarshal_class.is_a? Stream
|
47
|
+
proc { |o| unmarshal_class.send(unmarshal_method, o) }
|
48
|
+
end
|
49
|
+
|
50
|
+
def handle_request_response(active_call, mth, inter_ctx)
|
51
|
+
req = active_call.read_unary_request
|
52
|
+
call = active_call.single_req_view
|
53
|
+
|
54
|
+
inter_ctx.intercept!(
|
55
|
+
:request_response,
|
56
|
+
method: mth,
|
57
|
+
call: call,
|
58
|
+
request: req
|
59
|
+
) do
|
60
|
+
resp = mth.call(req, call)
|
61
|
+
active_call.server_unary_response(
|
62
|
+
resp,
|
63
|
+
trailing_metadata: active_call.output_metadata
|
64
|
+
)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def handle_client_streamer(active_call, mth, inter_ctx)
|
69
|
+
call = active_call.multi_req_view
|
70
|
+
|
71
|
+
inter_ctx.intercept!(
|
72
|
+
:client_streamer,
|
73
|
+
method: mth,
|
74
|
+
call: call
|
75
|
+
) do
|
76
|
+
resp = mth.call(call)
|
77
|
+
active_call.server_unary_response(
|
78
|
+
resp,
|
79
|
+
trailing_metadata: active_call.output_metadata
|
80
|
+
)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def handle_server_streamer(active_call, mth, inter_ctx)
|
85
|
+
req = active_call.read_unary_request
|
86
|
+
call = active_call.single_req_view
|
87
|
+
|
88
|
+
inter_ctx.intercept!(
|
89
|
+
:server_streamer,
|
90
|
+
method: mth,
|
91
|
+
call: call,
|
92
|
+
request: req
|
93
|
+
) do
|
94
|
+
replies = mth.call(req, call)
|
95
|
+
replies.each { |r| active_call.remote_send(r) }
|
96
|
+
send_status(active_call, OK, 'OK', active_call.output_metadata)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
##
|
101
|
+
# @param [GRPC::ActiveCall] active_call
|
102
|
+
# @param [Method] mth
|
103
|
+
# @param [Array<GRPC::InterceptionContext>] inter_ctx
|
104
|
+
#
|
105
|
+
def handle_bidi_streamer(active_call, mth, inter_ctx)
|
106
|
+
active_call.run_server_bidi(mth, inter_ctx)
|
107
|
+
send_status(active_call, OK, 'OK', active_call.output_metadata)
|
108
|
+
end
|
109
|
+
|
110
|
+
##
|
111
|
+
# @param [GRPC::ActiveCall] active_call The current active call object
|
112
|
+
# for the request
|
113
|
+
# @param [Method] mth The current RPC method being called
|
114
|
+
# @param [GRPC::InterceptionContext] inter_ctx The interception context
|
115
|
+
# being executed
|
116
|
+
#
|
117
|
+
def run_server_method(active_call, mth, inter_ctx = InterceptionContext.new)
|
118
|
+
# While a server method is running, it might be cancelled, its deadline
|
119
|
+
# might be reached, the handler could throw an unknown error, or a
|
120
|
+
# well-behaved handler could throw a StatusError.
|
121
|
+
if request_response?
|
122
|
+
handle_request_response(active_call, mth, inter_ctx)
|
123
|
+
elsif client_streamer?
|
124
|
+
handle_client_streamer(active_call, mth, inter_ctx)
|
125
|
+
elsif server_streamer?
|
126
|
+
handle_server_streamer(active_call, mth, inter_ctx)
|
127
|
+
else # is a bidi_stream
|
128
|
+
handle_bidi_streamer(active_call, mth, inter_ctx)
|
129
|
+
end
|
130
|
+
rescue BadStatus => e
|
131
|
+
# this is raised by handlers that want GRPC to send an application error
|
132
|
+
# code and detail message and some additional app-specific metadata.
|
133
|
+
GRPC.logger.debug("app err:#{active_call}, status:#{e.code}:#{e.details}")
|
134
|
+
send_status(active_call, e.code, e.details, e.metadata)
|
135
|
+
rescue Core::CallError => e
|
136
|
+
# This is raised by GRPC internals but should rarely, if ever happen.
|
137
|
+
# Log it, but don't notify the other endpoint..
|
138
|
+
GRPC.logger.warn("failed call: #{active_call}\n#{e}")
|
139
|
+
rescue Core::OutOfTime
|
140
|
+
# This is raised when active_call#method.call exceeds the deadline
|
141
|
+
# event. Send a status of deadline exceeded
|
142
|
+
GRPC.logger.warn("late call: #{active_call}")
|
143
|
+
send_status(active_call, DEADLINE_EXCEEDED, 'late')
|
144
|
+
rescue StandardError, NotImplementedError => e
|
145
|
+
# This will usuaally be an unhandled error in the handling code.
|
146
|
+
# Send back a UNKNOWN status to the client
|
147
|
+
#
|
148
|
+
# Note: this intentionally does not map NotImplementedError to
|
149
|
+
# UNIMPLEMENTED because NotImplementedError is intended for low-level
|
150
|
+
# OS interaction (e.g. syscalls) not supported by the current OS.
|
151
|
+
GRPC.logger.warn("failed handler: #{active_call}; sending status:UNKNOWN")
|
152
|
+
GRPC.logger.warn(e)
|
153
|
+
send_status(active_call, UNKNOWN, "#{e.class}: #{e.message}")
|
154
|
+
end
|
155
|
+
|
156
|
+
def assert_arity_matches(mth)
|
157
|
+
# A bidi handler function can optionally be passed a second
|
158
|
+
# call object parameter for access to metadata, cancelling, etc.
|
159
|
+
if bidi_streamer?
|
160
|
+
if mth.arity != 2 && mth.arity != 1
|
161
|
+
fail arity_error(mth, 2, "should be #{mth.name}(req, call) or " \
|
162
|
+
"#{mth.name}(req)")
|
163
|
+
end
|
164
|
+
elsif request_response? || server_streamer?
|
165
|
+
if mth.arity != 2
|
166
|
+
fail arity_error(mth, 2, "should be #{mth.name}(req, call)")
|
167
|
+
end
|
168
|
+
else
|
169
|
+
if mth.arity != 1
|
170
|
+
fail arity_error(mth, 1, "should be #{mth.name}(call)")
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def request_response?
|
176
|
+
!input.is_a?(Stream) && !output.is_a?(Stream)
|
177
|
+
end
|
178
|
+
|
179
|
+
def client_streamer?
|
180
|
+
input.is_a?(Stream) && !output.is_a?(Stream)
|
181
|
+
end
|
182
|
+
|
183
|
+
def server_streamer?
|
184
|
+
!input.is_a?(Stream) && output.is_a?(Stream)
|
185
|
+
end
|
186
|
+
|
187
|
+
def bidi_streamer?
|
188
|
+
input.is_a?(Stream) && output.is_a?(Stream)
|
189
|
+
end
|
190
|
+
|
191
|
+
def arity_error(mth, want, msg)
|
192
|
+
"##{mth.name}: bad arg count; got:#{mth.arity}, want:#{want}, #{msg}"
|
193
|
+
end
|
194
|
+
|
195
|
+
def send_status(active_client, code, details, metadata = {})
|
196
|
+
details = 'Not sure why' if details.nil?
|
197
|
+
GRPC.logger.debug("Sending status #{code}:#{details}")
|
198
|
+
active_client.send_status(code, details, code == OK, metadata: metadata)
|
199
|
+
rescue StandardError => e
|
200
|
+
GRPC.logger.warn("Could not send status #{code}:#{details}")
|
201
|
+
GRPC.logger.warn(e)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
@@ -0,0 +1,551 @@
|
|
1
|
+
# Copyright 2015 gRPC authors.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
require_relative '../grpc'
|
16
|
+
require_relative 'active_call'
|
17
|
+
require_relative 'service'
|
18
|
+
require 'thread'
|
19
|
+
|
20
|
+
# GRPC contains the General RPC module.
|
21
|
+
module GRPC
|
22
|
+
# Pool is a simple thread pool.
|
23
|
+
class Pool
|
24
|
+
# Default keep alive period is 1s
|
25
|
+
DEFAULT_KEEP_ALIVE = 1
|
26
|
+
|
27
|
+
def initialize(size, keep_alive: DEFAULT_KEEP_ALIVE)
|
28
|
+
fail 'pool size must be positive' unless size > 0
|
29
|
+
@jobs = Queue.new
|
30
|
+
@size = size
|
31
|
+
@stopped = false
|
32
|
+
@stop_mutex = Mutex.new # needs to be held when accessing @stopped
|
33
|
+
@stop_cond = ConditionVariable.new
|
34
|
+
@workers = []
|
35
|
+
@keep_alive = keep_alive
|
36
|
+
|
37
|
+
# Each worker thread has its own queue to push and pull jobs
|
38
|
+
# these queues are put into @ready_queues when that worker is idle
|
39
|
+
@ready_workers = Queue.new
|
40
|
+
end
|
41
|
+
|
42
|
+
# Returns the number of jobs waiting
|
43
|
+
def jobs_waiting
|
44
|
+
@jobs.size
|
45
|
+
end
|
46
|
+
|
47
|
+
def ready_for_work?
|
48
|
+
# Busy worker threads are either doing work, or have a single job
|
49
|
+
# waiting on them. Workers that are idle with no jobs waiting
|
50
|
+
# have their "queues" in @ready_workers
|
51
|
+
!@ready_workers.empty?
|
52
|
+
end
|
53
|
+
|
54
|
+
# Runs the given block on the queue with the provided args.
|
55
|
+
#
|
56
|
+
# @param args the args passed blk when it is called
|
57
|
+
# @param blk the block to call
|
58
|
+
def schedule(*args, &blk)
|
59
|
+
return if blk.nil?
|
60
|
+
@stop_mutex.synchronize do
|
61
|
+
if @stopped
|
62
|
+
GRPC.logger.warn('did not schedule job, already stopped')
|
63
|
+
return
|
64
|
+
end
|
65
|
+
GRPC.logger.info('schedule another job')
|
66
|
+
fail 'No worker threads available' if @ready_workers.empty?
|
67
|
+
worker_queue = @ready_workers.pop
|
68
|
+
|
69
|
+
fail 'worker already has a task waiting' unless worker_queue.empty?
|
70
|
+
worker_queue << [blk, args]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Starts running the jobs in the thread pool.
|
75
|
+
def start
|
76
|
+
@stop_mutex.synchronize do
|
77
|
+
fail 'already stopped' if @stopped
|
78
|
+
end
|
79
|
+
until @workers.size == @size.to_i
|
80
|
+
new_worker_queue = Queue.new
|
81
|
+
@ready_workers << new_worker_queue
|
82
|
+
next_thread = Thread.new(new_worker_queue) do |jobs|
|
83
|
+
catch(:exit) do # allows { throw :exit } to kill a thread
|
84
|
+
loop_execute_jobs(jobs)
|
85
|
+
end
|
86
|
+
remove_current_thread
|
87
|
+
end
|
88
|
+
@workers << next_thread
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Stops the jobs in the pool
|
93
|
+
def stop
|
94
|
+
GRPC.logger.info('stopping, will wait for all the workers to exit')
|
95
|
+
@stop_mutex.synchronize do # wait @keep_alive seconds for workers to stop
|
96
|
+
@stopped = true
|
97
|
+
loop do
|
98
|
+
break unless ready_for_work?
|
99
|
+
worker_queue = @ready_workers.pop
|
100
|
+
worker_queue << [proc { throw :exit }, []]
|
101
|
+
end
|
102
|
+
@stop_cond.wait(@stop_mutex, @keep_alive) if @workers.size > 0
|
103
|
+
end
|
104
|
+
forcibly_stop_workers
|
105
|
+
GRPC.logger.info('stopped, all workers are shutdown')
|
106
|
+
end
|
107
|
+
|
108
|
+
protected
|
109
|
+
|
110
|
+
# Forcibly shutdown any threads that are still alive.
|
111
|
+
def forcibly_stop_workers
|
112
|
+
return unless @workers.size > 0
|
113
|
+
GRPC.logger.info("forcibly terminating #{@workers.size} worker(s)")
|
114
|
+
@workers.each do |t|
|
115
|
+
next unless t.alive?
|
116
|
+
begin
|
117
|
+
t.exit
|
118
|
+
rescue StandardError => e
|
119
|
+
GRPC.logger.warn('error while terminating a worker')
|
120
|
+
GRPC.logger.warn(e)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# removes the threads from workers, and signal when all the
|
126
|
+
# threads are complete.
|
127
|
+
def remove_current_thread
|
128
|
+
@stop_mutex.synchronize do
|
129
|
+
@workers.delete(Thread.current)
|
130
|
+
@stop_cond.signal if @workers.size.zero?
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def loop_execute_jobs(worker_queue)
|
135
|
+
loop do
|
136
|
+
begin
|
137
|
+
blk, args = worker_queue.pop
|
138
|
+
blk.call(*args)
|
139
|
+
rescue StandardError, GRPC::Core::CallError => e
|
140
|
+
GRPC.logger.warn('Error in worker thread')
|
141
|
+
GRPC.logger.warn(e)
|
142
|
+
end
|
143
|
+
# there shouldn't be any work given to this thread while its busy
|
144
|
+
fail('received a task while busy') unless worker_queue.empty?
|
145
|
+
@stop_mutex.synchronize do
|
146
|
+
return if @stopped
|
147
|
+
@ready_workers << worker_queue
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
# RpcServer hosts a number of services and makes them available on the
|
154
|
+
# network.
|
155
|
+
class RpcServer
|
156
|
+
include Core::CallOps
|
157
|
+
include Core::TimeConsts
|
158
|
+
extend ::Forwardable
|
159
|
+
|
160
|
+
def_delegators :@server, :add_http2_port
|
161
|
+
|
162
|
+
# Default thread pool size is 30
|
163
|
+
DEFAULT_POOL_SIZE = 30
|
164
|
+
|
165
|
+
# Deprecated due to internal changes to the thread pool
|
166
|
+
DEFAULT_MAX_WAITING_REQUESTS = 20
|
167
|
+
|
168
|
+
# Default poll period is 1s
|
169
|
+
DEFAULT_POLL_PERIOD = 1
|
170
|
+
|
171
|
+
# Signal check period is 0.25s
|
172
|
+
SIGNAL_CHECK_PERIOD = 0.25
|
173
|
+
|
174
|
+
# setup_connect_md_proc is used by #initialize to validate the
|
175
|
+
# connect_md_proc.
|
176
|
+
def self.setup_connect_md_proc(a_proc)
|
177
|
+
return nil if a_proc.nil?
|
178
|
+
fail(TypeError, '!Proc') unless a_proc.is_a? Proc
|
179
|
+
a_proc
|
180
|
+
end
|
181
|
+
|
182
|
+
# Creates a new RpcServer.
|
183
|
+
#
|
184
|
+
# The RPC server is configured using keyword arguments.
|
185
|
+
#
|
186
|
+
# There are some specific keyword args used to configure the RpcServer
|
187
|
+
# instance.
|
188
|
+
#
|
189
|
+
# * pool_size: the size of the thread pool the server uses to run its
|
190
|
+
# threads. No more concurrent requests can be made than the size
|
191
|
+
# of the thread pool
|
192
|
+
#
|
193
|
+
# * max_waiting_requests: Deprecated due to internal changes to the thread
|
194
|
+
# pool. This is still an argument for compatibility but is ignored.
|
195
|
+
#
|
196
|
+
# * poll_period: The amount of time in seconds to wait for
|
197
|
+
# currently-serviced RPC's to finish before cancelling them when shutting
|
198
|
+
# down the server.
|
199
|
+
#
|
200
|
+
# * pool_keep_alive: The amount of time in seconds to wait
|
201
|
+
# for currently busy thread-pool threads to finish before
|
202
|
+
# forcing an abrupt exit to each thread.
|
203
|
+
#
|
204
|
+
# * connect_md_proc:
|
205
|
+
# when non-nil is a proc for determining metadata to send back the client
|
206
|
+
# on receiving an invocation req. The proc signature is:
|
207
|
+
# {key: val, ..} func(method_name, {key: val, ...})
|
208
|
+
#
|
209
|
+
# * server_args:
|
210
|
+
# A server arguments hash to be passed down to the underlying core server
|
211
|
+
#
|
212
|
+
# * interceptors:
|
213
|
+
# An array of GRPC::ServerInterceptor objects that will be used for
|
214
|
+
# intercepting server handlers to provide extra functionality.
|
215
|
+
# Interceptors are an EXPERIMENTAL API.
|
216
|
+
#
|
217
|
+
def initialize(pool_size: DEFAULT_POOL_SIZE,
|
218
|
+
max_waiting_requests: DEFAULT_MAX_WAITING_REQUESTS,
|
219
|
+
poll_period: DEFAULT_POLL_PERIOD,
|
220
|
+
pool_keep_alive: Pool::DEFAULT_KEEP_ALIVE,
|
221
|
+
connect_md_proc: nil,
|
222
|
+
server_args: {},
|
223
|
+
interceptors: [])
|
224
|
+
@connect_md_proc = RpcServer.setup_connect_md_proc(connect_md_proc)
|
225
|
+
@max_waiting_requests = max_waiting_requests
|
226
|
+
@poll_period = poll_period
|
227
|
+
@pool_size = pool_size
|
228
|
+
@pool = Pool.new(@pool_size, keep_alive: pool_keep_alive)
|
229
|
+
@run_cond = ConditionVariable.new
|
230
|
+
@run_mutex = Mutex.new
|
231
|
+
# running_state can take 4 values: :not_started, :running, :stopping, and
|
232
|
+
# :stopped. State transitions can only proceed in that order.
|
233
|
+
@running_state = :not_started
|
234
|
+
@server = Core::Server.new(server_args)
|
235
|
+
@interceptors = InterceptorRegistry.new(interceptors)
|
236
|
+
end
|
237
|
+
|
238
|
+
# stops a running server
|
239
|
+
#
|
240
|
+
# the call has no impact if the server is already stopped, otherwise
|
241
|
+
# server's current call loop is it's last.
|
242
|
+
def stop
|
243
|
+
# if called via run_till_terminated_or_interrupted,
|
244
|
+
# signal stop_server_thread and don't do anything
|
245
|
+
if @stop_server.nil? == false && @stop_server == false
|
246
|
+
@stop_server = true
|
247
|
+
@stop_server_cv.broadcast
|
248
|
+
return
|
249
|
+
end
|
250
|
+
@run_mutex.synchronize do
|
251
|
+
fail 'Cannot stop before starting' if @running_state == :not_started
|
252
|
+
return if @running_state != :running
|
253
|
+
transition_running_state(:stopping)
|
254
|
+
deadline = from_relative_time(@poll_period)
|
255
|
+
@server.shutdown_and_notify(deadline)
|
256
|
+
end
|
257
|
+
@pool.stop
|
258
|
+
end
|
259
|
+
|
260
|
+
def running_state
|
261
|
+
@run_mutex.synchronize do
|
262
|
+
return @running_state
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
# Can only be called while holding @run_mutex
|
267
|
+
def transition_running_state(target_state)
|
268
|
+
state_transitions = {
|
269
|
+
not_started: :running,
|
270
|
+
running: :stopping,
|
271
|
+
stopping: :stopped
|
272
|
+
}
|
273
|
+
if state_transitions[@running_state] == target_state
|
274
|
+
@running_state = target_state
|
275
|
+
else
|
276
|
+
fail "Bad server state transition: #{@running_state}->#{target_state}"
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
def running?
|
281
|
+
running_state == :running
|
282
|
+
end
|
283
|
+
|
284
|
+
def stopped?
|
285
|
+
running_state == :stopped
|
286
|
+
end
|
287
|
+
|
288
|
+
# Is called from other threads to wait for #run to start up the server.
|
289
|
+
#
|
290
|
+
# If run has not been called, this returns immediately.
|
291
|
+
#
|
292
|
+
# @param timeout [Numeric] number of seconds to wait
|
293
|
+
# @return [true, false] true if the server is running, false otherwise
|
294
|
+
def wait_till_running(timeout = nil)
|
295
|
+
@run_mutex.synchronize do
|
296
|
+
@run_cond.wait(@run_mutex, timeout) if @running_state == :not_started
|
297
|
+
return @running_state == :running
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
# handle registration of classes
|
302
|
+
#
|
303
|
+
# service is either a class that includes GRPC::GenericService and whose
|
304
|
+
# #new function can be called without argument or any instance of such a
|
305
|
+
# class.
|
306
|
+
#
|
307
|
+
# E.g, after
|
308
|
+
#
|
309
|
+
# class Divider
|
310
|
+
# include GRPC::GenericService
|
311
|
+
# rpc :div DivArgs, DivReply # single request, single response
|
312
|
+
# def initialize(optional_arg='default option') # no args
|
313
|
+
# ...
|
314
|
+
# end
|
315
|
+
#
|
316
|
+
# srv = GRPC::RpcServer.new(...)
|
317
|
+
#
|
318
|
+
# # Either of these works
|
319
|
+
#
|
320
|
+
# srv.handle(Divider)
|
321
|
+
#
|
322
|
+
# # or
|
323
|
+
#
|
324
|
+
# srv.handle(Divider.new('replace optional arg'))
|
325
|
+
#
|
326
|
+
# It raises RuntimeError:
|
327
|
+
# - if service is not valid service class or object
|
328
|
+
# - its handler methods are already registered
|
329
|
+
# - if the server is already running
|
330
|
+
#
|
331
|
+
# @param service [Object|Class] a service class or object as described
|
332
|
+
# above
|
333
|
+
def handle(service)
|
334
|
+
@run_mutex.synchronize do
|
335
|
+
unless @running_state == :not_started
|
336
|
+
fail 'cannot add services if the server has been started'
|
337
|
+
end
|
338
|
+
cls = service.is_a?(Class) ? service : service.class
|
339
|
+
assert_valid_service_class(cls)
|
340
|
+
add_rpc_descs_for(service)
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
# runs the server
|
345
|
+
#
|
346
|
+
# - if no rpc_descs are registered, this exits immediately, otherwise it
|
347
|
+
# continues running permanently and does not return until program exit.
|
348
|
+
#
|
349
|
+
# - #running? returns true after this is called, until #stop cause the
|
350
|
+
# the server to stop.
|
351
|
+
def run
|
352
|
+
@run_mutex.synchronize do
|
353
|
+
fail 'cannot run without registering services' if rpc_descs.size.zero?
|
354
|
+
@pool.start
|
355
|
+
@server.start
|
356
|
+
transition_running_state(:running)
|
357
|
+
@run_cond.broadcast
|
358
|
+
end
|
359
|
+
loop_handle_server_calls
|
360
|
+
end
|
361
|
+
|
362
|
+
alias_method :run_till_terminated, :run
|
363
|
+
|
364
|
+
# runs the server with signal handlers
|
365
|
+
# @param signals
|
366
|
+
# List of String, Integer or both representing signals that the user
|
367
|
+
# would like to send to the server for graceful shutdown
|
368
|
+
# @param wait_interval (optional)
|
369
|
+
# Integer seconds that user would like stop_server_thread to poll
|
370
|
+
# stop_server
|
371
|
+
def run_till_terminated_or_interrupted(signals, wait_interval = 60)
|
372
|
+
@stop_server = false
|
373
|
+
@stop_server_mu = Mutex.new
|
374
|
+
@stop_server_cv = ConditionVariable.new
|
375
|
+
|
376
|
+
@stop_server_thread = Thread.new do
|
377
|
+
loop do
|
378
|
+
break if @stop_server
|
379
|
+
@stop_server_mu.synchronize do
|
380
|
+
@stop_server_cv.wait(@stop_server_mu, wait_interval)
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
# stop is surrounded by mutex, should handle multiple calls to stop
|
385
|
+
# correctly
|
386
|
+
stop
|
387
|
+
end
|
388
|
+
|
389
|
+
valid_signals = Signal.list
|
390
|
+
|
391
|
+
# register signal handlers
|
392
|
+
signals.each do |sig|
|
393
|
+
# input validation
|
394
|
+
target_sig = if sig.class == String
|
395
|
+
# cut out the SIG prefix to see if valid signal
|
396
|
+
sig.upcase.start_with?('SIG') ? sig.upcase[3..-1] : sig.upcase
|
397
|
+
else
|
398
|
+
sig
|
399
|
+
end
|
400
|
+
|
401
|
+
# register signal traps for all valid signals
|
402
|
+
if valid_signals.value?(target_sig) || valid_signals.key?(target_sig)
|
403
|
+
Signal.trap(target_sig) do
|
404
|
+
@stop_server = true
|
405
|
+
@stop_server_cv.broadcast
|
406
|
+
end
|
407
|
+
else
|
408
|
+
fail "#{target_sig} not a valid signal"
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
412
|
+
run
|
413
|
+
|
414
|
+
@stop_server_thread.join
|
415
|
+
end
|
416
|
+
|
417
|
+
# Sends RESOURCE_EXHAUSTED if there are too many unprocessed jobs
|
418
|
+
def available?(an_rpc)
|
419
|
+
return an_rpc if @pool.ready_for_work?
|
420
|
+
GRPC.logger.warn('no free worker threads currently')
|
421
|
+
noop = proc { |x| x }
|
422
|
+
|
423
|
+
# Create a new active call that knows that metadata hasn't been
|
424
|
+
# sent yet
|
425
|
+
c = ActiveCall.new(an_rpc.call, noop, noop, an_rpc.deadline,
|
426
|
+
metadata_received: true, started: false)
|
427
|
+
c.send_status(GRPC::Core::StatusCodes::RESOURCE_EXHAUSTED,
|
428
|
+
'No free threads in thread pool')
|
429
|
+
nil
|
430
|
+
end
|
431
|
+
|
432
|
+
# Sends UNIMPLEMENTED if the method is not implemented by this server
|
433
|
+
def implemented?(an_rpc)
|
434
|
+
mth = an_rpc.method.to_sym
|
435
|
+
return an_rpc if rpc_descs.key?(mth)
|
436
|
+
GRPC.logger.warn("UNIMPLEMENTED: #{an_rpc}")
|
437
|
+
noop = proc { |x| x }
|
438
|
+
|
439
|
+
# Create a new active call that knows that
|
440
|
+
# metadata hasn't been sent yet
|
441
|
+
c = ActiveCall.new(an_rpc.call, noop, noop, an_rpc.deadline,
|
442
|
+
metadata_received: true, started: false)
|
443
|
+
c.send_status(GRPC::Core::StatusCodes::UNIMPLEMENTED, '')
|
444
|
+
nil
|
445
|
+
end
|
446
|
+
|
447
|
+
# handles calls to the server
|
448
|
+
def loop_handle_server_calls
|
449
|
+
fail 'not started' if running_state == :not_started
|
450
|
+
while running_state == :running
|
451
|
+
begin
|
452
|
+
an_rpc = @server.request_call
|
453
|
+
break if (!an_rpc.nil?) && an_rpc.call.nil?
|
454
|
+
active_call = new_active_server_call(an_rpc)
|
455
|
+
unless active_call.nil?
|
456
|
+
@pool.schedule(active_call) do |ac|
|
457
|
+
c, mth = ac
|
458
|
+
begin
|
459
|
+
rpc_descs[mth].run_server_method(
|
460
|
+
c,
|
461
|
+
rpc_handlers[mth],
|
462
|
+
@interceptors.build_context
|
463
|
+
)
|
464
|
+
rescue StandardError
|
465
|
+
c.send_status(GRPC::Core::StatusCodes::INTERNAL,
|
466
|
+
'Server handler failed')
|
467
|
+
end
|
468
|
+
end
|
469
|
+
end
|
470
|
+
rescue Core::CallError, RuntimeError => e
|
471
|
+
# these might happen for various reasons. The correct behavior of
|
472
|
+
# the server is to log them and continue, if it's not shutting down.
|
473
|
+
if running_state == :running
|
474
|
+
GRPC.logger.warn("server call failed: #{e}")
|
475
|
+
end
|
476
|
+
next
|
477
|
+
end
|
478
|
+
end
|
479
|
+
# @running_state should be :stopping here
|
480
|
+
@run_mutex.synchronize do
|
481
|
+
transition_running_state(:stopped)
|
482
|
+
GRPC.logger.info("stopped: #{self}")
|
483
|
+
@server.close
|
484
|
+
end
|
485
|
+
end
|
486
|
+
|
487
|
+
def new_active_server_call(an_rpc)
|
488
|
+
return nil if an_rpc.nil? || an_rpc.call.nil?
|
489
|
+
|
490
|
+
# allow the metadata to be accessed from the call
|
491
|
+
an_rpc.call.metadata = an_rpc.metadata # attaches md to call for handlers
|
492
|
+
connect_md = nil
|
493
|
+
unless @connect_md_proc.nil?
|
494
|
+
connect_md = @connect_md_proc.call(an_rpc.method, an_rpc.metadata)
|
495
|
+
end
|
496
|
+
|
497
|
+
return nil unless available?(an_rpc)
|
498
|
+
return nil unless implemented?(an_rpc)
|
499
|
+
|
500
|
+
# Create the ActiveCall. Indicate that metadata hasnt been sent yet.
|
501
|
+
GRPC.logger.info("deadline is #{an_rpc.deadline}; (now=#{Time.now})")
|
502
|
+
rpc_desc = rpc_descs[an_rpc.method.to_sym]
|
503
|
+
c = ActiveCall.new(an_rpc.call,
|
504
|
+
rpc_desc.marshal_proc,
|
505
|
+
rpc_desc.unmarshal_proc(:input),
|
506
|
+
an_rpc.deadline,
|
507
|
+
metadata_received: true,
|
508
|
+
started: false,
|
509
|
+
metadata_to_send: connect_md)
|
510
|
+
c.attach_peer_cert(an_rpc.call.peer_cert)
|
511
|
+
mth = an_rpc.method.to_sym
|
512
|
+
[c, mth]
|
513
|
+
end
|
514
|
+
|
515
|
+
protected
|
516
|
+
|
517
|
+
def rpc_descs
|
518
|
+
@rpc_descs ||= {}
|
519
|
+
end
|
520
|
+
|
521
|
+
def rpc_handlers
|
522
|
+
@rpc_handlers ||= {}
|
523
|
+
end
|
524
|
+
|
525
|
+
def assert_valid_service_class(cls)
|
526
|
+
unless cls.include?(GenericService)
|
527
|
+
fail "#{cls} must 'include GenericService'"
|
528
|
+
end
|
529
|
+
fail "#{cls} should specify some rpc descriptions" if
|
530
|
+
cls.rpc_descs.size.zero?
|
531
|
+
end
|
532
|
+
|
533
|
+
# This should be called while holding @run_mutex
|
534
|
+
def add_rpc_descs_for(service)
|
535
|
+
cls = service.is_a?(Class) ? service : service.class
|
536
|
+
specs, handlers = (@rpc_descs ||= {}), (@rpc_handlers ||= {})
|
537
|
+
cls.rpc_descs.each_pair do |name, spec|
|
538
|
+
route = "/#{cls.service_name}/#{name}".to_sym
|
539
|
+
fail "already registered: rpc #{route} from #{spec}" if specs.key? route
|
540
|
+
specs[route] = spec
|
541
|
+
rpc_name = GenericService.underscore(name.to_s).to_sym
|
542
|
+
if service.is_a?(Class)
|
543
|
+
handlers[route] = cls.new.method(rpc_name)
|
544
|
+
else
|
545
|
+
handlers[route] = service.method(rpc_name)
|
546
|
+
end
|
547
|
+
GRPC.logger.info("handling #{route} with #{handlers[route]}")
|
548
|
+
end
|
549
|
+
end
|
550
|
+
end
|
551
|
+
end
|