ccrpc 0.4.0 → 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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/README.md +10 -9
- data/lib/ccrpc/escape.rb +1 -1
- data/lib/ccrpc/rpc_connection.rb +188 -53
- data/lib/ccrpc/version.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +25 -26
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a3f1a9ac22e449534ede2f1d3f94efcc11becf3d65d7e868ce1f6334f580ba54
|
4
|
+
data.tar.gz: 70a5849b9f6594090a5069b13d566b39cf53baf69bf5ee743ead1fa56d7bd1a3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
1
|
+
# Ccrpc - A minimalist RPC library for Ruby
|
2
2
|
|
3
3
|
Features:
|
4
|
-
|
5
|
-
*
|
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
|
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], "
|
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
|
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.
|
8
|
+
data.gsub(/([\a\r\n\t\\])/n){ "\\x" + $1.unpack1("H2") }
|
9
9
|
end
|
10
10
|
|
11
11
|
def self.unescape(data)
|
data/lib/ccrpc/rpc_connection.rb
CHANGED
@@ -76,8 +76,11 @@ 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
|
@@ -88,11 +91,28 @@ class RpcConnection
|
|
88
91
|
# If enabled the return value of #call is always a Ccrpc::Promise object.
|
89
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.
|
90
93
|
# See {#call} for more description.
|
91
|
-
|
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)
|
92
101
|
super()
|
93
102
|
|
94
103
|
@read_io = read_io
|
95
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
|
96
116
|
if lazy_answers
|
97
117
|
require 'ccrpc/lazy'
|
98
118
|
alias maybe_lazy do_lazy
|
@@ -104,27 +124,100 @@ class RpcConnection
|
|
104
124
|
@write_io.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, true)
|
105
125
|
end
|
106
126
|
|
107
|
-
# A random number as start call ID is not technically required, but makes transferred data more readable.
|
108
|
-
@id = rand(1000)
|
109
127
|
@id_mutex = Mutex.new
|
110
128
|
@read_mutex = Mutex.new
|
111
129
|
@write_mutex = Mutex.new
|
112
130
|
@answers = {}
|
113
131
|
@receivers = {}
|
114
132
|
@answers_mutex = Mutex.new
|
133
|
+
@proto_ack_mutex = Mutex.new
|
115
134
|
@new_answer = ConditionVariable.new
|
116
135
|
|
117
136
|
@read_enum = Enumerator.new do |y|
|
118
137
|
begin
|
119
138
|
while @read_enum
|
120
|
-
|
121
|
-
|
122
|
-
|
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
|
123
206
|
end
|
124
207
|
rescue => err
|
125
208
|
y << err
|
126
209
|
end
|
127
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
|
128
221
|
end
|
129
222
|
|
130
223
|
private def do_lazy(&block)
|
@@ -134,11 +227,11 @@ class RpcConnection
|
|
134
227
|
yield
|
135
228
|
end
|
136
229
|
|
137
|
-
# Disable reception of data from the read_io object.
|
230
|
+
# Disable reception of data from the {read_io} object.
|
138
231
|
#
|
139
|
-
# This function doesn't close the IO objects.
|
232
|
+
# This function doesn't close the +IO+ objects.
|
140
233
|
# A waiting reception is not aborted by this call.
|
141
|
-
# 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.
|
142
235
|
def detach
|
143
236
|
@read_enum = nil
|
144
237
|
end
|
@@ -146,9 +239,9 @@ class RpcConnection
|
|
146
239
|
# Do a RPC call and/or wait for a RPC call from the other side.
|
147
240
|
#
|
148
241
|
# {#call} must be called with either a function name (and optional parameters) or with a block or with both.
|
149
|
-
# If
|
150
|
-
# If
|
151
|
-
# If
|
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} .
|
152
245
|
#
|
153
246
|
# @param func [String, Symbol] The RPC function to be called on the other side.
|
154
247
|
# The other side must wait for calls through {#call} without arguments but with a block.
|
@@ -165,9 +258,9 @@ class RpcConnection
|
|
165
258
|
# This type of answers can be enabled by +RpcConnection#new(lazy_answers: true)+
|
166
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.
|
167
260
|
# This way several calls can be send in parallel without using threads.
|
168
|
-
# As soon as
|
169
|
-
#
|
170
|
-
# It is recommended to use Promise#itself to trigger waiting for call answers or callbacks (although any other method triggers waiting as well).
|
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).
|
171
264
|
# @return [NilClass] Waiting for further answers was stopped gracefully by either returning +[hash, true]+ from the block or because the connection was closed.
|
172
265
|
def call(func=nil, params={}, &block)
|
173
266
|
call_intern(func, params, &block)
|
@@ -175,15 +268,16 @@ class RpcConnection
|
|
175
268
|
|
176
269
|
protected
|
177
270
|
|
178
|
-
def
|
179
|
-
id
|
271
|
+
def register_call(func, id, &block)
|
272
|
+
id ||= next_id if func
|
180
273
|
|
181
274
|
@answers_mutex.synchronize do
|
182
|
-
@receivers[id] = CallbackReceiver.new(block_given? ? nil : caller[
|
275
|
+
@receivers[id] = CallbackReceiver.new(block_given? ? nil : caller[4], [])
|
183
276
|
end
|
277
|
+
id
|
278
|
+
end
|
184
279
|
|
185
|
-
|
186
|
-
|
280
|
+
def wait_for_return(id, &block)
|
187
281
|
maybe_lazy do
|
188
282
|
@answers_mutex.synchronize do
|
189
283
|
res = loop do
|
@@ -191,6 +285,7 @@ class RpcConnection
|
|
191
285
|
if cb=@receivers[id].callbacks.shift
|
192
286
|
@answers_mutex.unlock
|
193
287
|
begin
|
288
|
+
# invoke the user block
|
194
289
|
rets, exit = yield(cb)
|
195
290
|
if rets
|
196
291
|
cb.answer = rets
|
@@ -229,47 +324,88 @@ class RpcConnection
|
|
229
324
|
end
|
230
325
|
end
|
231
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)
|
331
|
+
end
|
332
|
+
|
232
333
|
def next_id
|
233
334
|
@id_mutex.synchronize do
|
234
335
|
@id = (@id + 1) & 0xffffffff
|
235
336
|
end
|
236
337
|
end
|
237
338
|
|
238
|
-
def
|
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
|
353
|
+
end
|
354
|
+
end
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
358
|
+
def send_call_or_answer(params)
|
239
359
|
to_send = String.new
|
240
360
|
@write_mutex.synchronize do
|
241
361
|
params.compact.each do |key, value|
|
242
|
-
|
243
|
-
|
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
|
244
370
|
if to_send.bytesize > 9999
|
245
371
|
@write_io.write to_send
|
246
372
|
to_send = String.new
|
247
373
|
end
|
248
374
|
end
|
249
|
-
to_send
|
250
|
-
to_send
|
251
|
-
@write_io.write(to_send << "\n")
|
375
|
+
yield(to_send)
|
376
|
+
@write_io.write(to_send)
|
252
377
|
@write_io.flush
|
253
378
|
end
|
254
379
|
after_write
|
255
380
|
end
|
256
381
|
|
257
|
-
def
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
to_send
|
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*")
|
266
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"
|
267
407
|
end
|
268
|
-
to_send << "\a#{id}" if id
|
269
|
-
@write_io.write(to_send << "\n")
|
270
|
-
@write_io.flush
|
271
408
|
end
|
272
|
-
after_write
|
273
409
|
end
|
274
410
|
|
275
411
|
def receive_answers
|
@@ -278,27 +414,25 @@ class RpcConnection
|
|
278
414
|
case l
|
279
415
|
when Exception
|
280
416
|
raise l
|
281
|
-
when /\A([^\t\a\n]+)\t(.*?)\r?\n\z/mn
|
282
|
-
# received key/value pair used for either callback parameters or return values
|
283
|
-
rets[Escape.unescape($1).force_encoding(Encoding::UTF_8)] ||= Escape.unescape($2.force_encoding(Encoding::UTF_8))
|
284
417
|
|
285
|
-
when
|
286
|
-
|
287
|
-
cbfunc, id, recv_id = $1, $2&.to_i, $3&.to_i
|
418
|
+
when Array
|
419
|
+
rets[l[0]] ||= l[1]
|
288
420
|
|
289
|
-
|
421
|
+
when ReceivedCallData
|
422
|
+
# received callback
|
423
|
+
callback = Call.new(self, l.cbfunc.to_sym, rets, l.id)
|
290
424
|
|
291
425
|
@answers_mutex.synchronize do
|
292
|
-
receiver = @receivers[recv_id]
|
426
|
+
receiver = @receivers[l.recv_id]
|
293
427
|
|
294
428
|
if !receiver
|
295
|
-
if recv_id
|
296
|
-
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"
|
297
431
|
else
|
298
|
-
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"
|
299
433
|
end
|
300
434
|
elsif meth=receiver.meth
|
301
|
-
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}"
|
302
436
|
end
|
303
437
|
|
304
438
|
receiver.callbacks << callback
|
@@ -306,9 +440,10 @@ class RpcConnection
|
|
306
440
|
end
|
307
441
|
return
|
308
442
|
|
309
|
-
when
|
443
|
+
when Integer
|
310
444
|
# received return event
|
311
|
-
id =
|
445
|
+
id = l
|
446
|
+
|
312
447
|
@answers_mutex.synchronize do
|
313
448
|
@answers[id] = rets
|
314
449
|
@new_answer.broadcast
|
data/lib/ccrpc/version.rb
CHANGED
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,37 +1,38 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ccrpc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
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
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
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
|
33
34
|
-----END CERTIFICATE-----
|
34
|
-
date:
|
35
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
35
36
|
dependencies: []
|
36
37
|
description: Simple bidirectional and thread safe RPC protocol. Works on arbitrary
|
37
38
|
Ruby IO objects.
|
@@ -63,7 +64,6 @@ licenses:
|
|
63
64
|
metadata:
|
64
65
|
homepage_uri: https://github.com/larskanis/ccrpc
|
65
66
|
documentation_uri: https://rubydoc.info/gems/ccrpc
|
66
|
-
post_install_message:
|
67
67
|
rdoc_options: []
|
68
68
|
require_paths:
|
69
69
|
- lib
|
@@ -78,8 +78,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
78
78
|
- !ruby/object:Gem::Version
|
79
79
|
version: '0'
|
80
80
|
requirements: []
|
81
|
-
rubygems_version: 3.
|
82
|
-
signing_key:
|
81
|
+
rubygems_version: 3.6.9
|
83
82
|
specification_version: 4
|
84
83
|
summary: Simple bidirectional RPC protocol
|
85
84
|
test_files: []
|
metadata.gz.sig
CHANGED
Binary file
|