arf 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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