arf 0.1.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.
Files changed (80) hide show
  1. checksums.yaml +7 -0
  2. data/.ruby-version +1 -0
  3. data/LICENSE.txt +21 -0
  4. data/Rakefile +12 -0
  5. data/lib/arf/configuration.rb +67 -0
  6. data/lib/arf/context.rb +170 -0
  7. data/lib/arf/errors.rb +10 -0
  8. data/lib/arf/io/buffer.rb +25 -0
  9. data/lib/arf/io/compression.rb +44 -0
  10. data/lib/arf/io/limit_reader.rb +30 -0
  11. data/lib/arf/observer.rb +29 -0
  12. data/lib/arf/proto/array.rb +31 -0
  13. data/lib/arf/proto/boolean.rb +15 -0
  14. data/lib/arf/proto/bytes.rb +35 -0
  15. data/lib/arf/proto/decoder.rb +31 -0
  16. data/lib/arf/proto/encoder.rb +71 -0
  17. data/lib/arf/proto/float.rb +48 -0
  18. data/lib/arf/proto/map.rb +49 -0
  19. data/lib/arf/proto/registry.rb +27 -0
  20. data/lib/arf/proto/scalar.rb +55 -0
  21. data/lib/arf/proto/string.rb +36 -0
  22. data/lib/arf/proto/struct.rb +84 -0
  23. data/lib/arf/proto/types.rb +67 -0
  24. data/lib/arf/proto/union.rb +25 -0
  25. data/lib/arf/proto.rb +17 -0
  26. data/lib/arf/reactor.rb +270 -0
  27. data/lib/arf/rpc/base_message.rb +119 -0
  28. data/lib/arf/rpc/client_base.rb +110 -0
  29. data/lib/arf/rpc/end_stream.rb +9 -0
  30. data/lib/arf/rpc/enum.rb +51 -0
  31. data/lib/arf/rpc/message_kind.rb +27 -0
  32. data/lib/arf/rpc/metadata.rb +120 -0
  33. data/lib/arf/rpc/method_meta.rb +42 -0
  34. data/lib/arf/rpc/request.rb +39 -0
  35. data/lib/arf/rpc/responder.rb +186 -0
  36. data/lib/arf/rpc/response.rb +46 -0
  37. data/lib/arf/rpc/service_base.rb +137 -0
  38. data/lib/arf/rpc/start_stream.rb +9 -0
  39. data/lib/arf/rpc/stream_error.rb +23 -0
  40. data/lib/arf/rpc/stream_item.rb +16 -0
  41. data/lib/arf/rpc/stream_metadata.rb +16 -0
  42. data/lib/arf/rpc/struct.rb +255 -0
  43. data/lib/arf/rpc.rb +19 -0
  44. data/lib/arf/server.rb +123 -0
  45. data/lib/arf/status.rb +75 -0
  46. data/lib/arf/types/array_type.rb +24 -0
  47. data/lib/arf/types/base_type.rb +14 -0
  48. data/lib/arf/types/coercion.rb +36 -0
  49. data/lib/arf/types/in_out_stream.rb +28 -0
  50. data/lib/arf/types/input_stream.rb +8 -0
  51. data/lib/arf/types/map_type.rb +32 -0
  52. data/lib/arf/types/mixin.rb +29 -0
  53. data/lib/arf/types/output_stream.rb +8 -0
  54. data/lib/arf/types/streamer.rb +21 -0
  55. data/lib/arf/types.rb +69 -0
  56. data/lib/arf/version.rb +5 -0
  57. data/lib/arf/wire/base_connection.rb +177 -0
  58. data/lib/arf/wire/client.rb +101 -0
  59. data/lib/arf/wire/encoding.rb +49 -0
  60. data/lib/arf/wire/error_code.rb +35 -0
  61. data/lib/arf/wire/errors.rb +88 -0
  62. data/lib/arf/wire/frame.rb +111 -0
  63. data/lib/arf/wire/frame_kind.rb +23 -0
  64. data/lib/arf/wire/frame_reader.rb +104 -0
  65. data/lib/arf/wire/frames/base_frame.rb +108 -0
  66. data/lib/arf/wire/frames/configuration_frame.rb +33 -0
  67. data/lib/arf/wire/frames/data_frame.rb +21 -0
  68. data/lib/arf/wire/frames/go_away_frame.rb +29 -0
  69. data/lib/arf/wire/frames/make_stream_frame.rb +15 -0
  70. data/lib/arf/wire/frames/ping_frame.rb +18 -0
  71. data/lib/arf/wire/frames/reset_stream_frame.rb +19 -0
  72. data/lib/arf/wire/frames.rb +9 -0
  73. data/lib/arf/wire/server/peer.rb +85 -0
  74. data/lib/arf/wire/server.rb +63 -0
  75. data/lib/arf/wire/stream/state.rb +69 -0
  76. data/lib/arf/wire/stream.rb +128 -0
  77. data/lib/arf/wire/wait_signal.rb +24 -0
  78. data/lib/arf/wire.rb +14 -0
  79. data/lib/arf.rb +46 -0
  80. metadata +195 -0
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arf
4
+ module RPC
5
+ class BaseMessage
6
+ def self.register(kind, cls)
7
+ @messages ||= {}
8
+ @messages[kind] = cls
9
+ end
10
+
11
+ def self.message_by_kind(kind)
12
+ @messages ||= {}
13
+ @messages[kind]
14
+ end
15
+
16
+ def self.initialize_from(data)
17
+ type = MESSAGE_KIND_FROM_BYTE[data.readbyte] || :invalid
18
+ raise "Cannot decode invalid message" if type == :invalid
19
+
20
+ instance = message_by_kind(type).new
21
+ instance.decode(data)
22
+ instance
23
+ end
24
+
25
+ def self.encode(message)
26
+ IO::Buffer.new
27
+ .write(MESSAGE_KIND_FROM_SYMBOL[message.kind])
28
+ .write_raw(message.encode)
29
+ .string
30
+ end
31
+
32
+ def self.has_status
33
+ define_method(:status=) do |val|
34
+ case val
35
+ when Symbol
36
+ @status = Status::FROM_SYMBOL[val]
37
+ raise ArgumentError, "Invalid value #{val.inspect} for status: Unknown status" unless @status
38
+ when Integer
39
+ if Status::TO_SYMBOL[val].nil?
40
+ raise ArgumentError, "Invalid value #{val.inspect} for status: Unknown status"
41
+ end
42
+
43
+ @status = val
44
+ else
45
+ raise ArgumentError, "Invalid value #{val.inspect} for status: Expected symbol or integer"
46
+ end
47
+ end
48
+
49
+ define_method(:status) do
50
+ return nil if @status.nil?
51
+
52
+ Status::TO_SYMBOL[@status] || :unknown
53
+ end
54
+ end
55
+
56
+ def self.has_metadata
57
+ attr_reader :metadata
58
+
59
+ define_method(:metadata=) do |val|
60
+ case val
61
+ when NilClass
62
+ @metadata = Metadata.new
63
+ when Metadata
64
+ @metadata = val
65
+ when Hash
66
+ @metadata = Metadata.new(**val)
67
+ else
68
+ raise ArgumentError, "Invalid value #{val.inspect} for metadata: Expected nil, Arf::RPC::Metadata, or Hash"
69
+ end
70
+ end
71
+ end
72
+
73
+ def self.has_streaming
74
+ attr_accessor :streaming
75
+
76
+ define_method(:streaming?) { @streaming }
77
+ end
78
+
79
+ def self.kind(kind = nil)
80
+ return @kind if kind.nil?
81
+
82
+ @kind = kind
83
+ define_method(:kind) { self.class.kind }
84
+ BaseMessage.register(kind, self)
85
+ kind
86
+ end
87
+
88
+ def initialize(**kwargs)
89
+ kwargs.each { |k, v| send("#{k}=", v) }
90
+ @params ||= [] if respond_to? :params=
91
+ @streaming ||= false if respond_to? :streaming=
92
+ @metadata ||= Metadata.new if respond_to? :metadata=
93
+ end
94
+
95
+ def encode = ""
96
+
97
+ def decode(_data) = nil
98
+
99
+ def decode_string(data)
100
+ type, header = Proto.read_type(data)
101
+ raise "Invalid message payload" if type != Proto::TYPE_STRING
102
+
103
+ Proto.decode_string(header, data)
104
+ end
105
+
106
+ def decode_bytes(data)
107
+ type, header = Proto.read_type(data)
108
+ raise "Invalid message payload" if type != Proto::TYPE_BYTES
109
+
110
+ Proto.decode_bytes(header, data)
111
+ end
112
+
113
+ def decode_uint16(data) = Wire.decode_uint16(data)
114
+ def encode_uint16(value) = Wire.encode_uint16(value)
115
+ def encode_string(str) = Proto.encode_string(str)
116
+ def encode_bytes(data) = Proto.encode_bytes(data)
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arf
4
+ module RPC
5
+ class ClientBase
6
+ include Arf::Types::Mixin
7
+
8
+ def self.arf_service_id(id)
9
+ @arf_service_id = id
10
+ end
11
+
12
+ def self.rpc(name, inputs: nil, outputs: nil)
13
+ @rpc_metadata ||= {}
14
+ inputs ||= {}
15
+ outputs = [outputs].compact unless outputs.is_a? Array
16
+
17
+ @rpc_metadata[name] = {
18
+ name:, inputs: inputs.dup, outputs: outputs.dup
19
+ }
20
+
21
+ inputs = inputs.to_a
22
+ .map do |type|
23
+ if type.last.is_a? InputStream
24
+ outputs << type.last
25
+ next nil
26
+ end
27
+ type
28
+ end
29
+ .compact.to_h
30
+
31
+ if outputs.length > 1 && outputs[-2].is_a?(Streamer) && outputs[-1].is_a?(Streamer)
32
+ input = outputs.find { _1.is_a? InputStream }
33
+ output = outputs.find { _1.is_a? OutputStream }
34
+
35
+ outputs = outputs[...-2]
36
+ outputs << InOutStream[input.type, output.type]
37
+ end
38
+
39
+ @rpc_metadata[name][:native_inputs] = inputs
40
+ @rpc_metadata[name][:native_outputs] = outputs
41
+
42
+ internal_name = "_#{name}"
43
+ service_name = @arf_service_id
44
+
45
+ class_eval(<<~RUBY, __FILE__, __LINE__ + 1)
46
+ # def name(param, param, param, **metadata, &block)
47
+ # internal_name(params, param, param, **metadata, &block)
48
+ # end
49
+ def #{name}(#{inputs.keys.join(", ")}#{inputs.length.positive? ? ", " : ""}**metadata, &block)
50
+ #{internal_name}(#{inputs.keys.join(", ")}#{inputs.length.positive? ? ", " : ""}**metadata, &block)
51
+ end
52
+ RUBY
53
+
54
+ define_method(internal_name) do |*args, **metadata, &block|
55
+ _invoke_method(service_name, name, inputs.values, outputs, args, metadata, &block)
56
+ end
57
+ end
58
+
59
+ def initialize(client)
60
+ @client = client
61
+ end
62
+
63
+ def _invoke_method(service_id, method_name, inputs, outputs, args, meta)
64
+ mapped_args = args.map.with_index do |v, idx|
65
+ raw_type = case (t = inputs[idx])
66
+ when Symbol then t
67
+ when String then self.class.find_type(t)
68
+ else
69
+ raise ArgumentError, "Unknown type definition #{v.inspect}"
70
+ end
71
+
72
+ Arf::Types.coerce_value(v, raw_type)
73
+ end
74
+
75
+ # Obtain a stream from @client
76
+ str = @client.new_stream
77
+
78
+ # Push the request
79
+ streaming = outputs.any? { _1.is_a?(InputStream) || _1.is_a?(InOutStream) }
80
+ req = Request.new(
81
+ streaming:,
82
+ metadata: meta,
83
+ service: service_id,
84
+ method: method_name,
85
+ params: mapped_args
86
+ )
87
+
88
+ input_type = nil
89
+ output_type = nil
90
+
91
+ streamer = outputs.last
92
+ case streamer
93
+ when InputStream
94
+ input_type = streamer.resolved_type(self.class)
95
+ when OutputStream
96
+ output_type = streamer.resolved_type(self.class)
97
+ when InOutStream
98
+ input_type = streamer.resolved_input(self.class)
99
+ output_type = streamer.resolved_output(self.class)
100
+ end
101
+
102
+ resp = Responder.new(str, input_type, output_type)
103
+
104
+ str.write_data(BaseMessage.encode(req), end_stream: !streaming)
105
+
106
+ resp
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arf
4
+ module RPC
5
+ class EndStream < BaseMessage
6
+ kind :end_stream
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arf
4
+ module RPC
5
+ class Enum
6
+ def self.option(**kwargs)
7
+ @options ||= {}
8
+ kwargs.each { |k, v| @options[k.to_sym] = v }
9
+ end
10
+
11
+ def self.option_by_name(name)
12
+ @options ||= {}
13
+ @options[name.to_sym]
14
+ end
15
+
16
+ def self.option_by_value(value)
17
+ @options ||= {}
18
+ @options.find { _2 == value }&.first
19
+ end
20
+
21
+ class << self
22
+ attr_reader :options
23
+ end
24
+
25
+ def self.to_i(value)
26
+ case value
27
+ when Symbol, String
28
+ option_by_name(value) or raise(ArgumentError, "Invalid value #{value.inspect} for #{self}")
29
+ when Integer
30
+ option_by_value(value) or raise(ArgumentError, "Invalid value #{value.inspect} for #{self}")
31
+ value
32
+ else
33
+ raise ArgumentError, "Invalid value type #{value.class}. Expected Symbol, String or Integer."
34
+ end
35
+ end
36
+
37
+ def self.to_sym(value)
38
+ case value
39
+ when Symbol, String
40
+ value = value.to_sym
41
+ option_by_name(value) or raise(ArgumentError, "Invalid value #{value.inspect} for #{self}")
42
+ value
43
+ when Integer
44
+ option_by_value(value) or raise(ArgumentError, "Invalid value #{value.inspect} for #{self}")
45
+ else
46
+ raise ArgumentError, "Invalid value type #{value.class}. Expected Symbol, String or Integer."
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arf
4
+ module RPC
5
+ MESSAGE_KIND_INVALID = 0x00
6
+ MESSAGE_KIND_REQUEST = 0x01
7
+ MESSAGE_KIND_RESPONSE = 0x02
8
+ MESSAGE_KIND_START_STREAM = 0x03
9
+ MESSAGE_KIND_STREAM_ITEM = 0x04
10
+ MESSAGE_KIND_STREAM_METADATA = 0x05
11
+ MESSAGE_KIND_END_STREAM = 0x06
12
+ MESSAGE_KIND_STREAM_ERROR = 0x07
13
+
14
+ MESSAGE_KIND_FROM_BYTE = {
15
+ MESSAGE_KIND_INVALID => :invalid,
16
+ MESSAGE_KIND_REQUEST => :request,
17
+ MESSAGE_KIND_RESPONSE => :response,
18
+ MESSAGE_KIND_START_STREAM => :start_stream,
19
+ MESSAGE_KIND_STREAM_ITEM => :stream_item,
20
+ MESSAGE_KIND_STREAM_METADATA => :stream_metadata,
21
+ MESSAGE_KIND_END_STREAM => :end_stream,
22
+ MESSAGE_KIND_STREAM_ERROR => :stream_error
23
+ }.freeze
24
+
25
+ MESSAGE_KIND_FROM_SYMBOL = MESSAGE_KIND_FROM_BYTE.to_a.to_h(&:reverse)
26
+ end
27
+ end
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arf
4
+ module RPC
5
+ class Metadata < BaseMessage
6
+ def initialize(**kwargs)
7
+ super()
8
+ @pairs = {}
9
+ @dirty = false
10
+ @observer = Observer.new
11
+ kwargs.each { |k, v| set(k, v) }
12
+ end
13
+
14
+ def attach_observer(obs)
15
+ @observer.attach_handler(obs)
16
+ end
17
+
18
+ def add(k, v) = @observer.modify { _add(k, v) }
19
+
20
+ def [](k) = @pairs[k]
21
+ def get(k) = @pairs[k]&.first
22
+
23
+ def []=(k, v)
24
+ @observer.modify { _add(k, v) }
25
+ end
26
+
27
+ def set(k, v)
28
+ v = [v] unless v.is_a? Array
29
+ @observer.modify do
30
+ @pairs[k] = []
31
+ v.each { _add(k, _1) }
32
+ end
33
+ end
34
+
35
+ def merge!(other)
36
+ @observer.modify do
37
+ other.each do |k, v|
38
+ @pairs[k] ||= []
39
+ @pairs[k].append(*v)
40
+ end
41
+ @dirty = true
42
+ end
43
+ self
44
+ end
45
+
46
+ def replace!(other)
47
+ @observer.modify do
48
+ @pairs = {}
49
+ other.each do |k, v|
50
+ @pairs[k] ||= []
51
+ @pairs[k].append(*v)
52
+ end
53
+ @dirty = true
54
+ end
55
+ self
56
+ end
57
+
58
+ def key?(k) = @pairs.key?(k.to_s)
59
+ alias has_key? key?
60
+
61
+ def each(&block) = tap { @pairs.each(&block) }
62
+ alias each_pair each
63
+
64
+ def keys = @pairs.keys
65
+ def each_key(&block) = tap { @pairs.keys(&block) }
66
+
67
+ def dirty? = @dirty
68
+
69
+ def encode
70
+ pairs = []
71
+ @pairs.each do |k, v|
72
+ v.each { pairs << [k, _1] }
73
+ end
74
+ keys = IO::Buffer.new
75
+ values = IO::Buffer.new
76
+ pairs.each do |p|
77
+ keys.write_raw(encode_string(p.first))
78
+ values.write_raw(encode_bytes(p.last))
79
+ end
80
+
81
+ @dirty = false
82
+ IO::Buffer.new
83
+ .write_raw(encode_uint16(pairs.length))
84
+ .write_raw(keys.string)
85
+ .write_raw(values.string)
86
+ .string
87
+ end
88
+
89
+ def decode(data)
90
+ len = decode_uint16(data)
91
+ keys = []
92
+ values = []
93
+
94
+ len.times { keys << decode_string(data) }
95
+ len.times { values << decode_bytes(data) }
96
+
97
+ @pairs = {}
98
+ keys.zip(values).each { |pair| _add(*pair) }
99
+ @dirty = false
100
+
101
+ self
102
+ end
103
+
104
+ private
105
+
106
+ def _add(k, v)
107
+ k = k.to_s
108
+ v = case v
109
+ when String then v
110
+ when StringIO then v.string
111
+ else v.to_s
112
+ end
113
+
114
+ @pairs[k] ||= []
115
+ @pairs[k] << v
116
+ @dirty = true
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arf
4
+ module RPC
5
+ class MethodMeta
6
+ Streamer = Arf::Types::Streamer
7
+ InOutStream = Arf::Types::InOutStream
8
+
9
+ ANY_STREAMER = ->(v) { v.is_a?(Streamer) || v.is_a?(InOutStream) }
10
+ NOT_STREAMER = ->(v) { !v.is_a?(Streamer) && !v.is_a?(InOutStream) }
11
+
12
+ def initialize(inputs, outputs)
13
+ @inputs = inputs || {}
14
+ @outputs = outputs || []
15
+ end
16
+
17
+ def output_stream = @outputs.find(&ANY_STREAMER)
18
+ def input_stream = @inputs.values.find(&ANY_STREAMER)
19
+ def output_stream? = !output_stream.nil?
20
+ def input_stream? = !input_stream.nil?
21
+ def output? = @outputs.any?(&NOT_STREAMER)
22
+ def inputs? = @inputs.values.any?(&NOT_STREAMER)
23
+ def output_types = @outputs.filter(&NOT_STREAMER)
24
+
25
+ def coerce_result(value, resolver)
26
+ return [] if value.nil?
27
+
28
+ [value].flatten.map.with_index do |v, idx|
29
+ expected_type = output_types[idx]
30
+ case expected_type
31
+ when String
32
+ Arf::Types.coerce_value(v, resolver.find_type(expected_type))
33
+ when Arf::Types::BaseType
34
+ expected_type.coerce(v)
35
+ else
36
+ Arf::Types.coerce_value(v, expected_type)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arf
4
+ module RPC
5
+ class Request < BaseMessage
6
+ kind :request
7
+ has_streaming
8
+ has_metadata
9
+ attr_accessor :service, :method, :params
10
+
11
+ def encode
12
+ flags = 0x00
13
+ flags |= (0x01 << 0x00) if streaming?
14
+
15
+ params = @params.map { Proto.encode(_1) }
16
+
17
+ IO::Buffer.new
18
+ .write_raw(encode_string(@service))
19
+ .write_raw(encode_string(@method))
20
+ .write(flags)
21
+ .write_raw(@metadata.encode)
22
+ .write_raw(encode_uint16(params.length))
23
+ .write_raw(params.join)
24
+ .string
25
+ end
26
+
27
+ def decode(data)
28
+ @service = decode_string(data)
29
+ @method = decode_string(data)
30
+ flags = data.readbyte
31
+ @streaming = !flags.nobits?((0x01 << 0x00))
32
+ @metadata = Metadata.new.decode(data)
33
+ params_len = decode_uint16(data)
34
+ @params = []
35
+ params_len.times { @params << Proto.decode(data) }
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,186 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arf
4
+ module RPC
5
+ class Responder
6
+ def initialize(str, input_stream_type, output_stream_type)
7
+ @metadata = Metadata.new
8
+ @stream = str
9
+
10
+ @response = nil
11
+ @response_error = nil
12
+ @response_lock = Mutex.new
13
+ @response_cond = Thread::ConditionVariable.new
14
+
15
+ @input_stream_type = output_stream_type
16
+ @has_input_stream = !output_stream_type.nil?
17
+ @input_stream_started = false
18
+ @input_stream_error = nil
19
+ @input_stream_completed = false
20
+ @input_stream_items = Queue.new
21
+ @input_stream_closed_lock = Mutex.new
22
+ @input_stream_closed = false
23
+
24
+ @output_stream_type = input_stream_type
25
+ @has_output_stream = !input_stream_type.nil?
26
+ @output_stream_started_lock = Mutex.new
27
+ @output_stream_started = false
28
+ @output_stream_error = nil
29
+ @output_stream_closed_lock = Mutex.new
30
+ @output_stream_closed = false
31
+
32
+ str.attach_handler(self)
33
+ end
34
+
35
+ def handle_data(value)
36
+ msg = BaseMessage.initialize_from(value)
37
+ case msg
38
+ when Response
39
+ @response_lock.synchronize do
40
+ @response = msg
41
+ @metadata.merge!(msg.metadata)
42
+ if @response.status == :ok
43
+ @params = msg.params
44
+ else
45
+ @response_error = Status::BadStatus.new(
46
+ @response.status,
47
+ @response.metadata.get("arf-status-description")
48
+ )
49
+ end
50
+ @response_cond.broadcast
51
+ end
52
+
53
+ when StartStream
54
+ @input_stream_started = true
55
+
56
+ when EndStream
57
+ @input_stream_completed = true
58
+ @input_stream_items.close
59
+
60
+ when StreamMetadata
61
+ @metadata.replace!(msg.metadata)
62
+
63
+ when StreamItem
64
+ return if @input_stream_completed
65
+
66
+ @input_stream_closed_lock.synchronize do
67
+ return if @input_stream_closed
68
+ end
69
+ @input_stream_items << msg.value
70
+ end
71
+ end
72
+
73
+ def push(value, **kwargs)
74
+ raise ArgumentError, "#push receives either a value or kwargs, not both." if value && !kwargs.empty?
75
+
76
+ raise RPC::NoStreamError, false unless @has_output_stream
77
+ raise @output_stream_error if @output_stream_error
78
+
79
+ # TODO: Warn? Error?
80
+ return if @output_stream_closed
81
+
82
+ @output_stream_closed_lock.synchronize do
83
+ return if @output_stream_closed
84
+ end
85
+
86
+ @output_stream_started_lock.synchronize do
87
+ next if @output_stream_started
88
+
89
+ @stream.write_data(BaseMessage.encode(StartStream.new))
90
+ @output_stream_started = true
91
+ end
92
+
93
+ @stream.write_data(BaseMessage.encode(StreamItem.new(value:)))
94
+ value
95
+ end
96
+
97
+ alias << push
98
+
99
+ def recv
100
+ raise RPC::NoStreamError, true unless @has_input_stream
101
+ raise @input_stream_error if @input_stream_error
102
+
103
+ @input_stream_items.pop
104
+ end
105
+
106
+ def each
107
+ loop do
108
+ item = recv
109
+ break if item.nil?
110
+
111
+ yield item
112
+ rescue StopIteration
113
+ break
114
+ end
115
+ self
116
+ end
117
+
118
+ def close_send
119
+ return if @output_stream_closed
120
+
121
+ @output_stream_closed_lock.synchronize do
122
+ @output_stream_closed = true
123
+ @stream.write_data(BaseMessage.encode(EndStream.new))
124
+ end
125
+ end
126
+
127
+ def close_recv
128
+ return if @input_stream_closed
129
+
130
+ @input_stream_closed_lock.synchronize do
131
+ @input_stream_closed = true
132
+ @input_stream_items.close
133
+ end
134
+ end
135
+
136
+ def _wait_response(throw_error: true)
137
+ raise @response_error if @response_error && throw_error
138
+
139
+ return if @response
140
+
141
+ catch :break_loop do
142
+ loop do
143
+ @response_lock.synchronize do
144
+ @response_cond.wait(@response_lock)
145
+ next if @response.nil?
146
+
147
+ throw :break_loop
148
+ end
149
+ end
150
+ end
151
+ end
152
+
153
+ def params
154
+ _wait_response
155
+ normalize_response_params
156
+ end
157
+
158
+ def normalize_response_params
159
+ if @response.params.empty?
160
+ nil
161
+ elsif @response.params.length == 1
162
+ @response.params.first
163
+ else
164
+ @response.params
165
+ end
166
+ end
167
+
168
+ def metadata
169
+ # It makes little sense to access metadata before a response is
170
+ # received. Given that:
171
+ _wait_response
172
+
173
+ @metadata
174
+ end
175
+
176
+ def status
177
+ _wait_response(throw_error: false)
178
+ @response.status
179
+ end
180
+
181
+ def success?
182
+ status == :ok
183
+ end
184
+ end
185
+ end
186
+ end