grpc 1.42.0.pre1-arm64-darwin

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 (128) hide show
  1. checksums.yaml +7 -0
  2. data/etc/roots.pem +4337 -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 +123 -0
  14. data/src/ruby/ext/grpc/rb_byte_buffer.c +65 -0
  15. data/src/ruby/ext/grpc/rb_byte_buffer.h +35 -0
  16. data/src/ruby/ext/grpc/rb_call.c +1051 -0
  17. data/src/ruby/ext/grpc/rb_call.h +57 -0
  18. data/src/ruby/ext/grpc/rb_call_credentials.c +341 -0
  19. data/src/ruby/ext/grpc/rb_call_credentials.h +31 -0
  20. data/src/ruby/ext/grpc/rb_channel.c +846 -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 +286 -0
  25. data/src/ruby/ext/grpc/rb_channel_credentials.h +37 -0
  26. data/src/ruby/ext/grpc/rb_completion_queue.c +101 -0
  27. data/src/ruby/ext/grpc/rb_completion_queue.h +36 -0
  28. data/src/ruby/ext/grpc/rb_compression_options.c +471 -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 +145 -0
  32. data/src/ruby/ext/grpc/rb_event_thread.h +21 -0
  33. data/src/ruby/ext/grpc/rb_grpc.c +333 -0
  34. data/src/ruby/ext/grpc/rb_grpc.h +77 -0
  35. data/src/ruby/ext/grpc/rb_grpc_imports.generated.c +605 -0
  36. data/src/ruby/ext/grpc/rb_grpc_imports.generated.h +913 -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 +385 -0
  40. data/src/ruby/ext/grpc/rb_server.h +32 -0
  41. data/src/ruby/ext/grpc/rb_server_credentials.c +259 -0
  42. data/src/ruby/ext/grpc/rb_server_credentials.h +37 -0
  43. data/src/ruby/ext/grpc/rb_xds_channel_credentials.c +218 -0
  44. data/src/ruby/ext/grpc/rb_xds_channel_credentials.h +37 -0
  45. data/src/ruby/ext/grpc/rb_xds_server_credentials.c +170 -0
  46. data/src/ruby/ext/grpc/rb_xds_server_credentials.h +37 -0
  47. data/src/ruby/lib/grpc/2.4/grpc_c.bundle +0 -0
  48. data/src/ruby/lib/grpc/2.5/grpc_c.bundle +0 -0
  49. data/src/ruby/lib/grpc/2.6/grpc_c.bundle +0 -0
  50. data/src/ruby/lib/grpc/2.7/grpc_c.bundle +0 -0
  51. data/src/ruby/lib/grpc/3.0/grpc_c.bundle +0 -0
  52. data/src/ruby/lib/grpc/core/status_codes.rb +135 -0
  53. data/src/ruby/lib/grpc/core/time_consts.rb +56 -0
  54. data/src/ruby/lib/grpc/errors.rb +277 -0
  55. data/src/ruby/lib/grpc/generic/active_call.rb +669 -0
  56. data/src/ruby/lib/grpc/generic/bidi_call.rb +233 -0
  57. data/src/ruby/lib/grpc/generic/client_stub.rb +503 -0
  58. data/src/ruby/lib/grpc/generic/interceptor_registry.rb +53 -0
  59. data/src/ruby/lib/grpc/generic/interceptors.rb +186 -0
  60. data/src/ruby/lib/grpc/generic/rpc_desc.rb +204 -0
  61. data/src/ruby/lib/grpc/generic/rpc_server.rb +551 -0
  62. data/src/ruby/lib/grpc/generic/service.rb +211 -0
  63. data/src/ruby/lib/grpc/google_rpc_status_utils.rb +40 -0
  64. data/src/ruby/lib/grpc/grpc.rb +24 -0
  65. data/src/ruby/lib/grpc/logconfig.rb +44 -0
  66. data/src/ruby/lib/grpc/notifier.rb +45 -0
  67. data/src/ruby/lib/grpc/structs.rb +15 -0
  68. data/src/ruby/lib/grpc/version.rb +18 -0
  69. data/src/ruby/lib/grpc.rb +37 -0
  70. data/src/ruby/pb/README.md +42 -0
  71. data/src/ruby/pb/generate_proto_ruby.sh +51 -0
  72. data/src/ruby/pb/grpc/health/checker.rb +75 -0
  73. data/src/ruby/pb/grpc/health/v1/health_pb.rb +31 -0
  74. data/src/ruby/pb/grpc/health/v1/health_services_pb.rb +62 -0
  75. data/src/ruby/pb/grpc/testing/duplicate/echo_duplicate_services_pb.rb +44 -0
  76. data/src/ruby/pb/grpc/testing/metrics_pb.rb +28 -0
  77. data/src/ruby/pb/grpc/testing/metrics_services_pb.rb +49 -0
  78. data/src/ruby/pb/src/proto/grpc/testing/empty_pb.rb +17 -0
  79. data/src/ruby/pb/src/proto/grpc/testing/messages_pb.rb +145 -0
  80. data/src/ruby/pb/src/proto/grpc/testing/test_pb.rb +16 -0
  81. data/src/ruby/pb/src/proto/grpc/testing/test_services_pb.rb +152 -0
  82. data/src/ruby/pb/test/client.rb +769 -0
  83. data/src/ruby/pb/test/server.rb +252 -0
  84. data/src/ruby/pb/test/xds_client.rb +415 -0
  85. data/src/ruby/spec/call_credentials_spec.rb +42 -0
  86. data/src/ruby/spec/call_spec.rb +180 -0
  87. data/src/ruby/spec/channel_connection_spec.rb +126 -0
  88. data/src/ruby/spec/channel_credentials_spec.rb +124 -0
  89. data/src/ruby/spec/channel_spec.rb +245 -0
  90. data/src/ruby/spec/client_auth_spec.rb +152 -0
  91. data/src/ruby/spec/client_server_spec.rb +664 -0
  92. data/src/ruby/spec/compression_options_spec.rb +149 -0
  93. data/src/ruby/spec/debug_message_spec.rb +134 -0
  94. data/src/ruby/spec/error_sanity_spec.rb +49 -0
  95. data/src/ruby/spec/errors_spec.rb +142 -0
  96. data/src/ruby/spec/generic/active_call_spec.rb +683 -0
  97. data/src/ruby/spec/generic/client_interceptors_spec.rb +153 -0
  98. data/src/ruby/spec/generic/client_stub_spec.rb +1083 -0
  99. data/src/ruby/spec/generic/interceptor_registry_spec.rb +65 -0
  100. data/src/ruby/spec/generic/rpc_desc_spec.rb +374 -0
  101. data/src/ruby/spec/generic/rpc_server_pool_spec.rb +127 -0
  102. data/src/ruby/spec/generic/rpc_server_spec.rb +748 -0
  103. data/src/ruby/spec/generic/server_interceptors_spec.rb +218 -0
  104. data/src/ruby/spec/generic/service_spec.rb +263 -0
  105. data/src/ruby/spec/google_rpc_status_utils_spec.rb +282 -0
  106. data/src/ruby/spec/pb/codegen/grpc/testing/package_options.proto +28 -0
  107. data/src/ruby/spec/pb/codegen/grpc/testing/package_options_import.proto +22 -0
  108. data/src/ruby/spec/pb/codegen/grpc/testing/package_options_import2.proto +23 -0
  109. data/src/ruby/spec/pb/codegen/grpc/testing/package_options_ruby_style.proto +41 -0
  110. data/src/ruby/spec/pb/codegen/grpc/testing/same_package_service_name.proto +27 -0
  111. data/src/ruby/spec/pb/codegen/grpc/testing/same_ruby_package_service_name.proto +29 -0
  112. data/src/ruby/spec/pb/codegen/package_option_spec.rb +98 -0
  113. data/src/ruby/spec/pb/duplicate/codegen_spec.rb +57 -0
  114. data/src/ruby/spec/pb/health/checker_spec.rb +236 -0
  115. data/src/ruby/spec/server_credentials_spec.rb +104 -0
  116. data/src/ruby/spec/server_spec.rb +231 -0
  117. data/src/ruby/spec/spec_helper.rb +61 -0
  118. data/src/ruby/spec/support/helpers.rb +107 -0
  119. data/src/ruby/spec/support/services.rb +160 -0
  120. data/src/ruby/spec/testdata/README +1 -0
  121. data/src/ruby/spec/testdata/ca.pem +20 -0
  122. data/src/ruby/spec/testdata/client.key +28 -0
  123. data/src/ruby/spec/testdata/client.pem +20 -0
  124. data/src/ruby/spec/testdata/server1.key +28 -0
  125. data/src/ruby/spec/testdata/server1.pem +22 -0
  126. data/src/ruby/spec/time_consts_spec.rb +74 -0
  127. data/src/ruby/spec/user_agent_spec.rb +74 -0
  128. metadata +404 -0
@@ -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