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.
- checksums.yaml +7 -0
- data/.ruby-version +1 -0
- data/LICENSE.txt +21 -0
- data/Rakefile +12 -0
- data/lib/arf/configuration.rb +67 -0
- data/lib/arf/context.rb +170 -0
- data/lib/arf/errors.rb +10 -0
- data/lib/arf/io/buffer.rb +25 -0
- data/lib/arf/io/compression.rb +44 -0
- data/lib/arf/io/limit_reader.rb +30 -0
- data/lib/arf/observer.rb +29 -0
- data/lib/arf/proto/array.rb +31 -0
- data/lib/arf/proto/boolean.rb +15 -0
- data/lib/arf/proto/bytes.rb +35 -0
- data/lib/arf/proto/decoder.rb +31 -0
- data/lib/arf/proto/encoder.rb +71 -0
- data/lib/arf/proto/float.rb +48 -0
- data/lib/arf/proto/map.rb +49 -0
- data/lib/arf/proto/registry.rb +27 -0
- data/lib/arf/proto/scalar.rb +55 -0
- data/lib/arf/proto/string.rb +36 -0
- data/lib/arf/proto/struct.rb +84 -0
- data/lib/arf/proto/types.rb +67 -0
- data/lib/arf/proto/union.rb +25 -0
- data/lib/arf/proto.rb +17 -0
- data/lib/arf/reactor.rb +270 -0
- data/lib/arf/rpc/base_message.rb +119 -0
- data/lib/arf/rpc/client_base.rb +110 -0
- data/lib/arf/rpc/end_stream.rb +9 -0
- data/lib/arf/rpc/enum.rb +51 -0
- data/lib/arf/rpc/message_kind.rb +27 -0
- data/lib/arf/rpc/metadata.rb +120 -0
- data/lib/arf/rpc/method_meta.rb +42 -0
- data/lib/arf/rpc/request.rb +39 -0
- data/lib/arf/rpc/responder.rb +186 -0
- data/lib/arf/rpc/response.rb +46 -0
- data/lib/arf/rpc/service_base.rb +137 -0
- data/lib/arf/rpc/start_stream.rb +9 -0
- data/lib/arf/rpc/stream_error.rb +23 -0
- data/lib/arf/rpc/stream_item.rb +16 -0
- data/lib/arf/rpc/stream_metadata.rb +16 -0
- data/lib/arf/rpc/struct.rb +255 -0
- data/lib/arf/rpc.rb +19 -0
- data/lib/arf/server.rb +123 -0
- data/lib/arf/status.rb +75 -0
- data/lib/arf/types/array_type.rb +24 -0
- data/lib/arf/types/base_type.rb +14 -0
- data/lib/arf/types/coercion.rb +36 -0
- data/lib/arf/types/in_out_stream.rb +28 -0
- data/lib/arf/types/input_stream.rb +8 -0
- data/lib/arf/types/map_type.rb +32 -0
- data/lib/arf/types/mixin.rb +29 -0
- data/lib/arf/types/output_stream.rb +8 -0
- data/lib/arf/types/streamer.rb +21 -0
- data/lib/arf/types.rb +69 -0
- data/lib/arf/version.rb +5 -0
- data/lib/arf/wire/base_connection.rb +177 -0
- data/lib/arf/wire/client.rb +101 -0
- data/lib/arf/wire/encoding.rb +49 -0
- data/lib/arf/wire/error_code.rb +35 -0
- data/lib/arf/wire/errors.rb +88 -0
- data/lib/arf/wire/frame.rb +111 -0
- data/lib/arf/wire/frame_kind.rb +23 -0
- data/lib/arf/wire/frame_reader.rb +104 -0
- data/lib/arf/wire/frames/base_frame.rb +108 -0
- data/lib/arf/wire/frames/configuration_frame.rb +33 -0
- data/lib/arf/wire/frames/data_frame.rb +21 -0
- data/lib/arf/wire/frames/go_away_frame.rb +29 -0
- data/lib/arf/wire/frames/make_stream_frame.rb +15 -0
- data/lib/arf/wire/frames/ping_frame.rb +18 -0
- data/lib/arf/wire/frames/reset_stream_frame.rb +19 -0
- data/lib/arf/wire/frames.rb +9 -0
- data/lib/arf/wire/server/peer.rb +85 -0
- data/lib/arf/wire/server.rb +63 -0
- data/lib/arf/wire/stream/state.rb +69 -0
- data/lib/arf/wire/stream.rb +128 -0
- data/lib/arf/wire/wait_signal.rb +24 -0
- data/lib/arf/wire.rb +14 -0
- data/lib/arf.rb +46 -0
- metadata +195 -0
data/lib/arf/status.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Arf
|
4
|
+
module Status
|
5
|
+
OK = 0
|
6
|
+
CANCELLED = 1
|
7
|
+
UNKNOWN = 2
|
8
|
+
INVALID_ARGUMENT = 3
|
9
|
+
DEADLINE_EXCEEDED = 4
|
10
|
+
NOT_FOUND = 5
|
11
|
+
ALREADY_EXISTS = 6
|
12
|
+
PERMISSION_DENIED = 7
|
13
|
+
RESOURCE_EXHAUSTED = 8
|
14
|
+
FAILED_PRECONDITION = 9
|
15
|
+
ABORTED = 10
|
16
|
+
OUT_OF_RANGE = 11
|
17
|
+
UNIMPLEMENTED = 12
|
18
|
+
INTERNAL_ERROR = 13
|
19
|
+
UNAVAILABLE = 14
|
20
|
+
DATA_LOSS = 15
|
21
|
+
UNAUTHENTICATED = 16
|
22
|
+
|
23
|
+
TO_SYMBOL = {
|
24
|
+
OK => :ok,
|
25
|
+
CANCELLED => :cancelled,
|
26
|
+
UNKNOWN => :unknown,
|
27
|
+
INVALID_ARGUMENT => :invalid_argument,
|
28
|
+
DEADLINE_EXCEEDED => :deadline_exceeded,
|
29
|
+
NOT_FOUND => :not_found,
|
30
|
+
ALREADY_EXISTS => :already_exists,
|
31
|
+
PERMISSION_DENIED => :permission_denied,
|
32
|
+
RESOURCE_EXHAUSTED => :resource_exhausted,
|
33
|
+
FAILED_PRECONDITION => :failed_precondition,
|
34
|
+
ABORTED => :aborted,
|
35
|
+
OUT_OF_RANGE => :out_of_range,
|
36
|
+
UNIMPLEMENTED => :unimplemented,
|
37
|
+
INTERNAL_ERROR => :internal_error,
|
38
|
+
UNAVAILABLE => :unavailable,
|
39
|
+
DATA_LOSS => :data_loss,
|
40
|
+
UNAUTHENTICATED => :unauthenticated
|
41
|
+
}.freeze
|
42
|
+
|
43
|
+
FROM_SYMBOL = TO_SYMBOL.to_a.to_h(&:reverse)
|
44
|
+
|
45
|
+
STATUS_TEXT = {
|
46
|
+
ok: "OK",
|
47
|
+
cancelled: "Cancelled",
|
48
|
+
unknown: "Unknown",
|
49
|
+
invalid_argument: "Invalid Argument",
|
50
|
+
deadline_exceeded: "Deadline Exceeded",
|
51
|
+
not_found: "Not Found",
|
52
|
+
already_exists: "Already Exists",
|
53
|
+
permission_denied: "Permission Denied",
|
54
|
+
resource_exhausted: "Resource Exhausted",
|
55
|
+
failed_precondition: "Failed Precondition",
|
56
|
+
aborted: "Aborted",
|
57
|
+
out_of_range: "Out of Range",
|
58
|
+
unimplemented: "Unimplemented",
|
59
|
+
internal_error: "Internal Error",
|
60
|
+
unavailable: "Unavailable",
|
61
|
+
data_loss: "Data Loss",
|
62
|
+
unauthenticated: "Unauthenticated"
|
63
|
+
}.freeze
|
64
|
+
|
65
|
+
class BadStatus < Arf::Error
|
66
|
+
attr_reader :code
|
67
|
+
|
68
|
+
def initialize(code, msg = nil)
|
69
|
+
@code = code.is_a?(Symbol) ? code : TO_SYMBOL[code]
|
70
|
+
@msg = msg || STATUS_TEXT[@code] || "Unknown status"
|
71
|
+
super(@msg)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Arf
|
4
|
+
module Types
|
5
|
+
class ArrayType < BaseType
|
6
|
+
attr_reader :type
|
7
|
+
|
8
|
+
def initialize(v)
|
9
|
+
super()
|
10
|
+
@type = v
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.[](v) = new(v)
|
14
|
+
|
15
|
+
def resolved_type
|
16
|
+
@resolved_type ||= resolve_type(@type)
|
17
|
+
end
|
18
|
+
|
19
|
+
def bind(to) = tap { @bind = to }
|
20
|
+
|
21
|
+
def coerce(val) = val.map { coerce_value(_1, resolved_type) }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Arf
|
4
|
+
module Types
|
5
|
+
class BaseType
|
6
|
+
def bind(to) = tap { @bind = to }
|
7
|
+
def coerce_value(*) = Arf::Types.coerce_value(*)
|
8
|
+
|
9
|
+
def resolve_type(type)
|
10
|
+
type.is_a?(String) ? @bind.find_type(type) : type
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Arf
|
4
|
+
module Types
|
5
|
+
def self.coerce_value(value, type)
|
6
|
+
if Types::INTEGERS.include?(type)
|
7
|
+
value.to_i
|
8
|
+
elsif Types::FLOATS.include?(type)
|
9
|
+
value.to_f
|
10
|
+
elsif type == :string
|
11
|
+
value.to_s
|
12
|
+
elsif type == :bytes
|
13
|
+
case value
|
14
|
+
when StringIO then value.string
|
15
|
+
when String then value
|
16
|
+
when Array then value.pack("C*")
|
17
|
+
else
|
18
|
+
raise ArgumentError, "Invalid type for bytes: #{i.class.name}"
|
19
|
+
end
|
20
|
+
elsif type == :bool
|
21
|
+
!!value
|
22
|
+
elsif type.is_a?(Class)
|
23
|
+
case value
|
24
|
+
when Hash then type.new(**value)
|
25
|
+
when type then value
|
26
|
+
else
|
27
|
+
raise ArgumentError,
|
28
|
+
"Cannot initialize #{type} with #{value.inspect} (#{value.class}). " \
|
29
|
+
"You may want to use an instance of #{type}, or a Hash"
|
30
|
+
end
|
31
|
+
elsif type.is_a?(Arf::Types::ArrayType) || type.is_a?(Arf::Types::MapType)
|
32
|
+
type.coerce(value)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Arf
|
4
|
+
module Types
|
5
|
+
class InOutStream
|
6
|
+
def self.[](input, output) = new(input, output)
|
7
|
+
|
8
|
+
attr_accessor :input, :output
|
9
|
+
|
10
|
+
def initialize(input, output)
|
11
|
+
@input = input
|
12
|
+
@output = output
|
13
|
+
end
|
14
|
+
|
15
|
+
def resolved_input(resolver)
|
16
|
+
return input if input.is_a? Symbol
|
17
|
+
|
18
|
+
@resolved_input ||= resolver.find_type(input)
|
19
|
+
end
|
20
|
+
|
21
|
+
def resolved_output(resolver)
|
22
|
+
return output if output.is_a? Symbol
|
23
|
+
|
24
|
+
@resolved_output ||= resolver.find_type(output)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Arf
|
4
|
+
module Types
|
5
|
+
class MapType < BaseType
|
6
|
+
attr_reader :key, :value
|
7
|
+
|
8
|
+
def initialize(k, v)
|
9
|
+
super()
|
10
|
+
@key = k
|
11
|
+
@value = v
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.[](k, v) = new(k, v)
|
15
|
+
|
16
|
+
def resolved_types
|
17
|
+
return @resolved_types if @resolved_types
|
18
|
+
|
19
|
+
key = resolve_type(@key)
|
20
|
+
value = resolve_type(@value)
|
21
|
+
@resolved_types = [key, value]
|
22
|
+
end
|
23
|
+
|
24
|
+
def coerce(val)
|
25
|
+
key_type, value_type = resolved_types
|
26
|
+
val
|
27
|
+
.transform_keys { coerce_value(_1, key_type) }
|
28
|
+
.transform_values { coerce_value(_1, value_type) }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Arf
|
4
|
+
module Types
|
5
|
+
module Mixin
|
6
|
+
MapType = ::Arf::Types::MapType
|
7
|
+
ArrayType = ::Arf::Types::ArrayType
|
8
|
+
InputStream = ::Arf::Types::InputStream
|
9
|
+
OutputStream = ::Arf::Types::OutputStream
|
10
|
+
InOutStream = ::Arf::Types::InOutStream
|
11
|
+
Streamer = ::Arf::Types::Streamer
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
def find_type(named)
|
15
|
+
components = named.split("::")
|
16
|
+
if components.first.empty?
|
17
|
+
# Look from the root, starting at Object
|
18
|
+
::Arf::Types.lookup_type(Object, components[1...], direction: :down)
|
19
|
+
else
|
20
|
+
# Look from local scope upwards
|
21
|
+
::Arf::Types.lookup_type(self, components, direction: :up)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.included(base) = base.extend(ClassMethods)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Arf
|
4
|
+
module Types
|
5
|
+
class Streamer
|
6
|
+
def self.[](type) = new(type)
|
7
|
+
|
8
|
+
attr_accessor :type
|
9
|
+
|
10
|
+
def initialize(type)
|
11
|
+
@type = type
|
12
|
+
end
|
13
|
+
|
14
|
+
def resolved_type(resolver)
|
15
|
+
return type if type.is_a? Symbol
|
16
|
+
|
17
|
+
@resolved_type ||= resolver.find_type(type)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/arf/types.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "types/coercion"
|
4
|
+
require_relative "types/base_type"
|
5
|
+
require_relative "types/array_type"
|
6
|
+
require_relative "types/map_type"
|
7
|
+
require_relative "types/streamer"
|
8
|
+
require_relative "types/in_out_stream"
|
9
|
+
require_relative "types/input_stream"
|
10
|
+
require_relative "types/output_stream"
|
11
|
+
require_relative "types/mixin"
|
12
|
+
|
13
|
+
module Arf
|
14
|
+
module Types
|
15
|
+
INTEGERS = %i[uint8 uint16 uint32 uint64 int8 int16 int32 int64].freeze
|
16
|
+
FLOATS = %i[float32 float64].freeze
|
17
|
+
OTHERS = %i[bool string bytes].freeze
|
18
|
+
|
19
|
+
def self.try_const_get(mod, name)
|
20
|
+
mod.const_get(name)
|
21
|
+
rescue NameError
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.lookup_type(mod, path, direction:)
|
26
|
+
key = [mod, path.join("::"), direction]
|
27
|
+
@lookup_cache ||= {}
|
28
|
+
return @lookup_cache[key] if @lookup_cache.key? key
|
29
|
+
|
30
|
+
v = if direction == :up
|
31
|
+
lookup_type_up(mod, path)
|
32
|
+
else
|
33
|
+
lookup_type_down(mod, path)
|
34
|
+
end
|
35
|
+
|
36
|
+
@lookup_cache[key] = v
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.lookup_type_up(mod, path)
|
40
|
+
return mod if path.empty?
|
41
|
+
|
42
|
+
key = path.first
|
43
|
+
v = try_const_get(mod, key)
|
44
|
+
if v
|
45
|
+
lookup_type_down(v, path.tap(&:shift))
|
46
|
+
elsif mod == Object
|
47
|
+
nil
|
48
|
+
else
|
49
|
+
parent = mod.name.split("::").tap(&:pop).last
|
50
|
+
return nil if parent.nil?
|
51
|
+
|
52
|
+
v = try_const_get(mod, parent)
|
53
|
+
return nil if v.nil?
|
54
|
+
|
55
|
+
lookup_type_down(v, path)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.lookup_type_down(mod, path)
|
60
|
+
return mod if path.empty?
|
61
|
+
|
62
|
+
key = path.first
|
63
|
+
v = try_const_get(mod, key)
|
64
|
+
return nil unless v
|
65
|
+
|
66
|
+
lookup_type_down(v, path.tap(&:shift))
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/lib/arf/version.rb
ADDED
@@ -0,0 +1,177 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Arf
|
4
|
+
module Wire
|
5
|
+
class BaseConnection
|
6
|
+
def initialize
|
7
|
+
@_write_lock = Monitor.new
|
8
|
+
@_write_buffer = Queue.new
|
9
|
+
@_write_head = nil
|
10
|
+
@compression = :none
|
11
|
+
@last_stream_id = 0
|
12
|
+
@reader = FrameReader.new
|
13
|
+
@configured = false
|
14
|
+
@pong_signal = WaitSignal.new
|
15
|
+
|
16
|
+
@streams = {}
|
17
|
+
@streams_monitor = Monitor.new
|
18
|
+
|
19
|
+
@configuration_ready_lock = Mutex.new
|
20
|
+
@configuration_ready_signal = Thread::ConditionVariable.new
|
21
|
+
@registered = false
|
22
|
+
end
|
23
|
+
|
24
|
+
def registered? = @registered
|
25
|
+
|
26
|
+
def registered!
|
27
|
+
@registered = true
|
28
|
+
@_write_lock.synchronize do
|
29
|
+
Arf::Reactor.client_writes_pending(self) unless @_write_buffer.empty?
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def wait_configuration
|
34
|
+
return if @configured
|
35
|
+
|
36
|
+
loop do
|
37
|
+
@configuration_ready_lock.synchronize do
|
38
|
+
@configuration_ready_signal.wait(@configuration_ready_lock)
|
39
|
+
return if @configured
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def recv(data)
|
45
|
+
data.each_byte do |b|
|
46
|
+
v = @reader.feed(b)
|
47
|
+
next unless v
|
48
|
+
|
49
|
+
handle_frame(v)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def handle_frame(raw_frame)
|
54
|
+
frame = raw_frame.specialize(@compression)
|
55
|
+
case frame
|
56
|
+
when ConfigurationFrame
|
57
|
+
handle_configuration(frame)
|
58
|
+
when PingFrame
|
59
|
+
handle_ping(frame)
|
60
|
+
when GoAwayFrame
|
61
|
+
handle_go_away(frame)
|
62
|
+
when MakeStreamFrame
|
63
|
+
handle_make_stream(frame)
|
64
|
+
when ResetStreamFrame
|
65
|
+
handle_reset_stream(frame)
|
66
|
+
when DataFrame
|
67
|
+
handle_data(frame)
|
68
|
+
else
|
69
|
+
protocol_error!
|
70
|
+
end
|
71
|
+
rescue UnknownFrameKindError, InvalidFrameLengthError
|
72
|
+
protocol_error!
|
73
|
+
end
|
74
|
+
|
75
|
+
def cancel_streams(code)
|
76
|
+
@streams_monitor.synchronize do
|
77
|
+
@streams.each_value do |v|
|
78
|
+
v.reset(code)
|
79
|
+
rescue ClosedStreamError, StreamResetError
|
80
|
+
next
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def ping
|
86
|
+
wait_configuration
|
87
|
+
@streams_monitor.synchronize do
|
88
|
+
dispatch_frame(PingFrame) do |p|
|
89
|
+
p.payload = SecureRandom.bytes(8)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def wait_pong = @pong_signal.wait
|
95
|
+
|
96
|
+
def fetch_stream(id) = @streams_monitor.synchronize { @streams[id] }
|
97
|
+
|
98
|
+
def arf_close_after_writing?
|
99
|
+
@arf_close_after_writing || false
|
100
|
+
end
|
101
|
+
|
102
|
+
def close_connection = Reactor.detach_client(self)
|
103
|
+
|
104
|
+
def close
|
105
|
+
close_connection
|
106
|
+
end
|
107
|
+
|
108
|
+
def close_connection_after_writing
|
109
|
+
@arf_close_after_writing = true
|
110
|
+
end
|
111
|
+
|
112
|
+
def send_data(data)
|
113
|
+
@_write_lock.synchronize do
|
114
|
+
@_write_buffer << data
|
115
|
+
end
|
116
|
+
Reactor.client_writes_pending(self) if registered?
|
117
|
+
data.bytesize
|
118
|
+
end
|
119
|
+
|
120
|
+
def handle_ping(fr)
|
121
|
+
return protocol_error! unless @configured
|
122
|
+
|
123
|
+
if fr.ack?
|
124
|
+
@pong_signal.broadcast
|
125
|
+
return
|
126
|
+
end
|
127
|
+
|
128
|
+
dispatch_frame(PingFrame) do |p|
|
129
|
+
p.ack!
|
130
|
+
p.payload = fr.payload
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def dispatch_frame(type, terminate: false, &)
|
135
|
+
dispatch(type.new(&))
|
136
|
+
close_connection_after_writing if terminate
|
137
|
+
end
|
138
|
+
|
139
|
+
def dispatch(frame)
|
140
|
+
send_data(frame.to_frame.bytes(@compression))
|
141
|
+
end
|
142
|
+
|
143
|
+
def protocol_error! = go_away!(ERROR_CODE_PROTOCOL_ERROR, terminate: true)
|
144
|
+
|
145
|
+
def go_away!(code, extra_data: nil, terminate: false)
|
146
|
+
dispatch_frame(GoAwayFrame, terminate:) do |g|
|
147
|
+
g.last_stream_id = @last_stream_id
|
148
|
+
g.error_code = code
|
149
|
+
g.additional_data = extra_data if extra_data
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def flush_write_buffer(io)
|
154
|
+
@_write_lock.synchronize do
|
155
|
+
loop do
|
156
|
+
if @_write_head.nil?
|
157
|
+
return true if @_write_buffer.empty?
|
158
|
+
|
159
|
+
@_write_head = @_write_buffer.pop
|
160
|
+
end
|
161
|
+
|
162
|
+
written = io.write_nonblock(@_write_head, exception: false)
|
163
|
+
case written
|
164
|
+
when :wait_writable
|
165
|
+
return false
|
166
|
+
when @_write_head.bytesize
|
167
|
+
@_write_head = nil
|
168
|
+
else
|
169
|
+
@_write_head = @_write_head.byteslice(written, @_write_head.bytesize)
|
170
|
+
return false
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Arf
|
4
|
+
module Wire
|
5
|
+
class Client < BaseConnection
|
6
|
+
def post_init
|
7
|
+
dispatch_frame(ConfigurationFrame) do |fr|
|
8
|
+
compression = Arf.config.client_compression
|
9
|
+
case compression
|
10
|
+
when :brotli
|
11
|
+
fr.compression_brotli!
|
12
|
+
when :gzip
|
13
|
+
fr.compression_gzip!
|
14
|
+
else
|
15
|
+
Arf.config.client_compression = :none
|
16
|
+
end
|
17
|
+
|
18
|
+
@compression = Arf.config.client_compression
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def handle_ping(fr)
|
23
|
+
return protocol_error! unless @configured
|
24
|
+
|
25
|
+
if fr.ack?
|
26
|
+
@pong_signal.broadcast
|
27
|
+
return
|
28
|
+
end
|
29
|
+
|
30
|
+
dispatch_frame(PingFrame) do |p|
|
31
|
+
p.ack!
|
32
|
+
p.payload = fr.payload
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def handle_go_away(fr)
|
37
|
+
return protocol_error! unless @configured
|
38
|
+
|
39
|
+
# server intends to disconnect
|
40
|
+
cancel_streams(fr.error_code)
|
41
|
+
close_connection_after_writing
|
42
|
+
end
|
43
|
+
|
44
|
+
def handle_reset_stream(fr)
|
45
|
+
return protocol_error! unless @configured
|
46
|
+
|
47
|
+
str = fetch_stream(fr.stream_id)
|
48
|
+
return protocol_error! unless str
|
49
|
+
|
50
|
+
str.handle_reset_stream(fr)
|
51
|
+
end
|
52
|
+
|
53
|
+
def handle_configuration(fr)
|
54
|
+
return protocol_error! if !fr.ack? || @configured
|
55
|
+
|
56
|
+
@configured = true
|
57
|
+
@configuration_ready_lock.synchronize do
|
58
|
+
@configuration_ready_signal.broadcast
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def handle_data(fr)
|
63
|
+
return protocol_error! unless @configured
|
64
|
+
|
65
|
+
str = fetch_stream(fr.stream_id)
|
66
|
+
return reset_stream(fr.stream_id, ERROR_CODE_PROTOCOL_ERROR) unless str
|
67
|
+
|
68
|
+
str.handle_data(fr)
|
69
|
+
end
|
70
|
+
|
71
|
+
def new_stream
|
72
|
+
wait_configuration
|
73
|
+
id = nil
|
74
|
+
@streams_monitor.synchronize do
|
75
|
+
@last_stream_id += 1
|
76
|
+
id = @last_stream_id
|
77
|
+
@streams[id] = Stream.new(id, self)
|
78
|
+
dispatch_frame(MakeStreamFrame) do |fr|
|
79
|
+
fr.stream_id = id
|
80
|
+
end
|
81
|
+
end
|
82
|
+
@streams[id]
|
83
|
+
end
|
84
|
+
|
85
|
+
def terminate(reason)
|
86
|
+
dispatch_frame(GoAwayFrame) do |g|
|
87
|
+
g.error_code = reason
|
88
|
+
g.last_stream_id = @last_stream_id
|
89
|
+
end
|
90
|
+
close_connection_after_writing
|
91
|
+
end
|
92
|
+
|
93
|
+
def close
|
94
|
+
wait_configuration
|
95
|
+
@streams_monitor.synchronize do
|
96
|
+
close_connection
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Arf
|
4
|
+
module Wire
|
5
|
+
MAX_PAYLOAD = 65_535
|
6
|
+
|
7
|
+
def self.encode_uint16(v) = [v].pack("S>")
|
8
|
+
def self.encode_uint32(v) = [v].pack("L>")
|
9
|
+
def self.decode_uint16(io) = io.read(2).unpack1("S>")
|
10
|
+
def self.decode_uint32(io) = io.read(4).unpack1("L>")
|
11
|
+
|
12
|
+
def self.data_frames_from_buffer(id, buf, end_stream: false)
|
13
|
+
buf = case buf
|
14
|
+
when StringIO then buf.string
|
15
|
+
when String then buf
|
16
|
+
else buf.to_s
|
17
|
+
end
|
18
|
+
|
19
|
+
len = buf.length
|
20
|
+
if len <= MAX_PAYLOAD
|
21
|
+
return [
|
22
|
+
DataFrame.new do |fr|
|
23
|
+
fr.stream_id = id
|
24
|
+
fr.end_data!
|
25
|
+
fr.end_stream! if end_stream
|
26
|
+
fr.payload = buf
|
27
|
+
end
|
28
|
+
]
|
29
|
+
end
|
30
|
+
|
31
|
+
frames = []
|
32
|
+
written = 0
|
33
|
+
loop do
|
34
|
+
to_write = [len - written, MAX_PAYLOAD].min
|
35
|
+
end_data = (len - written - to_write).zero?
|
36
|
+
frames << DataFrame.new do |fr|
|
37
|
+
fr.stream_id = id
|
38
|
+
fr.end_data! if end_data
|
39
|
+
fr.end_stream! if end_stream
|
40
|
+
fr.payload = buf[written...written + to_write]
|
41
|
+
end
|
42
|
+
written += to_write
|
43
|
+
break if end_data
|
44
|
+
end
|
45
|
+
|
46
|
+
frames
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|