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.
@@ -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"