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,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