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.

Files changed (121) hide show
  1. checksums.yaml +7 -0
  2. data/etc/roots.pem +4644 -0
  3. data/grpc_c.32.ruby +0 -0
  4. data/grpc_c.64.ruby +0 -0
  5. data/src/ruby/bin/math_client.rb +140 -0
  6. data/src/ruby/bin/math_pb.rb +34 -0
  7. data/src/ruby/bin/math_server.rb +191 -0
  8. data/src/ruby/bin/math_services_pb.rb +51 -0
  9. data/src/ruby/bin/noproto_client.rb +93 -0
  10. data/src/ruby/bin/noproto_server.rb +97 -0
  11. data/src/ruby/ext/grpc/ext-export.clang +1 -0
  12. data/src/ruby/ext/grpc/ext-export.gcc +6 -0
  13. data/src/ruby/ext/grpc/extconf.rb +107 -0
  14. data/src/ruby/ext/grpc/rb_byte_buffer.c +64 -0
  15. data/src/ruby/ext/grpc/rb_byte_buffer.h +35 -0
  16. data/src/ruby/ext/grpc/rb_call.c +1050 -0
  17. data/src/ruby/ext/grpc/rb_call.h +53 -0
  18. data/src/ruby/ext/grpc/rb_call_credentials.c +297 -0
  19. data/src/ruby/ext/grpc/rb_call_credentials.h +31 -0
  20. data/src/ruby/ext/grpc/rb_channel.c +835 -0
  21. data/src/ruby/ext/grpc/rb_channel.h +34 -0
  22. data/src/ruby/ext/grpc/rb_channel_args.c +155 -0
  23. data/src/ruby/ext/grpc/rb_channel_args.h +38 -0
  24. data/src/ruby/ext/grpc/rb_channel_credentials.c +267 -0
  25. data/src/ruby/ext/grpc/rb_channel_credentials.h +32 -0
  26. data/src/ruby/ext/grpc/rb_completion_queue.c +100 -0
  27. data/src/ruby/ext/grpc/rb_completion_queue.h +36 -0
  28. data/src/ruby/ext/grpc/rb_compression_options.c +470 -0
  29. data/src/ruby/ext/grpc/rb_compression_options.h +29 -0
  30. data/src/ruby/ext/grpc/rb_enable_cpp.cc +22 -0
  31. data/src/ruby/ext/grpc/rb_event_thread.c +143 -0
  32. data/src/ruby/ext/grpc/rb_event_thread.h +21 -0
  33. data/src/ruby/ext/grpc/rb_grpc.c +328 -0
  34. data/src/ruby/ext/grpc/rb_grpc.h +76 -0
  35. data/src/ruby/ext/grpc/rb_grpc_imports.generated.c +573 -0
  36. data/src/ruby/ext/grpc/rb_grpc_imports.generated.h +865 -0
  37. data/src/ruby/ext/grpc/rb_loader.c +57 -0
  38. data/src/ruby/ext/grpc/rb_loader.h +25 -0
  39. data/src/ruby/ext/grpc/rb_server.c +372 -0
  40. data/src/ruby/ext/grpc/rb_server.h +32 -0
  41. data/src/ruby/ext/grpc/rb_server_credentials.c +243 -0
  42. data/src/ruby/ext/grpc/rb_server_credentials.h +32 -0
  43. data/src/ruby/lib/grpc.rb +37 -0
  44. data/src/ruby/lib/grpc/2.3/grpc_c.so +0 -0
  45. data/src/ruby/lib/grpc/2.4/grpc_c.so +0 -0
  46. data/src/ruby/lib/grpc/2.5/grpc_c.so +0 -0
  47. data/src/ruby/lib/grpc/2.6/grpc_c.so +0 -0
  48. data/src/ruby/lib/grpc/2.7/grpc_c.so +0 -0
  49. data/src/ruby/lib/grpc/core/status_codes.rb +135 -0
  50. data/src/ruby/lib/grpc/core/time_consts.rb +56 -0
  51. data/src/ruby/lib/grpc/errors.rb +277 -0
  52. data/src/ruby/lib/grpc/generic/active_call.rb +669 -0
  53. data/src/ruby/lib/grpc/generic/bidi_call.rb +233 -0
  54. data/src/ruby/lib/grpc/generic/client_stub.rb +501 -0
  55. data/src/ruby/lib/grpc/generic/interceptor_registry.rb +53 -0
  56. data/src/ruby/lib/grpc/generic/interceptors.rb +186 -0
  57. data/src/ruby/lib/grpc/generic/rpc_desc.rb +204 -0
  58. data/src/ruby/lib/grpc/generic/rpc_server.rb +551 -0
  59. data/src/ruby/lib/grpc/generic/service.rb +211 -0
  60. data/src/ruby/lib/grpc/google_rpc_status_utils.rb +40 -0
  61. data/src/ruby/lib/grpc/grpc.rb +24 -0
  62. data/src/ruby/lib/grpc/logconfig.rb +44 -0
  63. data/src/ruby/lib/grpc/notifier.rb +45 -0
  64. data/src/ruby/lib/grpc/structs.rb +15 -0
  65. data/src/ruby/lib/grpc/version.rb +18 -0
  66. data/src/ruby/pb/README.md +42 -0
  67. data/src/ruby/pb/generate_proto_ruby.sh +51 -0
  68. data/src/ruby/pb/grpc/health/checker.rb +75 -0
  69. data/src/ruby/pb/grpc/health/v1/health_pb.rb +31 -0
  70. data/src/ruby/pb/grpc/health/v1/health_services_pb.rb +62 -0
  71. data/src/ruby/pb/grpc/testing/duplicate/echo_duplicate_services_pb.rb +44 -0
  72. data/src/ruby/pb/grpc/testing/metrics_pb.rb +28 -0
  73. data/src/ruby/pb/grpc/testing/metrics_services_pb.rb +49 -0
  74. data/src/ruby/pb/src/proto/grpc/testing/empty_pb.rb +17 -0
  75. data/src/ruby/pb/src/proto/grpc/testing/messages_pb.rb +105 -0
  76. data/src/ruby/pb/src/proto/grpc/testing/test_pb.rb +16 -0
  77. data/src/ruby/pb/src/proto/grpc/testing/test_services_pb.rb +118 -0
  78. data/src/ruby/pb/test/client.rb +769 -0
  79. data/src/ruby/pb/test/server.rb +252 -0
  80. data/src/ruby/pb/test/xds_client.rb +213 -0
  81. data/src/ruby/spec/call_credentials_spec.rb +42 -0
  82. data/src/ruby/spec/call_spec.rb +180 -0
  83. data/src/ruby/spec/channel_connection_spec.rb +126 -0
  84. data/src/ruby/spec/channel_credentials_spec.rb +82 -0
  85. data/src/ruby/spec/channel_spec.rb +234 -0
  86. data/src/ruby/spec/client_auth_spec.rb +126 -0
  87. data/src/ruby/spec/client_server_spec.rb +664 -0
  88. data/src/ruby/spec/compression_options_spec.rb +149 -0
  89. data/src/ruby/spec/debug_message_spec.rb +134 -0
  90. data/src/ruby/spec/error_sanity_spec.rb +49 -0
  91. data/src/ruby/spec/errors_spec.rb +142 -0
  92. data/src/ruby/spec/generic/active_call_spec.rb +672 -0
  93. data/src/ruby/spec/generic/client_interceptors_spec.rb +153 -0
  94. data/src/ruby/spec/generic/client_stub_spec.rb +1083 -0
  95. data/src/ruby/spec/generic/interceptor_registry_spec.rb +65 -0
  96. data/src/ruby/spec/generic/rpc_desc_spec.rb +374 -0
  97. data/src/ruby/spec/generic/rpc_server_pool_spec.rb +127 -0
  98. data/src/ruby/spec/generic/rpc_server_spec.rb +748 -0
  99. data/src/ruby/spec/generic/server_interceptors_spec.rb +218 -0
  100. data/src/ruby/spec/generic/service_spec.rb +263 -0
  101. data/src/ruby/spec/google_rpc_status_utils_spec.rb +282 -0
  102. data/src/ruby/spec/pb/codegen/grpc/testing/package_options.proto +28 -0
  103. data/src/ruby/spec/pb/codegen/grpc/testing/package_options_import.proto +22 -0
  104. data/src/ruby/spec/pb/codegen/grpc/testing/package_options_import2.proto +23 -0
  105. data/src/ruby/spec/pb/codegen/grpc/testing/package_options_ruby_style.proto +41 -0
  106. data/src/ruby/spec/pb/codegen/package_option_spec.rb +82 -0
  107. data/src/ruby/spec/pb/duplicate/codegen_spec.rb +57 -0
  108. data/src/ruby/spec/pb/health/checker_spec.rb +236 -0
  109. data/src/ruby/spec/server_credentials_spec.rb +79 -0
  110. data/src/ruby/spec/server_spec.rb +209 -0
  111. data/src/ruby/spec/spec_helper.rb +61 -0
  112. data/src/ruby/spec/support/helpers.rb +107 -0
  113. data/src/ruby/spec/support/services.rb +160 -0
  114. data/src/ruby/spec/testdata/README +1 -0
  115. data/src/ruby/spec/testdata/ca.pem +20 -0
  116. data/src/ruby/spec/testdata/client.key +28 -0
  117. data/src/ruby/spec/testdata/client.pem +20 -0
  118. data/src/ruby/spec/testdata/server1.key +28 -0
  119. data/src/ruby/spec/testdata/server1.pem +22 -0
  120. data/src/ruby/spec/time_consts_spec.rb +74 -0
  121. 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