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,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arf
4
+ module RPC
5
+ class Response < BaseMessage
6
+ kind :response
7
+ has_status
8
+ has_metadata
9
+ has_streaming
10
+ attr_accessor :params
11
+
12
+ def encode
13
+ flags = 0x00
14
+ flags |= (0x01 << 0x00) if streaming?
15
+
16
+ params = @params.map { Proto.encode(_1) }
17
+
18
+ IO::Buffer.new
19
+ .write_raw(encode_uint16(@status))
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 ok? = status == :ok
28
+
29
+ def decode(data)
30
+ @status = decode_uint16(data)
31
+ flags = data.readbyte
32
+ @streaming = !flags.nobits?((0x01 << 0x00))
33
+ @metadata = Metadata.new.decode(data)
34
+ len = decode_uint16(data)
35
+ @params = []
36
+ len.times { @params << Proto.decode(data) }
37
+ end
38
+
39
+ def result
40
+ return @params if status == :ok
41
+
42
+ raise Status::BadStatus.new(@status, @metadata.get("arf-status-description"))
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,137 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arf
4
+ module RPC
5
+ class ServiceBase
6
+ include Arf::Types::Mixin
7
+
8
+ class YieldWithoutRespondError < Arf::Error
9
+ def initialize
10
+ super("This RPC method has response fields that must be sent before " \
11
+ "starting a stream. Please invoke #respond with the response " \
12
+ "parameters before yielding a value to the stream.")
13
+ end
14
+ end
15
+
16
+ class YieldWithoutStreamError < Arf::Error
17
+ def initialize
18
+ super("This RPC method does not have an output stream and cannot " \
19
+ "yield values.")
20
+ end
21
+ end
22
+
23
+ class YieldOnClosedStream < Arf::Error
24
+ def initialize
25
+ super("Cannot call yield after closing the output stream.")
26
+ end
27
+ end
28
+
29
+ def self.register(id, cls)
30
+ @services ||= {}
31
+ @services[id] = cls
32
+ end
33
+
34
+ def self.by_id(id)
35
+ @services ||= {}
36
+ @services[id]
37
+ end
38
+
39
+ def self.arf_service_id(id)
40
+ @arf_service_id = id
41
+ ::Arf::RPC::ServiceBase.register(id, self)
42
+ end
43
+
44
+ def self.respond_to_rpc?(name)
45
+ return true if @service_methods&.key?(name.to_sym)
46
+
47
+ if superclass != Arf::RPC::ServiceBase && superclass.respond_to?(:respond_to_rpc?)
48
+ return superclass.respond_to_rpc?(name)
49
+ end
50
+
51
+ false
52
+ end
53
+
54
+ def self.rpc_method_meta(name)
55
+ name = name.to_sym unless name.is_a? Symbol
56
+ return @service_methods[name] if @service_methods&.key?(name)
57
+
58
+ if superclass != Arf::RPC::ServiceBase && superclass.respond_to?(:rpc_method_meta)
59
+ return superclass.rpc_method_meta(name)
60
+ end
61
+
62
+ nil
63
+ end
64
+
65
+ def self.rpc(name, inputs: nil, outputs: nil)
66
+ inputs ||= {}
67
+ outputs = [outputs].compact unless outputs.is_a? Array
68
+
69
+ @service_methods ||= {}
70
+ @service_methods[name] = MethodMeta.new(inputs, outputs)
71
+ define_method(name) { unimplemented! }
72
+ end
73
+
74
+ attr_reader :log, :request
75
+
76
+ def arf_execute_request(ctx)
77
+ @context = ctx
78
+ @request = ctx.request
79
+ @metadata = @request.metadata
80
+ @service = @request.service
81
+ @method = @request.method
82
+ @log = ctx.log
83
+ @method_meta = self.class.rpc_method_meta(@method)
84
+ needs_response = @method_meta.output?
85
+ @context.prepare
86
+ if (str = @method_meta.output_stream)
87
+ @context.has_send_stream = true
88
+ @context.send_stream_type = str
89
+ end
90
+
91
+ @context.response.metadata.attach_observer(self)
92
+
93
+ output_type = @method_meta.output_stream&.resolved_type(self.class)
94
+
95
+ result = nil
96
+ begin
97
+ result = send(@method, *@request.params) do |val|
98
+ raise YieldWithoutStreamError unless ctx.has_send_stream
99
+ raise YieldWithoutRespondError if !ctx.has_sent_response && needs_response
100
+ raise YieldOnClosedStream if ctx.send_stream_finished
101
+
102
+ ctx.stream_send(Arf::Types.coerce_value(val, output_type))
103
+ nil
104
+ end
105
+ rescue Exception => e
106
+ @log.error("Failed executing ##{@method}", e)
107
+ error!
108
+ end
109
+ @log.debug("Finished executing request", method: @method, result:)
110
+ result
111
+ end
112
+
113
+ def observer_changed(_observer)
114
+ # We just have a single observer, it will be response metadata.
115
+ return unless ctx.send_stream_started && !ctx.send_stream_finished
116
+
117
+ ctx.stream_send_metadata
118
+ end
119
+
120
+ def recv = @context.recv
121
+
122
+ def set_meta(k, v) = @context.response.metadata.set(k, v)
123
+ def add_meta(k, v) = @context.response.metadata.add(k, v)
124
+
125
+ def respond(value, code: nil)
126
+ code ||= :ok
127
+
128
+ @context.response.params = @method_meta.coerce_result(value, self.class)
129
+ @context.response.status = code
130
+ @context.send_response
131
+ end
132
+
133
+ def unimplemented! = raise Status::BadStatus, :unimplemented
134
+ def error! = raise Status::BadStatus, :internal_error
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arf
4
+ module RPC
5
+ class StartStream < BaseMessage
6
+ kind :start_stream
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arf
4
+ module RPC
5
+ class StreamError < BaseMessage
6
+ kind :stream_error
7
+ has_status
8
+ has_metadata
9
+
10
+ def encode
11
+ IO::Buffer.new
12
+ .write_raw(encode_uint16(@status))
13
+ .write_raw(metadata.encode)
14
+ .string
15
+ end
16
+
17
+ def decode(data)
18
+ @status = decode_uint16(data)
19
+ @metadata = Metadata.new.decode(data)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arf
4
+ module RPC
5
+ class StreamItem < BaseMessage
6
+ kind :stream_item
7
+ attr_accessor :value
8
+
9
+ def encode = Proto.encode(@value)
10
+
11
+ def decode(data)
12
+ @value = Proto.decode(data)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arf
4
+ module RPC
5
+ class StreamMetadata < BaseMessage
6
+ kind :stream_metadata
7
+ has_metadata
8
+
9
+ def encode = metadata.encode
10
+
11
+ def decode(data)
12
+ @metadata = Metadata.new.decode(data)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,255 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arf
4
+ module RPC
5
+ class Struct
6
+ include Arf::Types::Mixin
7
+
8
+ def self.arf_struct_id(v = nil)
9
+ return @arf_struct_id if v.nil?
10
+
11
+ @arf_struct_id = v
12
+ Arf::Proto::Registry.register! self
13
+ v
14
+ end
15
+
16
+ def self.validate_subclass(type, v)
17
+ cls = find_type(type)
18
+ if cls.ancestors.include?(Arf::RPC::Enum)
19
+ !cls.to_i(v).nil?
20
+ else
21
+ v.is_a?(cls) || v.is_a?(Hash)
22
+ end
23
+ end
24
+
25
+ def self.validator_for_type(type, optional:)
26
+ validator = if ::Arf::Types::INTEGERS.include?(type)
27
+ ->(v) { v.is_a?(Float) || v.is_a?(Integer) }
28
+ elsif ::Arf::Types::FLOATS.include?(type)
29
+ ->(v) { v.is_a?(Float) || v.is_a?(Integer) }
30
+ elsif type == :string
31
+ ->(v) { v.is_a?(String) }
32
+ elsif type == :bool
33
+ ->(v) { v.is_a?(TrueClass) || v.is_a?(FalseClass) }
34
+ elsif type == :bytes
35
+ ->(v) { v.is_a?(StringIO) || v.is_a?(String) }
36
+ elsif type.is_a? MapType
37
+ ->(v) { v.is_a? Hash }
38
+ elsif type.is_a? ArrayType
39
+ ->(v) { v.is_a? Array }
40
+ elsif type.is_a? String
41
+ # Validation for nested classes must be done at time of assignment
42
+ # since we can't rely on it being available during the field's
43
+ # definition.
44
+ ->(v) { validate_subclass(type, v) }
45
+ elsif type.ancestors.include?(Arf::RPC::Enum)
46
+ ->(v) { !type.to_i(v).nil? }
47
+ else
48
+ raise ArgumentError, "Invalid type #{type.inspect} (#{type.class.name})"
49
+ end
50
+
51
+ if optional
52
+ ->(v) { v.nil? || validator.call(v) }
53
+ else
54
+ validator
55
+ end
56
+ end
57
+
58
+ def self.default_for_type(type)
59
+ if ::Arf::Types::INTEGERS.include?(type)
60
+ 0
61
+ elsif ::Arf::Types::FLOATS.include?(type)
62
+ 0.0
63
+ elsif type == :string
64
+ ""
65
+ elsif type == :bool
66
+ false
67
+ elsif type == :bytes
68
+ ""
69
+ elsif type.is_a? MapType
70
+ {}
71
+ elsif type.is_a? ArrayType
72
+ []
73
+ elsif type.is_a? String
74
+ # Validation for nested classes must be done at time of assignment
75
+ # since we can't rely on it being available during the field's
76
+ # definition.
77
+ cls = find_type(type)
78
+ if cls.ancestors.include?(Arf::RPC::Enum)
79
+ default_for_type(cls)
80
+ else
81
+ cls.new
82
+ end
83
+ elsif type.ancestors.include?(Arf::RPC::Enum)
84
+ type.options.keys.first
85
+ else
86
+ raise ArgumentError, "Invalid type #{type.inspect} (#{type.class.name})"
87
+ end
88
+ end
89
+
90
+ def self.field(id, name, type, optional: false)
91
+ @fields ||= []
92
+ @fields << { id:, name: name.to_s, type:, optional: }
93
+ attr_reader(name)
94
+
95
+ validator = validator_for_type(type, optional:)
96
+ define_method("#{name}=") do |v|
97
+ unless validator.call(v)
98
+ raise ArgumentError, "Invalid value for #{self.class.name}.#{name}, type #{type}: #{v.inspect}"
99
+ end
100
+
101
+ v = _coerce(v, type)
102
+
103
+ instance_variable_set("@__arf_union_set_id", id) if union? && !@skip_union_setter
104
+ instance_variable_set("@#{name}", v)
105
+ end
106
+ end
107
+
108
+ def self.all_fields
109
+ @all_fields ||= @fields
110
+ .map do |v|
111
+ next v unless v[:id] == :union
112
+
113
+ find_type(v[:type])
114
+ .all_fields
115
+ .map { _1.merge(via: v[:name]) }
116
+ end
117
+ .flatten
118
+ end
119
+
120
+ def self.fields = @fields || []
121
+ def self.field_by_id(id) = @fields.find { _1[:id] == id }
122
+ def self.field_by_name(name) = name.to_s.then { |n| @fields.find { _1[:name] == n } }
123
+
124
+ def self.union!
125
+ @union = true
126
+ attr_reader(:__arf_union_set_id)
127
+ end
128
+
129
+ def self.union? = @union || false
130
+
131
+ def arf_struct_id = self.class.arf_struct_id
132
+ def union? = self.class.union?
133
+
134
+ def initialize(**kwargs)
135
+ cls = self.class
136
+ kwargs.each_pair do |k, v|
137
+ field = cls.field_by_name(k)
138
+ raise ArgumentError, "#{cls.name}: Unknown field #{k}" unless field
139
+
140
+ v = _coerce(v, field[:type])
141
+ send("#{k}=", v)
142
+ end
143
+
144
+ @skip_union_setter = true
145
+ # Initialize missing variables
146
+ cls.fields.each do |f|
147
+ next if f[:optional]
148
+
149
+ send("#{f[:name]}=", cls.default_for_type(f[:type])) if instance_variable_get("@#{f[:name]}").nil?
150
+ end
151
+
152
+ if union? && __arf_union_set_id.nil?
153
+ @skip_union_setter = false
154
+ # Set the first field
155
+ f = cls.fields.first
156
+ send("#{f[:name]}=", cls.default_for_type(f[:type]))
157
+ end
158
+ ensure
159
+ @skip_union_setter = false
160
+ end
161
+
162
+ def _coerce(value, type)
163
+ cls = self.class
164
+ case type
165
+ when String
166
+ type = cls.find_type(type)
167
+ if type.ancestors.include?(Arf::RPC::Enum)
168
+ type.to_sym(value)
169
+ else
170
+ case value
171
+ when Hash then type.new(**value)
172
+ when type then value
173
+ else
174
+ raise ArgumentError,
175
+ "Cannot initialize #{type} with #{value.inspect} (#{value.class}). Did you mean to use a Hash?"
176
+ end
177
+ end
178
+ when ArrayType
179
+ type.coerce(value)
180
+ when MapType
181
+ type.coerce(value)
182
+ else
183
+ ::Arf::Types.coerce_value(value, type)
184
+ end
185
+ end
186
+
187
+ def decode_fields(fields)
188
+ fields.each_pair do |id, value|
189
+ meta = self.class.all_fields.find { _1[:id] == id }
190
+ unless meta
191
+ puts "WARNING: Skipping field ID #{id} without matching ID on target type #{self.class.name}"
192
+ next
193
+ end
194
+ if meta[:via]
195
+ union_field = self.class.all_fields.find { _1[:id] == value[:id] }
196
+ instance_variable_get("@#{meta[:via]}")
197
+ .send("#{union_field[:name]}=", value[:value])
198
+ next
199
+ end
200
+
201
+ next if meta[:optional] && value.nil?
202
+
203
+ instance_variable_set("@#{meta[:name]}", _coerce(value, meta[:type]))
204
+ end
205
+ self
206
+ end
207
+
208
+ def ==(other)
209
+ return false unless other.is_a? self.class
210
+
211
+ return eq_union(other) if union?
212
+
213
+ eq_struct(other)
214
+ end
215
+
216
+ def eq_struct(other)
217
+ self.class.fields.each do |v|
218
+ return false unless send(v[:name]) == other.send(v[:name])
219
+ end
220
+ true
221
+ end
222
+
223
+ def eq_union(other)
224
+ return false unless other.__arf_union_set_id == __arf_union_set_id
225
+
226
+ self.class.fields.each do |v|
227
+ return false if instance_variable_get("@#{v[:name]}") != other.instance_variable_get("@#{v[:name]}")
228
+ end
229
+
230
+ true
231
+ end
232
+
233
+ def hashify(value)
234
+ case value
235
+ when nil then nil
236
+ when Arf::RPC::Struct then value.to_h
237
+ when Array then value.map { hashify(_1) }
238
+ when Hash then value.transform_values { |v| hashify(v) }
239
+ else value
240
+ end
241
+ end
242
+
243
+ def to_h
244
+ if union?
245
+ set = self.class.fields.find { _1[:id] == __arf_union_set_id }
246
+ return { set[:name] => hashify(instance_variable_get("@#{set[:name]}")) }
247
+ end
248
+
249
+ self.class.fields
250
+ .to_h { |f| [f[:name], hashify(instance_variable_get("@#{f[:name]}"))] }
251
+ .compact
252
+ end
253
+ end
254
+ end
255
+ end
data/lib/arf/rpc.rb ADDED
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "rpc/struct"
4
+ require_relative "rpc/enum"
5
+ require_relative "rpc/message_kind"
6
+ require_relative "rpc/base_message"
7
+ require_relative "rpc/metadata"
8
+ require_relative "rpc/request"
9
+ require_relative "rpc/response"
10
+ require_relative "rpc/start_stream"
11
+ require_relative "rpc/end_stream"
12
+ require_relative "rpc/stream_error"
13
+ require_relative "rpc/stream_item"
14
+ require_relative "rpc/stream_metadata"
15
+ require_relative "rpc/method_meta"
16
+
17
+ require_relative "rpc/service_base"
18
+ require_relative "rpc/client_base"
19
+ require_relative "rpc/responder"
data/lib/arf/server.rb ADDED
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arf
4
+ class Server
5
+ def self.pseudo_uuid
6
+ hostname = Socket.gethostname
7
+ lambda do
8
+ "#{hostname}-#{SecureRandom.hex(8)}"
9
+ end
10
+ end
11
+
12
+ def default_options
13
+ @default_options ||= {
14
+ max_concurrent_streams: 0,
15
+ logger: Arf.configuration.logger,
16
+ id_generator: Server.pseudo_uuid
17
+ }.merge
18
+ end
19
+
20
+ def initialize(**opts)
21
+ opts = default_options.merge(opts)
22
+ opts[:id_generator] ||= Server.pseudo_uuid
23
+ @logger = opts[:logger]
24
+ @id_generator = opts[:id_generator]
25
+ @max_concurrent_streams = opts[:max_concurrent_streams]
26
+ @server = Arf::Wire::Server.new(self)
27
+ @interceptors = []
28
+ end
29
+
30
+ def register_interceptor(callable, &block)
31
+ callable ||= block
32
+ @interceptors << callable
33
+ end
34
+
35
+ def run = @server.run
36
+ def shutdown = @server.shutdown
37
+
38
+ def handle_stream(stream)
39
+ req_id = @id_generator.call
40
+ stream.external_id = req_id
41
+ log = @logger.with_fields(request_id: req_id)
42
+ log.debug("Servicing stream")
43
+ ctx = Context.new(req_id, log, stream)
44
+ log.debug("Reading request...", thread: Thread.current.name)
45
+ msg = RPC::BaseMessage.initialize_from(stream.read_blocking)
46
+ unless msg.is_a? RPC::Request
47
+ log.info("Rejecting stream as it does not start with a Request frame")
48
+ stream.write_data(RPC::BaseMessage.encode(RPC::Response.new(
49
+ status: :failed_precondition,
50
+ metadata: {
51
+ "arf-request-id" => req_id,
52
+ "arf-status-description" => "Missing Request frame"
53
+ }
54
+ )))
55
+ stream.close_local
56
+ return
57
+ end
58
+
59
+ log.debug("Request read OK")
60
+ ctx.request = msg
61
+ # chain interceptors
62
+ call_next(0, ctx)
63
+ handle_request(ctx)
64
+ end
65
+
66
+ def handle_request(ctx)
67
+ base_svc = Arf::RPC::ServiceBase.by_id(ctx.request.service)
68
+ svc = base_svc&.subclasses&.first
69
+
70
+ if svc.nil?
71
+ ctx.log.info("Rejecting request as there's no service registered with the requested name",
72
+ service: ctx.request.service, method: ctx.request.method)
73
+ failure(ctx, :unimplemented)
74
+ return false
75
+ end
76
+
77
+ unless svc.respond_to_rpc?(ctx.request.method)
78
+ ctx.log.info("Rejecting request as service does not respond to the requested method",
79
+ service: ctx.request.service, method: ctx.request.method)
80
+ failure(ctx, :unimplemented)
81
+ return false
82
+ end
83
+
84
+ ctx.log.debug("Handler got request")
85
+
86
+ svc_inst = svc.new
87
+
88
+ begin
89
+ result = svc_inst.arf_execute_request(ctx)
90
+ rescue Status::BadStatus => e
91
+ failure(ctx, e.code, e.message)
92
+ return false
93
+ rescue Exception => e
94
+ ctx.log.error("Failed invoking method #{ctx.request.method}", e)
95
+ failure(ctx, :internal_error)
96
+ return false
97
+ end
98
+
99
+ ctx.end_send if ctx.has_send_stream
100
+ svc_inst.respond(result) unless ctx.has_sent_response
101
+ true
102
+ end
103
+
104
+ def failure(ctx, status, message = nil)
105
+ resp = RPC::Response.new(
106
+ status: Status::FROM_SYMBOL[status],
107
+ metadata: {
108
+ "arf-status-description" => message || Status::STATUS_TEXT[status]
109
+ }
110
+ )
111
+ ctx.stream.write_data(RPC::BaseMessage.encode(resp), end_stream: true)
112
+ ctx.has_sent_response = true
113
+ end
114
+
115
+ def cancel_stream(stream); end
116
+
117
+ def call_next(index, ctx)
118
+ return unless index < @interceptors.size
119
+
120
+ @interceptors[index].call(ctx) { call_next(index + 1, ctx) }
121
+ end
122
+ end
123
+ end