rsmp 0.48.4 → 0.51.1
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 +4 -4
- data/CHANGELOG.md +14 -0
- data/Gemfile.lock +19 -20
- data/README.md +27 -3
- data/Rakefile +3 -1
- data/documentation/cli.md +79 -4
- data/documentation/collecting_message.md +90 -170
- data/documentation/configuration.md +8 -0
- data/documentation/message_distribution.md +17 -3
- data/documentation/tasks.md +65 -117
- data/lib/rsmp/cli.rb +93 -5
- data/lib/rsmp/collect/collector.rb +117 -134
- data/lib/rsmp/collect/distributor.rb +16 -3
- data/lib/rsmp/collect/queue.rb +14 -6
- data/lib/rsmp/collect/receiver.rb +1 -1
- data/lib/rsmp/collect/state_collector.rb +11 -2
- data/lib/rsmp/collection.rb +12 -0
- data/lib/rsmp/completion.rb +49 -0
- data/lib/rsmp/component/component_base.rb +3 -3
- data/lib/rsmp/convert/export/json_schema/index.rb +8 -4
- data/lib/rsmp/convert/import/yaml.rb +12 -8
- data/lib/rsmp/delivery.rb +18 -0
- data/lib/rsmp/event.rb +19 -0
- data/lib/rsmp/event_source.rb +36 -0
- data/lib/rsmp/exchange.rb +10 -0
- data/lib/rsmp/failure.rb +23 -0
- data/lib/rsmp/helpers/error.rb +14 -23
- data/lib/rsmp/message.rb +10 -9
- data/lib/rsmp/node/node.rb +3 -21
- data/lib/rsmp/node/site/connections.rb +4 -9
- data/lib/rsmp/node/site/site.rb +33 -17
- data/lib/rsmp/node/supervisor/modules/configuration.rb +24 -11
- data/lib/rsmp/node/supervisor/modules/connection.rb +19 -5
- data/lib/rsmp/node/supervisor/modules/sites.rb +16 -5
- data/lib/rsmp/node/supervisor/supervisor.rb +13 -14
- data/lib/rsmp/node/task.rb +59 -53
- data/lib/rsmp/proxy/modules/acknowledgements.rb +22 -9
- data/lib/rsmp/proxy/modules/lifecycle.rb +134 -0
- data/lib/rsmp/proxy/modules/receive.rb +109 -66
- data/lib/rsmp/proxy/modules/send.rb +109 -51
- data/lib/rsmp/proxy/modules/state.rb +15 -5
- data/lib/rsmp/proxy/modules/tasks.rb +96 -70
- data/lib/rsmp/proxy/modules/versions.rb +69 -19
- data/lib/rsmp/proxy/modules/watchdogs.rb +15 -2
- data/lib/rsmp/proxy/proxy.rb +7 -69
- data/lib/rsmp/proxy/site/connection.rb +27 -22
- data/lib/rsmp/proxy/site/modules/aggregated_status.rb +18 -8
- data/lib/rsmp/proxy/site/modules/alarms.rb +45 -38
- data/lib/rsmp/proxy/site/modules/commands.rb +18 -8
- data/lib/rsmp/proxy/site/modules/status.rb +44 -19
- data/lib/rsmp/proxy/site/site_proxy.rb +12 -10
- data/lib/rsmp/proxy/site/sxl_selection.rb +7 -2
- data/lib/rsmp/proxy/supervisor/modules/aggregated_status.rb +1 -1
- data/lib/rsmp/proxy/supervisor/modules/alarms.rb +1 -1
- data/lib/rsmp/proxy/supervisor/modules/commands.rb +1 -1
- data/lib/rsmp/proxy/supervisor/modules/message_buffer.rb +8 -9
- data/lib/rsmp/proxy/supervisor/modules/status.rb +1 -1
- data/lib/rsmp/proxy/supervisor/modules/status_updates.rb +1 -1
- data/lib/rsmp/proxy/supervisor/supervisor_proxy.rb +36 -31
- data/lib/rsmp/result.rb +95 -0
- data/lib/rsmp/schema/validation.rb +5 -3
- data/lib/rsmp/schema/version.rb +27 -0
- data/lib/rsmp/schema.rb +1 -0
- data/lib/rsmp/sxl/interface.rb +8 -1
- data/lib/rsmp/sxl/processing/catalogue.rb +61 -0
- data/lib/rsmp/sxl/processing/conflict_checker.rb +30 -0
- data/lib/rsmp/sxl/processing/dependency_graph.rb +37 -0
- data/lib/rsmp/sxl/processing/document.rb +158 -0
- data/lib/rsmp/sxl/processing/error.rb +8 -0
- data/lib/rsmp/sxl/processing/manifest.rb +144 -0
- data/lib/rsmp/sxl/processing/natural_sort.rb +22 -0
- data/lib/rsmp/sxl/processing/resolver.rb +94 -0
- data/lib/rsmp/sxl/processing/version_requirement.rb +86 -0
- data/lib/rsmp/sxl/processing.rb +18 -0
- data/lib/rsmp/sxl/supervisor_interface.rb +8 -1
- data/lib/rsmp/termination.rb +10 -0
- data/lib/rsmp/tlc/modules/plans.rb +0 -2
- data/lib/rsmp/tlc/modules/system.rb +2 -2
- data/lib/rsmp/tlc/proxy/control.rb +50 -16
- data/lib/rsmp/tlc/proxy/detectors.rb +15 -4
- data/lib/rsmp/tlc/proxy/io.rb +46 -31
- data/lib/rsmp/tlc/proxy/plans.rb +36 -19
- data/lib/rsmp/tlc/proxy/plans_bang.rb +44 -0
- data/lib/rsmp/tlc/proxy/status.rb +47 -23
- data/lib/rsmp/tlc/proxy/system.rb +16 -4
- data/lib/rsmp/tlc/supervisor_interface.rb +7 -1
- data/lib/rsmp/tlc/traffic_controller_site.rb +6 -9
- data/lib/rsmp/validation.rb +22 -0
- data/lib/rsmp/version.rb +1 -1
- data/lib/rsmp.rb +19 -4
- data/rsmp.gemspec +1 -1
- metadata +26 -4
- data/lib/rsmp/collect/collector/status.rb +0 -34
data/lib/rsmp/node/task.rb
CHANGED
|
@@ -1,95 +1,101 @@
|
|
|
1
1
|
module RSMP
|
|
2
|
-
|
|
3
|
-
end
|
|
4
|
-
|
|
5
|
-
# Task helpers for starting and managing an Async task lifecycle.
|
|
2
|
+
# Explicit ownership for a long-running Async task tree.
|
|
6
3
|
module Task
|
|
4
|
+
class ConditionWaitTimeout < Async::TimeoutError; end
|
|
5
|
+
|
|
7
6
|
attr_reader :task
|
|
8
7
|
|
|
9
8
|
def initialize_task
|
|
10
9
|
@task = nil
|
|
10
|
+
@termination_completion = Completion.new
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
def start
|
|
16
|
-
return if @task
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
else
|
|
28
|
-
Async do |task|
|
|
29
|
-
task.annotate "#{self.class.name} main task"
|
|
30
|
-
@task = task
|
|
31
|
-
run
|
|
32
|
-
stop_subtasks
|
|
33
|
-
@task = nil
|
|
34
|
-
end
|
|
13
|
+
# Start under an explicit Async parent. A current task is accepted for
|
|
14
|
+
# convenience, but this library never creates a hidden root reactor.
|
|
15
|
+
def start(parent: Async::Task.current)
|
|
16
|
+
return @task if @task&.running?
|
|
17
|
+
|
|
18
|
+
valid_parent = parent.respond_to?(:async) && (!parent.respond_to?(:running?) || parent.running?)
|
|
19
|
+
raise ArgumentError, 'an active Async parent task is required' unless valid_parent
|
|
20
|
+
|
|
21
|
+
child = parent.async do |task|
|
|
22
|
+
task.annotate "#{self.class.name} main task"
|
|
23
|
+
@task = task
|
|
24
|
+
run
|
|
25
|
+
ensure
|
|
26
|
+
stop_subtasks
|
|
35
27
|
end
|
|
36
|
-
|
|
28
|
+
@task = child
|
|
37
29
|
end
|
|
38
30
|
|
|
39
|
-
# initiate restart by raising a Restart exception
|
|
40
31
|
def restart
|
|
41
|
-
|
|
32
|
+
termination_completion.succeed(Termination.new(reason: :restart, source: self))
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def wait_for_termination
|
|
36
|
+
termination_completion.wait
|
|
42
37
|
end
|
|
43
38
|
|
|
44
|
-
# get the status of our task, or nil of no task
|
|
45
39
|
def task_status
|
|
46
40
|
@task&.status
|
|
47
41
|
end
|
|
48
42
|
|
|
49
|
-
# perform any long-running work
|
|
50
|
-
# the method will be called from an async task, and should not return
|
|
51
|
-
# if subtasks are needed, the method should call wait() on each of them
|
|
52
|
-
# once running, ready() must be called
|
|
53
43
|
def run
|
|
54
44
|
start_subtasks
|
|
55
45
|
end
|
|
56
46
|
|
|
57
|
-
# wait
|
|
47
|
+
# Async::Task#wait raises unexpected child failures and returns nil for
|
|
48
|
+
# cancellation, matching Async's native task contract.
|
|
58
49
|
def wait
|
|
59
50
|
@task&.wait
|
|
60
51
|
end
|
|
61
52
|
|
|
62
|
-
# stop our task
|
|
63
53
|
def stop
|
|
64
|
-
|
|
65
|
-
stop_task if @task
|
|
54
|
+
stop_task
|
|
66
55
|
end
|
|
67
56
|
|
|
68
57
|
def stop_subtasks; end
|
|
69
58
|
|
|
70
|
-
# stop our task and any subtask
|
|
71
59
|
def stop_task
|
|
72
|
-
@task
|
|
73
|
-
|
|
60
|
+
task = @task
|
|
61
|
+
return unless task&.running?
|
|
62
|
+
|
|
63
|
+
task.cancel
|
|
64
|
+
task.wait unless task.current?
|
|
74
65
|
end
|
|
75
66
|
|
|
76
|
-
#
|
|
77
|
-
#
|
|
67
|
+
# Wait for an edge-triggered condition and return an expected timeout as a
|
|
68
|
+
# Result. Async cancellation is not rescued and therefore propagates.
|
|
78
69
|
def wait_for_condition(condition, timeout:, task: Async::Task.current, &block)
|
|
79
|
-
raise
|
|
70
|
+
raise ArgumentError, 'an active Async task is required' unless task&.running?
|
|
80
71
|
|
|
81
|
-
task.with_timeout(timeout) do
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
72
|
+
value = task.with_timeout(timeout, ConditionWaitTimeout) do
|
|
73
|
+
loop do
|
|
74
|
+
signalled = condition.wait
|
|
75
|
+
break signalled unless block
|
|
85
76
|
|
|
86
|
-
|
|
87
|
-
|
|
77
|
+
matched = yield(signalled)
|
|
78
|
+
break matched if matched
|
|
88
79
|
end
|
|
89
|
-
raise "Can't wait for condition because task #{task.object_id} #{task.annotation} is not running"
|
|
90
80
|
end
|
|
91
|
-
|
|
92
|
-
|
|
81
|
+
Result.success(value)
|
|
82
|
+
rescue ConditionWaitTimeout => e
|
|
83
|
+
Result.failure(
|
|
84
|
+
:timeout,
|
|
85
|
+
message: "Condition was not met within #{timeout}s",
|
|
86
|
+
source: :timeout,
|
|
87
|
+
cause: e
|
|
88
|
+
)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def wait_for_condition!(...)
|
|
92
|
+
wait_for_condition(...).value!
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
private
|
|
96
|
+
|
|
97
|
+
def termination_completion
|
|
98
|
+
@termination_completion ||= Completion.new
|
|
93
99
|
end
|
|
94
100
|
end
|
|
95
101
|
end
|
|
@@ -5,16 +5,16 @@ module RSMP
|
|
|
5
5
|
# Manages sending/receiving acks and nacks, and tracking acknowledged messages
|
|
6
6
|
module Acknowledgements
|
|
7
7
|
def acknowledge(original)
|
|
8
|
-
raise
|
|
8
|
+
raise ArgumentError, 'original message is required' unless original
|
|
9
9
|
|
|
10
10
|
ack = MessageAck.build_from(original)
|
|
11
11
|
ack.original = original.clone
|
|
12
|
-
|
|
12
|
+
send_generated_message ack, "for #{ack.original.type} #{original.m_id_short}"
|
|
13
13
|
check_ingoing_acknowledged original
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def dont_acknowledge(original, prefix = nil, reason = nil, force: true)
|
|
17
|
-
raise
|
|
17
|
+
raise ArgumentError, 'original message is required' unless original
|
|
18
18
|
|
|
19
19
|
str = [prefix, reason].join(' ')
|
|
20
20
|
log str, message: original, level: :warning if reason
|
|
@@ -23,7 +23,7 @@ module RSMP
|
|
|
23
23
|
'rea' => reason || 'Unknown reason'
|
|
24
24
|
})
|
|
25
25
|
message.original = original.clone
|
|
26
|
-
|
|
26
|
+
send_generated_message message, "for #{original.type} #{original.m_id_short}", force: force
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
def expect_acknowledgement(message)
|
|
@@ -45,12 +45,25 @@ module RSMP
|
|
|
45
45
|
|
|
46
46
|
str = "No acknowledgements for #{message.type} #{message.m_id_short} within #{timeout} seconds"
|
|
47
47
|
log str, level: :error
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
48
|
+
failure = Failure.new(
|
|
49
|
+
code: :missing_acknowledgement,
|
|
50
|
+
message: str,
|
|
51
|
+
source: :peer,
|
|
52
|
+
context: { message: message, reason: :missing_acknowledgement, session_id: session_id }
|
|
53
|
+
)
|
|
54
|
+
distribute_event(
|
|
55
|
+
Event.new(
|
|
56
|
+
type: :missing_acknowledgement,
|
|
57
|
+
source: self,
|
|
58
|
+
session_id: session_id,
|
|
59
|
+
message: message,
|
|
60
|
+
failure: failure
|
|
61
|
+
)
|
|
62
|
+
)
|
|
63
|
+
return Result.failure(failure: failure)
|
|
53
64
|
end
|
|
65
|
+
|
|
66
|
+
Result.success
|
|
54
67
|
end
|
|
55
68
|
|
|
56
69
|
def find_original_for_message(message)
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
module RSMP
|
|
2
|
+
class Proxy
|
|
3
|
+
module Modules
|
|
4
|
+
# Owns one connection session and translates expected termination into
|
|
5
|
+
# lifecycle events without swallowing unexpected task exceptions.
|
|
6
|
+
module Lifecycle
|
|
7
|
+
def disconnect
|
|
8
|
+
close(reason: :local_stop)
|
|
9
|
+
Result.success(self)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def disconnect!
|
|
13
|
+
disconnect.value!
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def connect!
|
|
17
|
+
connect.value!
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def wait_for_reader
|
|
21
|
+
wait_for_session
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def begin_session
|
|
25
|
+
clear
|
|
26
|
+
@session_id += 1
|
|
27
|
+
@session_closed = false
|
|
28
|
+
@session_id
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def close(reason: :peer_closed, failure: nil, session_id: @session_id)
|
|
32
|
+
return if session_id != @session_id || @session_closed
|
|
33
|
+
|
|
34
|
+
@session_closed = true
|
|
35
|
+
log 'Closing connection', level: :warning
|
|
36
|
+
close_stream
|
|
37
|
+
close_socket
|
|
38
|
+
stop_reader
|
|
39
|
+
self.state = :disconnected
|
|
40
|
+
publish_connection_end(reason, failure, session_id)
|
|
41
|
+
stop_timer
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def stop_subtasks
|
|
45
|
+
stop_timer
|
|
46
|
+
stop_reader
|
|
47
|
+
clear
|
|
48
|
+
super
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def stop_timer
|
|
52
|
+
current = Async::Task.current?
|
|
53
|
+
@timer&.cancel unless @timer.equal?(current)
|
|
54
|
+
ensure
|
|
55
|
+
@timer = nil
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def stop_reader
|
|
59
|
+
current = Async::Task.current?
|
|
60
|
+
@reader&.cancel unless @reader.equal?(current)
|
|
61
|
+
ensure
|
|
62
|
+
@reader = nil
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def close_stream
|
|
66
|
+
@stream&.close
|
|
67
|
+
ensure
|
|
68
|
+
@stream = nil
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def close_socket
|
|
72
|
+
@socket&.close
|
|
73
|
+
ensure
|
|
74
|
+
@socket = nil
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def stop_task
|
|
78
|
+
close(reason: :local_stop)
|
|
79
|
+
super
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def distribute_event(event)
|
|
83
|
+
super
|
|
84
|
+
@node&.publish_event(event)
|
|
85
|
+
event
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def connection_end_failure(reason)
|
|
89
|
+
local = reason == :local_stop
|
|
90
|
+
Failure.new(
|
|
91
|
+
code: local ? :cancelled : :disconnected,
|
|
92
|
+
message: "Connection ended (#{reason})",
|
|
93
|
+
source: local ? :local : :connection,
|
|
94
|
+
context: { reason: reason, session_id: @session_id }
|
|
95
|
+
)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def close_from_result(result)
|
|
99
|
+
return close(reason: :peer_closed) if result.nil? || result.success?
|
|
100
|
+
|
|
101
|
+
reason = result.failure.context[:reason] || :transport_failure
|
|
102
|
+
close(reason: reason, failure: result.failure)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def publish_connection_attempt_failure(failure)
|
|
106
|
+
log failure.message, level: :warning
|
|
107
|
+
distribute_event(
|
|
108
|
+
Event.new(
|
|
109
|
+
type: :connection_attempt_failed,
|
|
110
|
+
source: self,
|
|
111
|
+
session_id: @session_id,
|
|
112
|
+
failure: failure
|
|
113
|
+
)
|
|
114
|
+
)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
private
|
|
118
|
+
|
|
119
|
+
def publish_connection_end(reason, failure, session_id)
|
|
120
|
+
failure ||= connection_end_failure(reason)
|
|
121
|
+
distribute_event(
|
|
122
|
+
Event.new(
|
|
123
|
+
type: :connection_ended,
|
|
124
|
+
source: self,
|
|
125
|
+
session_id: session_id,
|
|
126
|
+
failure: failure,
|
|
127
|
+
context: { reason: reason }
|
|
128
|
+
)
|
|
129
|
+
)
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
module RSMP
|
|
2
2
|
class Proxy
|
|
3
3
|
module Modules
|
|
4
|
-
# Message processing functionality
|
|
5
|
-
#
|
|
4
|
+
# Message processing functionality. Peer-controlled invalid input is
|
|
5
|
+
# represented by Result::Failure; unexpected implementation errors escape.
|
|
6
6
|
module Receive
|
|
7
7
|
def should_validate_ingoing_message?(message)
|
|
8
8
|
return false if message.is_a?(Version) && !@version_determined
|
|
9
|
-
|
|
10
9
|
return true unless @site_settings
|
|
11
10
|
|
|
12
11
|
skip = @site_settings['skip_validation']
|
|
@@ -24,93 +23,137 @@ module RSMP
|
|
|
24
23
|
expect_version_message(message) unless @version_determined
|
|
25
24
|
end
|
|
26
25
|
|
|
27
|
-
def
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
26
|
+
def process_packet(json)
|
|
27
|
+
attributes = Message.parse_attributes(json)
|
|
28
|
+
message = Message.build(attributes, json)
|
|
29
|
+
process_incoming_message(message)
|
|
30
|
+
rescue InvalidPacket => e
|
|
31
|
+
reject_invalid_packet(json, e)
|
|
32
|
+
rescue MalformedMessage => e
|
|
33
|
+
reject_malformed_packet(json, attributes, e)
|
|
34
|
+
rescue PeerMessageError => e
|
|
35
|
+
reject_processed_message(message, e)
|
|
36
|
+
rescue HandshakeError, FatalError => e
|
|
37
|
+
reject_fatal_message(message, e)
|
|
38
|
+
ensure
|
|
39
|
+
@node&.clear_deferred
|
|
32
40
|
end
|
|
33
41
|
|
|
34
|
-
def
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
42
|
+
def process_incoming_message(message)
|
|
43
|
+
validate = should_validate_ingoing_message?(message)
|
|
44
|
+
if validate
|
|
45
|
+
validation = message.validate(schemas)
|
|
46
|
+
return reject_invalid_message(message, validation) if validation.invalid?
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
verify_sequence(message)
|
|
50
|
+
message.decode_for(schemas) if validate
|
|
51
|
+
with_deferred_distribution do
|
|
52
|
+
distribute(message)
|
|
53
|
+
process_message(message)
|
|
54
|
+
end
|
|
55
|
+
process_deferred
|
|
56
|
+
Result.success(message)
|
|
39
57
|
end
|
|
40
58
|
|
|
41
|
-
def
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
dont_acknowledge message, str, reason
|
|
48
|
-
message
|
|
59
|
+
def reject_invalid_packet(json, error)
|
|
60
|
+
reject_packet(
|
|
61
|
+
:invalid_packet,
|
|
62
|
+
"Received invalid packet, expected JSON but got #{json.size} bytes: #{error.message}",
|
|
63
|
+
raw: json
|
|
64
|
+
)
|
|
49
65
|
end
|
|
50
66
|
|
|
51
|
-
def
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
67
|
+
def reject_malformed_packet(json, attributes, error)
|
|
68
|
+
reject_packet(
|
|
69
|
+
:malformed_message,
|
|
70
|
+
"Received malformed message: #{error.message}",
|
|
71
|
+
raw: json,
|
|
72
|
+
message: Malformed.new(attributes || {})
|
|
73
|
+
)
|
|
57
74
|
end
|
|
58
75
|
|
|
59
|
-
def
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
76
|
+
def reject_invalid_message(message, validation)
|
|
77
|
+
schemas_string = schemas.map { |schema| "#{schema.first}: #{schema.last}" }.join(', ')
|
|
78
|
+
reason = "schema errors (#{schemas_string}): #{validation.message}"
|
|
79
|
+
text = "Received invalid #{message.type}"
|
|
80
|
+
failure = peer_failure(
|
|
81
|
+
:invalid_peer_message,
|
|
82
|
+
"#{text}: #{validation.message}",
|
|
83
|
+
message: message,
|
|
84
|
+
validation: validation
|
|
85
|
+
)
|
|
86
|
+
publish_peer_failure(failure, message: message)
|
|
87
|
+
log(text, message: message, level: :warning)
|
|
88
|
+
dont_acknowledge(message, text, reason)
|
|
89
|
+
Result.failure(failure: failure)
|
|
66
90
|
end
|
|
67
91
|
|
|
68
|
-
def
|
|
69
|
-
|
|
70
|
-
message
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
message
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
92
|
+
def reject_packet(code, text, raw:, message: nil)
|
|
93
|
+
failure = peer_failure(code, text, message: message, raw: raw)
|
|
94
|
+
publish_peer_failure(failure, message: message)
|
|
95
|
+
log(text, message: message, level: :warning)
|
|
96
|
+
Result.failure(failure: failure)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def reject_processed_message(message, error)
|
|
100
|
+
code = error.is_a?(MessageRejected) ? :message_rejected : :invalid_peer_message
|
|
101
|
+
text = "Received invalid #{message.type}: #{error.message}"
|
|
102
|
+
failure = peer_failure(code, text, message: message)
|
|
103
|
+
publish_peer_failure(failure, message: message)
|
|
104
|
+
dont_acknowledge(message, "Received invalid #{message.type}", error.message.to_s)
|
|
105
|
+
Result.failure(failure: failure)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def reject_fatal_message(message, error)
|
|
109
|
+
text = "Rejected #{message&.type || 'message'}: #{error.message}"
|
|
110
|
+
failure = peer_failure(:protocol_failure, text, message: message)
|
|
111
|
+
publish_peer_failure(failure, message: message)
|
|
112
|
+
dont_acknowledge(message, "Rejected #{message.type}", error.message.to_s) if message
|
|
113
|
+
close(reason: :protocol_failure, failure: failure)
|
|
114
|
+
Result.failure(failure: failure)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def peer_failure(code, text, message: nil, **context)
|
|
118
|
+
Failure.new(
|
|
119
|
+
code: code,
|
|
120
|
+
message: text,
|
|
121
|
+
source: :peer,
|
|
122
|
+
context: context.merge(message: message).compact
|
|
123
|
+
)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def publish_peer_failure(failure, message:)
|
|
127
|
+
distribute_event(
|
|
128
|
+
Event.new(
|
|
129
|
+
type: :invalid_message,
|
|
130
|
+
source: self,
|
|
131
|
+
session_id: session_id,
|
|
132
|
+
message: message,
|
|
133
|
+
failure: failure
|
|
134
|
+
)
|
|
135
|
+
)
|
|
93
136
|
end
|
|
94
137
|
|
|
95
138
|
def process_message(message)
|
|
96
139
|
case message
|
|
97
140
|
when MessageAck
|
|
98
|
-
process_ack
|
|
141
|
+
process_ack(message)
|
|
99
142
|
when MessageNotAck
|
|
100
|
-
process_not_ack
|
|
143
|
+
process_not_ack(message)
|
|
101
144
|
when Version
|
|
102
|
-
process_version
|
|
145
|
+
process_version(message)
|
|
103
146
|
when RSMP::Watchdog
|
|
104
|
-
process_watchdog
|
|
147
|
+
process_watchdog(message)
|
|
105
148
|
else
|
|
106
|
-
dont_acknowledge
|
|
149
|
+
dont_acknowledge(message, 'Received', "unknown message (#{message.type})")
|
|
107
150
|
end
|
|
108
151
|
end
|
|
109
152
|
|
|
110
153
|
def will_not_handle(message)
|
|
111
|
-
reason
|
|
154
|
+
reason = "since we're a #{self.class.name.downcase}"
|
|
112
155
|
log "Ignoring #{message.type}, #{reason}", message: message, level: :warning
|
|
113
|
-
dont_acknowledge
|
|
156
|
+
dont_acknowledge(message, nil, reason)
|
|
114
157
|
end
|
|
115
158
|
|
|
116
159
|
def expect_version_message(message)
|