grpc-dc 0.37.1
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 +7 -0
- data/LICENSE +201 -0
- data/README.md +10 -0
- data/lib/grpc_dc.rb +1 -0
- data/lib/grpcdc/channel.rb +327 -0
- data/lib/grpcdc/codec.rb +262 -0
- data/lib/grpcdc/dynamic.rb +677 -0
- data/lib/grpcdc/endpoint.rb +397 -0
- data/lib/grpcdc/eventbus.rb +250 -0
- data/lib/grpcdc/eventbus_git.rb +132 -0
- data/lib/grpcdc/events_grpc_dc.rb +232 -0
- data/lib/grpcdc/events_pb.rb +432 -0
- data/lib/grpcdc/loopback.rb +43 -0
- data/lib/grpcdc/server.rb +106 -0
- data/lib/grpcdc/sidecar.rb +63 -0
- data/lib/grpcdc/wire_gen.rb +58 -0
- data/lib/grpcdc.rb +12 -0
- metadata +61 -0
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
# Generated code: use without restriction (see GENERATED-CODE-LICENSE.md).
|
|
2
|
+
module EventsPb
|
|
3
|
+
# @generated by protoc-gen-grpc-dc-ruby. DO NOT EDIT.
|
|
4
|
+
EventsPbVarint = lambda do |value|
|
|
5
|
+
out = +"".b
|
|
6
|
+
v = value & 0xffffffffffffffff
|
|
7
|
+
while v > 0x7f
|
|
8
|
+
out << ((v & 0x7f) | 0x80)
|
|
9
|
+
v >>= 7
|
|
10
|
+
end
|
|
11
|
+
out << v
|
|
12
|
+
out
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
class Writer
|
|
16
|
+
def initialize
|
|
17
|
+
@out = String.new(encoding: Encoding::BINARY)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def varint(value)
|
|
21
|
+
@out << EventsPbVarint.call(value)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def tag(field, wire)
|
|
25
|
+
varint((field << 3) | wire)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def bytes_field(field, payload)
|
|
29
|
+
tag(field, 2)
|
|
30
|
+
varint(payload.bytesize)
|
|
31
|
+
@out << payload.b
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def string_field(field, value)
|
|
35
|
+
return if value.nil? || value.empty?
|
|
36
|
+
|
|
37
|
+
bytes_field(field, value.encode(Encoding::UTF_8).b)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def fixed(fmt, value)
|
|
41
|
+
@out << [value].pack(fmt)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def raw(payload)
|
|
45
|
+
@out << payload.b
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def to_bytes
|
|
49
|
+
@out
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
class Reader
|
|
54
|
+
def initialize(buf)
|
|
55
|
+
@buf = buf.b
|
|
56
|
+
@pos = 0
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def done?
|
|
60
|
+
@pos >= @buf.bytesize
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def varint
|
|
64
|
+
result = 0
|
|
65
|
+
shift = 0
|
|
66
|
+
loop do
|
|
67
|
+
raise ArgumentError, "truncated varint" if @pos >= @buf.bytesize
|
|
68
|
+
|
|
69
|
+
b = @buf.getbyte(@pos)
|
|
70
|
+
@pos += 1
|
|
71
|
+
result |= (b & 0x7f) << shift
|
|
72
|
+
return result if (b & 0x80).zero?
|
|
73
|
+
|
|
74
|
+
shift += 7
|
|
75
|
+
raise ArgumentError, "varint too long" if shift >= 70
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def take(n)
|
|
80
|
+
raise ArgumentError, "truncated field" if @pos + n > @buf.bytesize
|
|
81
|
+
|
|
82
|
+
value = @buf.byteslice(@pos, n)
|
|
83
|
+
@pos += n
|
|
84
|
+
value
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# [number, wire, varint_value, bytes_value, fixed_value] or nil.
|
|
88
|
+
def field
|
|
89
|
+
return nil if done?
|
|
90
|
+
|
|
91
|
+
start = @pos
|
|
92
|
+
tag = varint
|
|
93
|
+
num = tag >> 3
|
|
94
|
+
wire = tag & 7
|
|
95
|
+
raw = -> { @buf.byteslice(start, @pos - start) }
|
|
96
|
+
case wire
|
|
97
|
+
when 0 then [num, wire, varint, "".b, "".b, raw.call]
|
|
98
|
+
when 2
|
|
99
|
+
n = varint
|
|
100
|
+
v = take(n)
|
|
101
|
+
[num, wire, 0, v, "".b, raw.call]
|
|
102
|
+
when 1
|
|
103
|
+
v = take(8)
|
|
104
|
+
[num, wire, 0, "".b, v, raw.call]
|
|
105
|
+
when 5
|
|
106
|
+
v = take(4)
|
|
107
|
+
[num, wire, 0, "".b, v, raw.call]
|
|
108
|
+
else raise ArgumentError, "bad wire type"
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
module Tag
|
|
114
|
+
TAG_UNSPECIFIED = 0
|
|
115
|
+
TOPIC = 1
|
|
116
|
+
SOURCE = 2
|
|
117
|
+
KIND = 3
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
class TagValue
|
|
121
|
+
attr_accessor :tag, :string_value, :int_value, :double_value, :bool_value, :enum_value, :unknown_fields
|
|
122
|
+
|
|
123
|
+
def initialize(tag: 0, string_value: "", int_value: 0, double_value: 0.0, bool_value: false, enum_value: 0)
|
|
124
|
+
@tag = tag
|
|
125
|
+
@string_value = string_value
|
|
126
|
+
@int_value = int_value
|
|
127
|
+
@double_value = double_value
|
|
128
|
+
@bool_value = bool_value
|
|
129
|
+
@enum_value = enum_value
|
|
130
|
+
@unknown_fields = "".b
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def marshal_dc
|
|
134
|
+
w = Writer.new
|
|
135
|
+
if @tag != 0
|
|
136
|
+
w.tag(1, 0)
|
|
137
|
+
w.varint(@tag)
|
|
138
|
+
end
|
|
139
|
+
unless (@int_value != 0 || @double_value != 0 || @bool_value || @enum_value != 0)
|
|
140
|
+
w.string_field(2, @string_value)
|
|
141
|
+
end
|
|
142
|
+
unless (@double_value != 0 || @bool_value || @enum_value != 0)
|
|
143
|
+
if @int_value != 0
|
|
144
|
+
w.tag(3, 0)
|
|
145
|
+
w.varint(@int_value)
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
unless (@bool_value || @enum_value != 0)
|
|
149
|
+
if @double_value != 0
|
|
150
|
+
w.tag(4, 1)
|
|
151
|
+
w.fixed("E", @double_value)
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
unless (@enum_value != 0)
|
|
155
|
+
if @bool_value
|
|
156
|
+
w.tag(5, 0)
|
|
157
|
+
w.varint((@bool_value ? 1 : 0))
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
if @enum_value != 0
|
|
161
|
+
w.tag(6, 0)
|
|
162
|
+
w.varint(@enum_value)
|
|
163
|
+
end
|
|
164
|
+
w.raw(@unknown_fields)
|
|
165
|
+
w.to_bytes
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def unmarshal_dc(data)
|
|
169
|
+
@unknown_fields = "".b
|
|
170
|
+
@tag = 0
|
|
171
|
+
@string_value = ""
|
|
172
|
+
@int_value = 0
|
|
173
|
+
@double_value = 0.0
|
|
174
|
+
@bool_value = false
|
|
175
|
+
@enum_value = 0
|
|
176
|
+
r = Reader.new(data)
|
|
177
|
+
loop do
|
|
178
|
+
item = r.field
|
|
179
|
+
break if item.nil?
|
|
180
|
+
|
|
181
|
+
num, wire, varint_value, bytes_value, fixed_value, raw_value = item
|
|
182
|
+
case num
|
|
183
|
+
when 1
|
|
184
|
+
@tag = (((varint_value & 0xffffffff) ^ 0x80000000) - 0x80000000)
|
|
185
|
+
when 2
|
|
186
|
+
@int_value = 0
|
|
187
|
+
@double_value = 0.0
|
|
188
|
+
@bool_value = false
|
|
189
|
+
@enum_value = 0
|
|
190
|
+
@string_value = bytes_value.dup.force_encoding(Encoding::UTF_8)
|
|
191
|
+
when 3
|
|
192
|
+
@string_value = ""
|
|
193
|
+
@double_value = 0.0
|
|
194
|
+
@bool_value = false
|
|
195
|
+
@enum_value = 0
|
|
196
|
+
@int_value = (((varint_value & 0xffffffffffffffff) ^ 0x8000000000000000) - 0x8000000000000000)
|
|
197
|
+
when 4
|
|
198
|
+
@string_value = ""
|
|
199
|
+
@int_value = 0
|
|
200
|
+
@bool_value = false
|
|
201
|
+
@enum_value = 0
|
|
202
|
+
@double_value = fixed_value.unpack1("E")
|
|
203
|
+
when 5
|
|
204
|
+
@string_value = ""
|
|
205
|
+
@int_value = 0
|
|
206
|
+
@double_value = 0.0
|
|
207
|
+
@enum_value = 0
|
|
208
|
+
@bool_value = (varint_value != 0)
|
|
209
|
+
when 6
|
|
210
|
+
@string_value = ""
|
|
211
|
+
@int_value = 0
|
|
212
|
+
@double_value = 0.0
|
|
213
|
+
@bool_value = false
|
|
214
|
+
@enum_value = (((varint_value & 0xffffffff) ^ 0x80000000) - 0x80000000)
|
|
215
|
+
else
|
|
216
|
+
@unknown_fields = @unknown_fields + raw_value
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
self
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def ==(other)
|
|
223
|
+
other.is_a?(TagValue) && @tag == other.tag && @string_value == other.string_value && @int_value == other.int_value && @double_value == other.double_value && @bool_value == other.bool_value && @enum_value == other.enum_value
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
class PublishRequest
|
|
228
|
+
attr_accessor :tags, :payload, :publisher_id, :unknown_fields
|
|
229
|
+
|
|
230
|
+
def initialize(tags: [], payload: "".b, publisher_id: "")
|
|
231
|
+
@tags = tags
|
|
232
|
+
@payload = payload
|
|
233
|
+
@publisher_id = publisher_id
|
|
234
|
+
@unknown_fields = "".b
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
def marshal_dc
|
|
238
|
+
w = Writer.new
|
|
239
|
+
@tags.each { |x| w.bytes_field(1, x.marshal_dc) }
|
|
240
|
+
if @payload.bytesize > 0
|
|
241
|
+
w.bytes_field(2, @payload)
|
|
242
|
+
end
|
|
243
|
+
w.string_field(3, @publisher_id)
|
|
244
|
+
w.raw(@unknown_fields)
|
|
245
|
+
w.to_bytes
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def unmarshal_dc(data)
|
|
249
|
+
@unknown_fields = "".b
|
|
250
|
+
@tags = []
|
|
251
|
+
@payload = "".b
|
|
252
|
+
@publisher_id = ""
|
|
253
|
+
r = Reader.new(data)
|
|
254
|
+
loop do
|
|
255
|
+
item = r.field
|
|
256
|
+
break if item.nil?
|
|
257
|
+
|
|
258
|
+
num, wire, varint_value, bytes_value, fixed_value, raw_value = item
|
|
259
|
+
case num
|
|
260
|
+
when 1
|
|
261
|
+
item = TagValue.new
|
|
262
|
+
item.unmarshal_dc(bytes_value)
|
|
263
|
+
@tags << item
|
|
264
|
+
when 2
|
|
265
|
+
@payload = bytes_value
|
|
266
|
+
when 3
|
|
267
|
+
@publisher_id = bytes_value.dup.force_encoding(Encoding::UTF_8)
|
|
268
|
+
else
|
|
269
|
+
@unknown_fields = @unknown_fields + raw_value
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
self
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
def ==(other)
|
|
276
|
+
other.is_a?(PublishRequest) && @tags == other.tags && @payload == other.payload && @publisher_id == other.publisher_id
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
class PublishReply
|
|
281
|
+
attr_accessor :seq, :unknown_fields
|
|
282
|
+
|
|
283
|
+
def initialize(seq: 0)
|
|
284
|
+
@seq = seq
|
|
285
|
+
@unknown_fields = "".b
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
def marshal_dc
|
|
289
|
+
w = Writer.new
|
|
290
|
+
if @seq != 0
|
|
291
|
+
w.tag(1, 0)
|
|
292
|
+
w.varint(@seq)
|
|
293
|
+
end
|
|
294
|
+
w.raw(@unknown_fields)
|
|
295
|
+
w.to_bytes
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def unmarshal_dc(data)
|
|
299
|
+
@unknown_fields = "".b
|
|
300
|
+
@seq = 0
|
|
301
|
+
r = Reader.new(data)
|
|
302
|
+
loop do
|
|
303
|
+
item = r.field
|
|
304
|
+
break if item.nil?
|
|
305
|
+
|
|
306
|
+
num, wire, varint_value, bytes_value, fixed_value, raw_value = item
|
|
307
|
+
case num
|
|
308
|
+
when 1
|
|
309
|
+
@seq = (varint_value & 0xffffffffffffffff)
|
|
310
|
+
else
|
|
311
|
+
@unknown_fields = @unknown_fields + raw_value
|
|
312
|
+
end
|
|
313
|
+
end
|
|
314
|
+
self
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
def ==(other)
|
|
318
|
+
other.is_a?(PublishReply) && @seq == other.seq
|
|
319
|
+
end
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
class SubscribeRequest
|
|
323
|
+
attr_accessor :filter, :unknown_fields
|
|
324
|
+
|
|
325
|
+
def initialize(filter: [])
|
|
326
|
+
@filter = filter
|
|
327
|
+
@unknown_fields = "".b
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
def marshal_dc
|
|
331
|
+
w = Writer.new
|
|
332
|
+
@filter.each { |x| w.bytes_field(1, x.marshal_dc) }
|
|
333
|
+
w.raw(@unknown_fields)
|
|
334
|
+
w.to_bytes
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
def unmarshal_dc(data)
|
|
338
|
+
@unknown_fields = "".b
|
|
339
|
+
@filter = []
|
|
340
|
+
r = Reader.new(data)
|
|
341
|
+
loop do
|
|
342
|
+
item = r.field
|
|
343
|
+
break if item.nil?
|
|
344
|
+
|
|
345
|
+
num, wire, varint_value, bytes_value, fixed_value, raw_value = item
|
|
346
|
+
case num
|
|
347
|
+
when 1
|
|
348
|
+
item = TagValue.new
|
|
349
|
+
item.unmarshal_dc(bytes_value)
|
|
350
|
+
@filter << item
|
|
351
|
+
else
|
|
352
|
+
@unknown_fields = @unknown_fields + raw_value
|
|
353
|
+
end
|
|
354
|
+
end
|
|
355
|
+
self
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
def ==(other)
|
|
359
|
+
other.is_a?(SubscribeRequest) && @filter == other.filter
|
|
360
|
+
end
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
class Event
|
|
364
|
+
attr_accessor :tags, :payload, :seq, :publisher_id, :timestamp_unix_ms, :unknown_fields
|
|
365
|
+
|
|
366
|
+
def initialize(tags: [], payload: "".b, seq: 0, publisher_id: "", timestamp_unix_ms: 0)
|
|
367
|
+
@tags = tags
|
|
368
|
+
@payload = payload
|
|
369
|
+
@seq = seq
|
|
370
|
+
@publisher_id = publisher_id
|
|
371
|
+
@timestamp_unix_ms = timestamp_unix_ms
|
|
372
|
+
@unknown_fields = "".b
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
def marshal_dc
|
|
376
|
+
w = Writer.new
|
|
377
|
+
@tags.each { |x| w.bytes_field(1, x.marshal_dc) }
|
|
378
|
+
if @payload.bytesize > 0
|
|
379
|
+
w.bytes_field(2, @payload)
|
|
380
|
+
end
|
|
381
|
+
if @seq != 0
|
|
382
|
+
w.tag(3, 0)
|
|
383
|
+
w.varint(@seq)
|
|
384
|
+
end
|
|
385
|
+
w.string_field(4, @publisher_id)
|
|
386
|
+
if @timestamp_unix_ms != 0
|
|
387
|
+
w.tag(5, 0)
|
|
388
|
+
w.varint(@timestamp_unix_ms)
|
|
389
|
+
end
|
|
390
|
+
w.raw(@unknown_fields)
|
|
391
|
+
w.to_bytes
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
def unmarshal_dc(data)
|
|
395
|
+
@unknown_fields = "".b
|
|
396
|
+
@tags = []
|
|
397
|
+
@payload = "".b
|
|
398
|
+
@seq = 0
|
|
399
|
+
@publisher_id = ""
|
|
400
|
+
@timestamp_unix_ms = 0
|
|
401
|
+
r = Reader.new(data)
|
|
402
|
+
loop do
|
|
403
|
+
item = r.field
|
|
404
|
+
break if item.nil?
|
|
405
|
+
|
|
406
|
+
num, wire, varint_value, bytes_value, fixed_value, raw_value = item
|
|
407
|
+
case num
|
|
408
|
+
when 1
|
|
409
|
+
item = TagValue.new
|
|
410
|
+
item.unmarshal_dc(bytes_value)
|
|
411
|
+
@tags << item
|
|
412
|
+
when 2
|
|
413
|
+
@payload = bytes_value
|
|
414
|
+
when 3
|
|
415
|
+
@seq = (varint_value & 0xffffffffffffffff)
|
|
416
|
+
when 4
|
|
417
|
+
@publisher_id = bytes_value.dup.force_encoding(Encoding::UTF_8)
|
|
418
|
+
when 5
|
|
419
|
+
@timestamp_unix_ms = (((varint_value & 0xffffffffffffffff) ^ 0x8000000000000000) - 0x8000000000000000)
|
|
420
|
+
else
|
|
421
|
+
@unknown_fields = @unknown_fields + raw_value
|
|
422
|
+
end
|
|
423
|
+
end
|
|
424
|
+
self
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
def ==(other)
|
|
428
|
+
other.is_a?(Event) && @tags == other.tags && @payload == other.payload && @seq == other.seq && @publisher_id == other.publisher_id && @timestamp_unix_ms == other.timestamp_unix_ms
|
|
429
|
+
end
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# @generated by grpc-dc codegen. DO NOT EDIT.
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
# In-memory transport pair for tests (one queue per direction).
|
|
4
|
+
|
|
5
|
+
require_relative "endpoint"
|
|
6
|
+
|
|
7
|
+
module Grpcdc
|
|
8
|
+
class MemoryTransport < Transport
|
|
9
|
+
SENTINEL = Object.new
|
|
10
|
+
|
|
11
|
+
def initialize(inbox, outbox)
|
|
12
|
+
super()
|
|
13
|
+
@inbox = inbox
|
|
14
|
+
@outbox = outbox
|
|
15
|
+
@closed = false
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def send(data)
|
|
19
|
+
raise EOFError, "transport closed" if @closed
|
|
20
|
+
|
|
21
|
+
@outbox << data.b.dup
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def recv
|
|
25
|
+
item = @inbox.pop
|
|
26
|
+
raise EOFError, "transport closed" if item.equal?(SENTINEL)
|
|
27
|
+
|
|
28
|
+
item
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def close
|
|
32
|
+
@closed = true
|
|
33
|
+
@inbox << SENTINEL
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Two connected in-memory transports.
|
|
38
|
+
def self.memory_transport_pair
|
|
39
|
+
a_to_b = Queue.new
|
|
40
|
+
b_to_a = Queue.new
|
|
41
|
+
[MemoryTransport.new(b_to_a, a_to_b), MemoryTransport.new(a_to_b, b_to_a)]
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# @generated by grpc-dc codegen. DO NOT EDIT.
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
# Server: dispatches inbound calls to registered methods.
|
|
4
|
+
|
|
5
|
+
require_relative "codec"
|
|
6
|
+
require_relative "endpoint"
|
|
7
|
+
require_relative "wire_gen"
|
|
8
|
+
|
|
9
|
+
module Grpcdc
|
|
10
|
+
KIND_UNARY = "unary"
|
|
11
|
+
KIND_SERVER_STREAMING = "server-streaming"
|
|
12
|
+
KIND_CLIENT_STREAMING = "client-streaming"
|
|
13
|
+
KIND_BIDI = "bidi"
|
|
14
|
+
|
|
15
|
+
# Reads request messages of one inbound call.
|
|
16
|
+
class RequestStream
|
|
17
|
+
def initialize(stream_input)
|
|
18
|
+
@input = stream_input
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Next request payload; EOFError at half-close.
|
|
22
|
+
def recv_raw
|
|
23
|
+
loop do
|
|
24
|
+
event = @input.get
|
|
25
|
+
raise StatusError.new(CODE_UNAVAILABLE, "data channel closed") if event.nil?
|
|
26
|
+
|
|
27
|
+
case event.kind
|
|
28
|
+
when EVENT_DATA
|
|
29
|
+
return event.payload
|
|
30
|
+
when EVENT_HALF_CLOSE
|
|
31
|
+
raise EOFError, "half-close"
|
|
32
|
+
when EVENT_TRAILERS, EVENT_RST
|
|
33
|
+
raise event.status
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def recv_into(req)
|
|
39
|
+
req.unmarshal_dc(recv_raw)
|
|
40
|
+
req
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
class Method
|
|
45
|
+
attr_reader :name, :path, :kind, :handler
|
|
46
|
+
|
|
47
|
+
# handler: callable(ctx, RequestStream, sink) -> nil
|
|
48
|
+
def initialize(name, path, kind, handler)
|
|
49
|
+
@name = name
|
|
50
|
+
@path = path
|
|
51
|
+
@kind = kind
|
|
52
|
+
@handler = handler
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
class ServiceDesc
|
|
57
|
+
attr_reader :name, :methods
|
|
58
|
+
|
|
59
|
+
def initialize(name, methods)
|
|
60
|
+
@name = name
|
|
61
|
+
@methods = methods
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Dispatches inbound calls to registered methods.
|
|
66
|
+
class Server
|
|
67
|
+
def initialize
|
|
68
|
+
@methods = {}
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def register_service(desc)
|
|
72
|
+
desc.methods.each { |method| @methods[method.path] = method }
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Attach to a transport (even stream ids); returns Endpoint.
|
|
76
|
+
def serve_transport(transport)
|
|
77
|
+
endpoint = Endpoint.new(transport, creator: false)
|
|
78
|
+
serve_endpoint(endpoint)
|
|
79
|
+
endpoint
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def serve_endpoint(endpoint)
|
|
83
|
+
endpoint.set_dispatcher(method(:dispatch))
|
|
84
|
+
endpoint.start
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
private
|
|
88
|
+
|
|
89
|
+
def dispatch(ctx, headers, stream_input, sink)
|
|
90
|
+
method = @methods[headers.path]
|
|
91
|
+
if method.nil?
|
|
92
|
+
return StatusError.new(CODE_UNIMPLEMENTED, "unknown method #{headers.path}")
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
trace_ctx = Grpcdc.trace_start("server", headers.path, Grpcdc.parse_traceparent((headers.metadata || {})["traceparent"]))
|
|
96
|
+
result = nil
|
|
97
|
+
begin
|
|
98
|
+
method.handler.call(ctx, RequestStream.new(stream_input), sink)
|
|
99
|
+
rescue StandardError => e # StatusError passes through via to_status
|
|
100
|
+
result = Grpcdc.to_status(e)
|
|
101
|
+
end
|
|
102
|
+
Grpcdc.trace_end("server", headers.path, trace_ctx, result.nil? ? "OK" : Grpcdc.status_name(result.code))
|
|
103
|
+
result
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# @generated by grpc-dc codegen. DO NOT EDIT.
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
# Transport over the Rust WebRTC-Direct sidecar: the sidecar
|
|
4
|
+
# owns DTLS/SCTP/TURN and bridges one datachannel to a local
|
|
5
|
+
# TCP socket using u32 big-endian length-prefixed messages
|
|
6
|
+
# (one prefix per DataChannel message). Stdlib only.
|
|
7
|
+
|
|
8
|
+
require "socket"
|
|
9
|
+
require_relative "endpoint"
|
|
10
|
+
|
|
11
|
+
module Grpcdc
|
|
12
|
+
class SidecarTransport < Transport
|
|
13
|
+
def initialize(sock)
|
|
14
|
+
super()
|
|
15
|
+
@sock = sock
|
|
16
|
+
@buf = +"".b
|
|
17
|
+
@closed = false
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.connect(host, port)
|
|
21
|
+
new(TCPSocket.new(host, port))
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.from_socket(sock)
|
|
25
|
+
new(sock)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def send(data)
|
|
29
|
+
raise EOFError, "transport closed" if @closed
|
|
30
|
+
|
|
31
|
+
payload = data.b
|
|
32
|
+
@sock.write([payload.bytesize].pack("N") + payload)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def recv
|
|
36
|
+
fill(4)
|
|
37
|
+
length = @buf.unpack1("N")
|
|
38
|
+
@buf = @buf.byteslice(4..) || +"".b
|
|
39
|
+
fill(length)
|
|
40
|
+
payload = @buf.byteslice(0, length) || +"".b
|
|
41
|
+
@buf = @buf.byteslice(length..) || +"".b
|
|
42
|
+
payload
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def close
|
|
46
|
+
@closed = true
|
|
47
|
+
@sock.close
|
|
48
|
+
rescue IOError
|
|
49
|
+
nil
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def fill(need)
|
|
55
|
+
while @buf.bytesize < need
|
|
56
|
+
chunk = @sock.readpartial(65_536)
|
|
57
|
+
raise EOFError, "transport closed" if chunk.nil? || chunk.empty?
|
|
58
|
+
|
|
59
|
+
@buf << chunk.b
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
# @generated by grpc-dc codegen. DO NOT EDIT.
|
|
3
|
+
|
|
4
|
+
# Frame type bytes and gRPC status codes (SPEC.md).
|
|
5
|
+
|
|
6
|
+
module Grpcdc
|
|
7
|
+
FRAME_HEADERS = 1 # call headers, JSON {path, metadata}
|
|
8
|
+
FRAME_DATA = 2 # one serialized protobuf message
|
|
9
|
+
FRAME_TRAILERS = 3 # terminal status, JSON {code, message}
|
|
10
|
+
FRAME_HALF_CLOSE = 4 # client finished sending
|
|
11
|
+
FRAME_RST = 5 # abort stream, JSON {code, message}
|
|
12
|
+
|
|
13
|
+
CODE_OK = 0
|
|
14
|
+
CODE_CANCELLED = 1
|
|
15
|
+
CODE_UNKNOWN = 2
|
|
16
|
+
CODE_INVALID_ARGUMENT = 3
|
|
17
|
+
CODE_DEADLINE_EXCEEDED = 4
|
|
18
|
+
CODE_NOT_FOUND = 5
|
|
19
|
+
CODE_ALREADY_EXISTS = 6
|
|
20
|
+
CODE_PERMISSION_DENIED = 7
|
|
21
|
+
CODE_RESOURCE_EXHAUSTED = 8
|
|
22
|
+
CODE_FAILED_PRECONDITION = 9
|
|
23
|
+
CODE_ABORTED = 10
|
|
24
|
+
CODE_OUT_OF_RANGE = 11
|
|
25
|
+
CODE_UNIMPLEMENTED = 12
|
|
26
|
+
CODE_INTERNAL = 13
|
|
27
|
+
CODE_UNAVAILABLE = 14
|
|
28
|
+
CODE_DATA_LOSS = 15
|
|
29
|
+
CODE_UNAUTHENTICATED = 16
|
|
30
|
+
|
|
31
|
+
FRAME_NAMES = {
|
|
32
|
+
"HEADERS" => 1,
|
|
33
|
+
"DATA" => 2,
|
|
34
|
+
"TRAILERS" => 3,
|
|
35
|
+
"HALF-CLOSE" => 4,
|
|
36
|
+
"RST" => 5,
|
|
37
|
+
}.freeze
|
|
38
|
+
|
|
39
|
+
STATUS_NAMES = {
|
|
40
|
+
0 => "OK",
|
|
41
|
+
1 => "CANCELLED",
|
|
42
|
+
2 => "UNKNOWN",
|
|
43
|
+
3 => "INVALID-ARGUMENT",
|
|
44
|
+
4 => "DEADLINE-EXCEEDED",
|
|
45
|
+
5 => "NOT-FOUND",
|
|
46
|
+
6 => "ALREADY-EXISTS",
|
|
47
|
+
7 => "PERMISSION-DENIED",
|
|
48
|
+
8 => "RESOURCE-EXHAUSTED",
|
|
49
|
+
9 => "FAILED-PRECONDITION",
|
|
50
|
+
10 => "ABORTED",
|
|
51
|
+
11 => "OUT-OF-RANGE",
|
|
52
|
+
12 => "UNIMPLEMENTED",
|
|
53
|
+
13 => "INTERNAL",
|
|
54
|
+
14 => "UNAVAILABLE",
|
|
55
|
+
15 => "DATA-LOSS",
|
|
56
|
+
16 => "UNAUTHENTICATED",
|
|
57
|
+
}.freeze
|
|
58
|
+
end
|
data/lib/grpcdc.rb
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# @generated by grpc-dc codegen. DO NOT EDIT.
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
# grpcdc: gRPC over WebRTC DataChannels (generated runtime).
|
|
4
|
+
|
|
5
|
+
require_relative "grpcdc/wire_gen"
|
|
6
|
+
require_relative "grpcdc/codec"
|
|
7
|
+
require_relative "grpcdc/endpoint"
|
|
8
|
+
require_relative "grpcdc/channel"
|
|
9
|
+
require_relative "grpcdc/server"
|
|
10
|
+
require_relative "grpcdc/loopback"
|
|
11
|
+
require_relative "grpcdc/sidecar"
|
|
12
|
+
require_relative "grpcdc/dynamic"
|