ccrpc 0.3.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 93b1358631d8f55fb408daa3644ff3966d518fdcc0bac693468e679d8ea6a144
4
- data.tar.gz: 18c6b183a8904c7ad65ea588ec557be7f063107e3d8c191bf54f1c52fdf16915
3
+ metadata.gz: a3f1a9ac22e449534ede2f1d3f94efcc11becf3d65d7e868ce1f6334f580ba54
4
+ data.tar.gz: 70a5849b9f6594090a5069b13d566b39cf53baf69bf5ee743ead1fa56d7bd1a3
5
5
  SHA512:
6
- metadata.gz: 328200b7e1cfbd503abb5c6503d9efc8851f76c7f964bbeb9ee61986d2a7aa0f89ed318df434bcc14cff28702d1919267bf11516900a26022a2dc9744a95ac88
7
- data.tar.gz: 22b0d11ce99abedc64c8e46216025ecd753966f7114ef5351484c0b3c0a2273517a82cc524b1923dcf3dd082b3b81572c388759813d476cde13c1fd24371d95e
6
+ metadata.gz: 31e497800794295aafed95c1f75b8d2cc866aff6255d94914f0377eb04d7ad3a0009da355bc6f240466a25cd5131de46a56d8845c345f4a67b8896303e79a5e2
7
+ data.tar.gz: c457015d25201324c856a7fea9b0bc3ccdc0b5a1f9819f7c0758e45f27f3cf4c732b4028fa3e8f9af955a2e14319bde9c6ff6da9e3a3cae09b3cc560e8109520
checksums.yaml.gz.sig CHANGED
Binary file
data/README.md CHANGED
@@ -1,15 +1,16 @@
1
- # Ccrpc - A minimalistic RPC library for Ruby
1
+ # Ccrpc - A minimalist RPC library for Ruby
2
2
 
3
3
  Features:
4
- * Simple human readable wire protocol
5
- * Works on arbitrary ruby IO objects (Pipes, Sockets, STDIN, STDOUT) even Windows CR/LF converting IOs
4
+
5
+ * Simple human readable wire protocol and optionally a faster binary protocol
6
+ * Works on arbitrary IO like objects (Pipes, Sockets, STDIN, STDOUT, OpenSSL) even Windows CR/LF converting IOs
6
7
  * No object definitions - only plain string transfers (so no issues with undefined classes or garbage collection like in DRb)
7
8
  * Each call transfers a function name and a list of parameters in form of a Hash<String=>String>
8
9
  * Each response equally transfers a list of parameters
9
- * Similar to closures, it's possible to respond to a particular call as a call_back
10
- * Fully asynchronous, either by use of multiple threads or by using lazy_answers, so that arbitrary calls in both directions can be mixed simultaneously without blocking each other
10
+ * Similar to closures, it's possible to respond to a particular call as a `call_back`
11
+ * Fully asynchronous, either by use of multiple threads or by using `lazy_answers`, so that arbitrary calls in both directions can be mixed simultaneously without blocking each other
11
12
  * Fully thread safe, but doesn't use additional internal threads
12
- * Each call_back arrives in the thread of the caller
13
+ * Each `call_back` arrives in the thread of the caller
13
14
  * Only dedicated functions can be called (not arbitrary as in DRb)
14
15
  * No dependencies
15
16
 
@@ -72,7 +73,7 @@ The following example invokes the call in the opposite direction, from the subpr
72
73
  require 'ccrpc'
73
74
  # Create the receiver side of the connection
74
75
  # Use a copy of STDOUT because...
75
- rpc = Ccrpc::RpcConnection.new(STDIN, STDOUT.dup)
76
+ rpc = Ccrpc::RpcConnection.new(STDIN.binmode, STDOUT.dup.binmode)
76
77
  # .. STDOUT is now redirected to STDERR, so that pp prints to STDERR
77
78
  STDOUT.reopen(STDERR)
78
79
  # Call function "hello" with param {"who" => "world"}
@@ -84,10 +85,10 @@ The following example invokes the call in the opposite direction, from the subpr
84
85
  tf.write(code)
85
86
  tf.flush
86
87
  # Execute the temp file in a subprocess
87
- io = IO.popen(['ruby', tf.path], "w+")
88
+ io = IO.popen(['ruby', tf.path], "wb+")
88
89
 
89
90
  # Create the caller side of the connection
90
- rpc = Ccrpc::RpcConnection.new(io, io)
91
+ rpc = Ccrpc::RpcConnection.new(io, io, protocol: :binary)
91
92
  # Wait for calls
92
93
  rpc.call do |call|
93
94
  # Print the received call data to STDERR
@@ -98,6 +99,8 @@ The following example invokes the call in the opposite direction, from the subpr
98
99
  # call returns when the IO is closed by the subprocess
99
100
  ```
100
101
 
102
+ Full API documentation is here: https://rubydoc.info/gems/ccrpc
103
+
101
104
 
102
105
  ## Development
103
106
 
data/ccrpc.gemspec CHANGED
@@ -13,6 +13,7 @@ Gem::Specification.new do |spec|
13
13
  spec.required_ruby_version = Gem::Requirement.new(">= 2.7.0")
14
14
 
15
15
  spec.metadata["homepage_uri"] = spec.homepage
16
+ spec.metadata["documentation_uri"] = "https://rubydoc.info/gems/ccrpc"
16
17
 
17
18
  # Specify which files should be added to the gem when it is released.
18
19
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
data/lib/ccrpc/escape.rb CHANGED
@@ -5,7 +5,7 @@ module Ccrpc
5
5
  module Escape
6
6
  def self.escape(data)
7
7
  data = data.b if data.frozen? || data.encoding != Encoding::BINARY
8
- data.gsub(/([\a\r\n\t\\])/n){ "\\x" + $1.unpack("H2")[0] }
8
+ data.gsub(/([\a\r\n\t\\])/n){ "\\x" + $1.unpack1("H2") }
9
9
  end
10
10
 
11
11
  def self.unescape(data)
@@ -76,20 +76,43 @@ class RpcConnection
76
76
  end
77
77
 
78
78
  CallbackReceiver = Struct.new :meth, :callbacks
79
+ ReceivedCallData = Struct.new :cbfunc, :id, :recv_id
79
80
 
81
+ # The kind of +IO+ object used to receive calls and answers. Set by {initialize}.
80
82
  attr_accessor :read_io
83
+ # The kind of +IO+ object used to send calls and answers. Set by {initialize}.
81
84
  attr_accessor :write_io
82
85
 
83
86
  # Create a RPC connection
84
87
  #
85
88
  # @param [IO] read_io readable IO object for reception of data
86
89
  # @param [IO] write_io writable IO object for transmission of data
87
- # @param [Boolean] lazy_answers Enable or disable lazy results. See {#call} for more description.
88
- def initialize(read_io, write_io, lazy_answers: false)
90
+ # @param [Boolean] lazy_answers Enable or disable lazy results.
91
+ # If enabled the return value of #call is always a Ccrpc::Promise object.
92
+ # It behaves like an ordinary +nil+ or Hash object, but the actual IO blocking operation is delayed to the first method call on the Promise object.
93
+ # See {#call} for more description.
94
+ # @param [Symbol] protocol Select the protocol which is used to send calls.
95
+ # * The +:text+ protocol is the classic default.
96
+ # * The +:binary+ protocol is faster, but not so readable for human.
97
+ # * The +:prefer_binary+ is the same as :binary, but with an initial round-trip to check that the other end is binary-capable (means ccrpc >= 0.5).
98
+ # The protocol used to receive calls is selected by the *protocol* option on the other end.
99
+ # A connection could use different protocols for both directions, although this has no advantage.
100
+ def initialize(read_io, write_io, lazy_answers: false, protocol: :text)
89
101
  super()
90
102
 
91
103
  @read_io = read_io
92
104
  @write_io = write_io
105
+ @read_binary = false
106
+ @write_binary = case protocol
107
+ when :binary
108
+ true
109
+ when :text, :only_text # only_text is to simulate ccrpc-0.4.0 peer
110
+ false
111
+ when :prefer_binary
112
+ nil
113
+ else
114
+ raise ArgumentError, "invalid protocol: #{protocol.inspect}"
115
+ end
93
116
  if lazy_answers
94
117
  require 'ccrpc/lazy'
95
118
  alias maybe_lazy do_lazy
@@ -101,27 +124,100 @@ class RpcConnection
101
124
  @write_io.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, true)
102
125
  end
103
126
 
104
- # A random number as start call ID is not technically required, but makes transferred data more readable.
105
- @id = rand(1000)
106
127
  @id_mutex = Mutex.new
107
128
  @read_mutex = Mutex.new
108
129
  @write_mutex = Mutex.new
109
130
  @answers = {}
110
131
  @receivers = {}
111
132
  @answers_mutex = Mutex.new
133
+ @proto_ack_mutex = Mutex.new
112
134
  @new_answer = ConditionVariable.new
113
135
 
114
136
  @read_enum = Enumerator.new do |y|
115
137
  begin
116
138
  while @read_enum
117
- l = @read_io.gets&.force_encoding(Encoding::BINARY)
118
- break if l.nil?
119
- y << l
139
+ if @read_binary
140
+ t = @read_io.read(1)&.getbyte(0)
141
+ # p @read_io=>t
142
+ case t
143
+ when 1
144
+ keysize, valsize = @read_io.read(8).unpack("NN")
145
+ key = @read_io.read(keysize).force_encoding(Encoding::UTF_8)
146
+ value = @read_io.read(valsize).force_encoding(Encoding::UTF_8)
147
+ y << [key, value]
148
+ when 2
149
+ id, funcsize = @read_io.read(8).unpack("NN")
150
+ func = @read_io.read(funcsize)
151
+ y << ReceivedCallData.new(func.force_encoding(Encoding::UTF_8), id)
152
+ when 3
153
+ id, recv_id, funcsize = @read_io.read(12).unpack("NNN")
154
+ func = @read_io.read(funcsize)
155
+ y << ReceivedCallData.new(func.force_encoding(Encoding::UTF_8), id, recv_id)
156
+ when 4
157
+ id = @read_io.read(4).unpack1("N")
158
+ y << id
159
+ when 79 # "O"
160
+ l = @read_io.read(6)
161
+ unless l == "\tK\n\a1\n"
162
+ raise InvalidResponse, "invalid binary response #{l.inspect}"
163
+ end
164
+ y << ["O", "K"]
165
+ y << 1
166
+
167
+ when NilClass
168
+ # connection closed
169
+ break
170
+
171
+ else
172
+ raise InvalidResponse, "invalid binary response #{t.inspect}"
173
+ end
174
+
175
+ else
176
+
177
+ l = @read_io.gets&.force_encoding(Encoding::BINARY)
178
+ # p @read_io=>l
179
+ case
180
+ when l=="\r\0\a1\n" && protocol != :only_text
181
+ @read_binary = true
182
+ when l=="\r\1\a1\n" && protocol != :only_text
183
+ @read_binary = true
184
+ send_answer({O: :K}, 1)
185
+
186
+ when l=~/\A([^\t\a\n]+)\t(.*?)\r?\n\z/mn
187
+ # received key/value pair used for either callback parameters or return values
188
+ y << [Escape.unescape($1).force_encoding(Encoding::UTF_8), Escape.unescape($2.force_encoding(Encoding::UTF_8))]
189
+
190
+ when l=~/\A([^\t\a\n]+)(?:\a(\d+))?(?:\a(\d+))?\r?\n\z/mn
191
+ # received callback
192
+ y << ReceivedCallData.new(Escape.unescape($1.force_encoding(Encoding::UTF_8)), $2&.to_i, $3&.to_i)
193
+
194
+ when l=~/\A\a(\d+)\r?\n\z/mn
195
+ # received return event
196
+ y << $1.to_i
197
+
198
+ when l.nil?
199
+ # connection closed
200
+ break
201
+
202
+ else
203
+ raise InvalidResponse, "invalid text response #{l.inspect}"
204
+ end
205
+ end
120
206
  end
121
207
  rescue => err
122
208
  y << err
123
209
  end
124
210
  end
211
+
212
+ if @write_binary == true # immediate binary mode
213
+ # Use ID 1 for proto change request to have a fixed string over the wire
214
+ register_call("\r", 1)
215
+ @write_io.write "\r\0\a1\n"
216
+ @write_io.flush
217
+ end
218
+
219
+ # A random number as start call ID is not technically required, but makes transferred data more readable.
220
+ @id = rand(1000) + 1
125
221
  end
126
222
 
127
223
  private def do_lazy(&block)
@@ -131,11 +227,11 @@ class RpcConnection
131
227
  yield
132
228
  end
133
229
 
134
- # Disable reception of data from the read_io object.
230
+ # Disable reception of data from the {read_io} object.
135
231
  #
136
- # This function doesn't close the IO objects.
232
+ # This function doesn't close the +IO+ objects.
137
233
  # A waiting reception is not aborted by this call.
138
- # It can be aborted by calling IO#close on the underlying read_io and write_io objects.
234
+ # It can be aborted by calling +IO#close+ on the underlying {read_io} and {write_io} objects.
139
235
  def detach
140
236
  @read_enum = nil
141
237
  end
@@ -143,9 +239,9 @@ class RpcConnection
143
239
  # Do a RPC call and/or wait for a RPC call from the other side.
144
240
  #
145
241
  # {#call} must be called with either a function name (and optional parameters) or with a block or with both.
146
- # If {#call} is called with a function name, the block on the other side of the RPC connection is called with that function name.
147
- # If {#call} is called with a block only, than it receives these kind of calls, which are called anonymous callbacks.
148
- # If {#call} is called with a function name and a block, then the RPC function on the other side is called and it is possible to call back to this dedicated block by invoking {Call#call_back} .
242
+ # * If called with a function name, the block on the other end of the RPC connection is called with that function name.
243
+ # * If called with a block only, than it receives these kind of calls, which are called anonymous callbacks.
244
+ # * If called with a function name and a block, then the RPC function on the other side is called and it is possible to call back to this dedicated block by invoking {Call#call_back} .
149
245
  #
150
246
  # @param func [String, Symbol] The RPC function to be called on the other side.
151
247
  # The other side must wait for calls through {#call} without arguments but with a block.
@@ -160,10 +256,11 @@ class RpcConnection
160
256
  # @return [Hash] Received answer parameters.
161
257
  # @return [Promise] Received answer parameters enveloped by a Promise.
162
258
  # This type of answers can be enabled by +RpcConnection#new(lazy_answers: true)+
163
- # The Promise object is returned as soon as the RPC call is sent, but before waiting for the corresponding answer.
259
+ # The Promise object is returned as soon as the RPC call is sent and a callback receiver is registered, but before waiting for the corresponding answer.
164
260
  # This way several calls can be send in parallel without using threads.
165
- # As soon as a method is called on the Promise object, this method is blocked until the RPC answer was received.
166
- # The Promise object then behaves like a Hash object.
261
+ # As soon as any method is called on the Promise object, this method is blocked until the RPC answer was received.
262
+ # When the RPC answer has been received, the Promise object then behaves like an ordinary Hash object or +nil+ in case of connection end.
263
+ # It is recommended to use +Promise#itself+ to trigger explicit waiting for call answers or callbacks (although any other method triggers waiting as well).
167
264
  # @return [NilClass] Waiting for further answers was stopped gracefully by either returning +[hash, true]+ from the block or because the connection was closed.
168
265
  def call(func=nil, params={}, &block)
169
266
  call_intern(func, params, &block)
@@ -171,22 +268,24 @@ class RpcConnection
171
268
 
172
269
  protected
173
270
 
174
- def call_intern(func, params={}, recv_id=nil, &block)
175
- id = next_id if func
271
+ def register_call(func, id, &block)
272
+ id ||= next_id if func
176
273
 
177
274
  @answers_mutex.synchronize do
178
- @receivers[id] = CallbackReceiver.new(block_given? ? nil : caller[3], [])
275
+ @receivers[id] = CallbackReceiver.new(block_given? ? nil : caller[4], [])
179
276
  end
277
+ id
278
+ end
180
279
 
181
- send_call(func, params, id, recv_id) if func
182
-
183
- pr = proc do
280
+ def wait_for_return(id, &block)
281
+ maybe_lazy do
184
282
  @answers_mutex.synchronize do
185
283
  res = loop do
186
284
  # Is a callback pending for this thread?
187
285
  if cb=@receivers[id].callbacks.shift
188
286
  @answers_mutex.unlock
189
287
  begin
288
+ # invoke the user block
190
289
  rets, exit = yield(cb)
191
290
  if rets
192
291
  cb.answer = rets
@@ -223,7 +322,12 @@ class RpcConnection
223
322
  res
224
323
  end
225
324
  end
226
- func ? maybe_lazy(&pr) : pr.call
325
+ end
326
+
327
+ def call_intern(func, params={}, recv_id=nil, &block)
328
+ id = register_call(func, nil, &block)
329
+ send_call(func, params, id, recv_id) if func
330
+ wait_for_return(id, &block)
227
331
  end
228
332
 
229
333
  def next_id
@@ -232,70 +336,103 @@ class RpcConnection
232
336
  end
233
337
  end
234
338
 
235
- def send_call(func, params, id, recv_id=nil)
236
- to_send = String.new
237
- @write_mutex.synchronize do
238
- params.reject{|k,v| v.nil? }.each do |key, value|
239
- to_send << Escape.escape(key.to_s) << "\t" <<
240
- Escape.escape(value.to_s) << "\n"
241
- if to_send.bytesize > 9999
242
- @write_io.write to_send
243
- to_send = String.new
339
+ def wait_for_proto_ack
340
+ @proto_ack_mutex.synchronize do
341
+ if @write_binary.nil? # acknowledge text/binary mode
342
+ register_call("\r", 1)
343
+ @write_mutex.synchronize do
344
+ @write_io.write "\r\1\a1\n"
345
+ @write_io.flush
346
+ end
347
+ # wait until protocol is acknowledged
348
+ res = wait_for_return(1)
349
+ if res == {"O" => "K"}
350
+ @write_binary = true
351
+ else
352
+ @write_binary = false
244
353
  end
245
354
  end
246
- to_send << Escape.escape(func.to_s) << "\a#{id}"
247
- to_send << "\a#{recv_id}" if recv_id
248
- @write_io.write(to_send << "\n")
249
355
  end
250
- @write_io.flush
251
- after_write
252
356
  end
253
357
 
254
- def send_answer(answer, id)
358
+ def send_call_or_answer(params)
255
359
  to_send = String.new
256
360
  @write_mutex.synchronize do
257
- answer.reject{|k,v| v.nil? }.each do |key, value|
258
- to_send << Escape.escape(key.to_s) << "\t" <<
259
- Escape.escape(value.to_s) << "\n"
361
+ params.compact.each do |key, value|
362
+ if @write_binary
363
+ k = key.to_s
364
+ v = value.to_s
365
+ to_send << [1, k.bytesize, v.bytesize, k, v].pack("CNNa*a*")
366
+ else
367
+ to_send << Escape.escape(key.to_s) << "\t" <<
368
+ Escape.escape(value.to_s) << "\n"
369
+ end
260
370
  if to_send.bytesize > 9999
261
371
  @write_io.write to_send
262
372
  to_send = String.new
263
373
  end
264
374
  end
265
- to_send << "\a#{id}" if id
266
- @write_io.write(to_send << "\n")
375
+ yield(to_send)
376
+ @write_io.write(to_send)
377
+ @write_io.flush
267
378
  end
268
- @write_io.flush
269
379
  after_write
270
380
  end
271
381
 
382
+ def send_call(func, params, id, recv_id)
383
+ wait_for_proto_ack unless recv_id
384
+ send_call_or_answer(params) do |to_send|
385
+ if @write_binary
386
+ f = func.to_s
387
+ if recv_id
388
+ to_send << [3, id, recv_id, f.bytesize, f].pack("CNNNa*")
389
+ else
390
+ to_send << [2, id, f.bytesize, f].pack("CNNa*")
391
+ end
392
+ else
393
+ to_send << Escape.escape(func.to_s) << "\a#{id}"
394
+ to_send << "\a#{recv_id}" if recv_id
395
+ to_send << "\n"
396
+ end
397
+ end
398
+ end
399
+
400
+ def send_answer(answer, id)
401
+ send_call_or_answer(answer) do |to_send|
402
+ if @write_binary
403
+ to_send << [4, id].pack("CN")
404
+ else
405
+ to_send << "\a#{id}" if id
406
+ to_send << "\n"
407
+ end
408
+ end
409
+ end
410
+
272
411
  def receive_answers
273
412
  rets = {}
274
413
  (@read_enum || raise(ConnectionDetached, "connection already detached")).each do |l|
275
414
  case l
276
415
  when Exception
277
416
  raise l
278
- when /\A([^\t\a\n]+)\t(.*?)\r?\n\z/mn
279
- # received key/value pair used for either callback parameters or return values
280
- rets[Escape.unescape($1).force_encoding(Encoding::UTF_8)] ||= Escape.unescape($2.force_encoding(Encoding::UTF_8))
281
417
 
282
- when /\A([^\t\a\n]+)(?:\a(\d+))?(?:\a(\d+))?\r?\n\z/mn
283
- # received callback
284
- cbfunc, id, recv_id = $1, $2&.to_i, $3&.to_i
418
+ when Array
419
+ rets[l[0]] ||= l[1]
285
420
 
286
- callback = Call.new(self, Escape.unescape(cbfunc.force_encoding(Encoding::UTF_8)).to_sym, rets, id)
421
+ when ReceivedCallData
422
+ # received callback
423
+ callback = Call.new(self, l.cbfunc.to_sym, rets, l.id)
287
424
 
288
425
  @answers_mutex.synchronize do
289
- receiver = @receivers[recv_id]
426
+ receiver = @receivers[l.recv_id]
290
427
 
291
428
  if !receiver
292
- if recv_id
293
- raise NoCallbackDefined, "call_back to #{cbfunc.inspect} was received, but corresponding call returned already"
429
+ if l.recv_id
430
+ raise NoCallbackDefined, "call_back to #{l.cbfunc.inspect} was received, but corresponding call returned already"
294
431
  else
295
- raise NoCallbackDefined, "call to #{cbfunc.inspect} was received, but there is no #{self.class}#call running"
432
+ raise NoCallbackDefined, "call to #{l.cbfunc.inspect} was received, but there is no #{self.class}#call running"
296
433
  end
297
434
  elsif meth=receiver.meth
298
- raise NoCallbackDefined, "call_back to #{cbfunc.inspect} was received, but corresponding call was called without a block in #{meth}"
435
+ raise NoCallbackDefined, "call_back to #{l.cbfunc.inspect} was received, but corresponding call was called without a block in #{meth}"
299
436
  end
300
437
 
301
438
  receiver.callbacks << callback
@@ -303,9 +440,10 @@ class RpcConnection
303
440
  end
304
441
  return
305
442
 
306
- when /\A\a(\d+)\r?\n\z/mn
443
+ when Integer
307
444
  # received return event
308
- id = $1.to_i
445
+ id = l
446
+
309
447
  @answers_mutex.synchronize do
310
448
  @answers[id] = rets
311
449
  @new_answer.broadcast
data/lib/ccrpc/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Ccrpc
2
- VERSION = "0.3.1"
2
+ VERSION = "0.5.0"
3
3
  end
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,35 +1,38 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ccrpc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lars Kanis
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain:
11
10
  - |
12
11
  -----BEGIN CERTIFICATE-----
13
- MIIDLjCCAhagAwIBAgIBDDANBgkqhkiG9w0BAQsFADA9MQ4wDAYDVQQDDAVrYW5p
14
- czEXMBUGCgmSJomT8ixkARkWB2NvbWNhcmQxEjAQBgoJkiaJk/IsZAEZFgJkZTAe
15
- Fw0yNDA1MDIxMTAwNDVaFw0yNTA1MDIxMTAwNDVaMD0xDjAMBgNVBAMMBWthbmlz
16
- MRcwFQYKCZImiZPyLGQBGRYHY29tY2FyZDESMBAGCgmSJomT8ixkARkWAmRlMIIB
17
- IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApop+rNmg35bzRugZ21VMGqI6
18
- HGzPLO4VHYncWn/xmgPU/ZMcZdfj6MzIaZJ/czXyt4eHpBk1r8QOV3gBXnRXEjVW
19
- 9xi+EdVOkTV2/AVFKThcbTAQGiF/bT1n2M+B1GTybRzMg6hyhOJeGPqIhLfJEpxn
20
- lJi4+ENAVT4MpqHEAGB8yFoPC0GqiOHQsdHxQV3P3c2OZqG+yJey74QtwA2tLcLn
21
- Q53c63+VLGsOjODl1yPn/2ejyq8qWu6ahfTxiIlSar2UbwtaQGBDFdb2CXgEufXT
22
- L7oaPxlmj+Q2oLOfOnInd2Oxop59HoJCQPsg8f921J43NCQGA8VHK6paxIRDLQID
23
- AQABozkwNzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIEsDAdBgNVHQ4EFgQUvgTdT7fe
24
- x17ugO3IOsjEJwW7KP4wDQYJKoZIhvcNAQELBQADggEBAH+LA+CcA9vbbqtuhK4J
25
- lG1lQwA+hCKiueQgVsepNbXyDzx6PMC8ap/bFaKSaoUWABBA/bsh3jDNXT/eVZrN
26
- lFP8cVGrznSYIBG8D/QQmJKpvDBJgnC4Zk01HkhYlqJC4qCTn9X+/uZNHLPLbAEL
27
- xl3P43zyL3GQb1IP9bp0xV6oxwG9FO9Rk8bYDojky/69ylowFI5aODS39v01Siu2
28
- FsEjM9tMSNb7lQRywQ/432KXi+8AAPTm+wdGnlt3wLE9w2TTpGUBsNKk+QiytTZO
29
- zwbjAdVlRm0pg/vpDzzFmRf1GYVckMm8hpCRt8BP0akAbNyw1snYvZBwgLqxztru
30
- bEo=
12
+ MIIEBDCCAmygAwIBAgIBAzANBgkqhkiG9w0BAQsFADAoMSYwJAYDVQQDDB1sYXJz
13
+ L0RDPWdyZWl6LXJlaW5zZG9yZi9EQz1kZTAeFw0yNDEyMjkxOTU2NTZaFw0yNTEy
14
+ MjkxOTU2NTZaMCgxJjAkBgNVBAMMHWxhcnMvREM9Z3JlaXotcmVpbnNkb3JmL0RD
15
+ PWRlMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAwum6Y1KznfpzXOT/
16
+ mZgJTBbxZuuZF49Fq3K0WA67YBzNlDv95qzSp7V/7Ek3NCcnT7G+2kSuhNo1FhdN
17
+ eSDO/moYebZNAcu3iqLsuzuULXPLuoU0GsMnVMqV9DZPh7cQHE5EBZ7hlzDBK7k/
18
+ 8nBMvR0mHo77kIkapHc26UzVq/G0nKLfDsIHXVylto3PjzOumjG6GhmFN4r3cP6e
19
+ SDfl1FSeRYVpt4kmQULz/zdSaOH3AjAq7PM2Z91iGwQvoUXMANH2v89OWjQO/NHe
20
+ JMNDFsmHK/6Ji4Kk48Z3TyscHQnipAID5GhS1oD21/WePdj7GhmbF5gBzkV5uepd
21
+ eJQPgWGwrQW/Z2oPjRuJrRofzWfrMWqbOahj9uth6WSxhNexUtbjk6P8emmXOJi5
22
+ chQPnWX+N3Gj+jjYxqTFdwT7Mj3pv1VHa+aNUbqSPpvJeDyxRIuo9hvzDaBHb/Cg
23
+ 9qRVcm8a96n4t7y2lrX1oookY6bkBaxWOMtWlqIprq8JZXM9AgMBAAGjOTA3MAkG
24
+ A1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQWBBQ4h1tIyvdUWtMI739xMzTR
25
+ 7EfMFzANBgkqhkiG9w0BAQsFAAOCAYEAoZZWzNV2XXaoSmvyamSSN+Wt/Ia+DNrU
26
+ 2pc3kMEqykH6l1WiVPszr6HavQ//2I2UcSRSS5AGDdiSXcfyFmHtMBdtJHhTPcn7
27
+ 4DLliB0szpvwG+ltGD8PI8eWkLaTQeFzs+0QCTavgKV+Zw56Q0J5zZvHHUMrLkUD
28
+ qhwKjdTdkrRTn9Sqi0BrIRRZGTUDdrt8qoWm35aES5arKZzytgrRD/kXfFW2LCg0
29
+ FzgTKibR4/3g8ph94kQLg/D2SMlVPkQ3ECi036mZxDC2n8V6u3rDkG5923wmrRZB
30
+ J6cqz475Q8HYORQCB68OPzkWMfC7mBo3vpSsIqRoNs1FE4FJu4FGwZG8fBSrDC4H
31
+ bZe+GtyS3e2SMjgT65zp35gLO9I7MquzYN9P6V2u1iBpTycchk5z9R1ghxzZSBT8
32
+ DrkJ9tVlPQtJB0LqT0tvBap4upnwT1xYq721b5dwH6AF4Pi6iz/dc5vnq1/MH8bV
33
+ 8VbbBzzeE7MsvgkP3sHlLmY8PtuyViJ8
31
34
  -----END CERTIFICATE-----
32
- date: 2024-07-11 00:00:00.000000000 Z
35
+ date: 1980-01-02 00:00:00.000000000 Z
33
36
  dependencies: []
34
37
  description: Simple bidirectional and thread safe RPC protocol. Works on arbitrary
35
38
  Ruby IO objects.
@@ -60,7 +63,7 @@ licenses:
60
63
  - MIT
61
64
  metadata:
62
65
  homepage_uri: https://github.com/larskanis/ccrpc
63
- post_install_message:
66
+ documentation_uri: https://rubydoc.info/gems/ccrpc
64
67
  rdoc_options: []
65
68
  require_paths:
66
69
  - lib
@@ -75,8 +78,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
75
78
  - !ruby/object:Gem::Version
76
79
  version: '0'
77
80
  requirements: []
78
- rubygems_version: 3.5.11
79
- signing_key:
81
+ rubygems_version: 3.6.9
80
82
  specification_version: 4
81
83
  summary: Simple bidirectional RPC protocol
82
84
  test_files: []
metadata.gz.sig CHANGED
Binary file