onstomp 1.0.0pre1
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.
- data/.autotest +2 -0
- data/.gitignore +6 -0
- data/.rspec +2 -0
- data/.yardopts +5 -0
- data/CHANGELOG.md +4 -0
- data/DeveloperNarrative.md +15 -0
- data/Gemfile +4 -0
- data/LICENSE.md +221 -0
- data/README.md +73 -0
- data/Rakefile +6 -0
- data/UserNarrative.md +8 -0
- data/examples/basic.rb +40 -0
- data/examples/events.rb +72 -0
- data/lib/onstomp/client.rb +152 -0
- data/lib/onstomp/components/frame.rb +108 -0
- data/lib/onstomp/components/frame_headers.rb +212 -0
- data/lib/onstomp/components/nil_processor.rb +20 -0
- data/lib/onstomp/components/scopes/header_scope.rb +25 -0
- data/lib/onstomp/components/scopes/receipt_scope.rb +25 -0
- data/lib/onstomp/components/scopes/transaction_scope.rb +191 -0
- data/lib/onstomp/components/scopes.rb +45 -0
- data/lib/onstomp/components/subscription.rb +30 -0
- data/lib/onstomp/components/threaded_processor.rb +62 -0
- data/lib/onstomp/components/uri.rb +30 -0
- data/lib/onstomp/components.rb +13 -0
- data/lib/onstomp/connections/base.rb +208 -0
- data/lib/onstomp/connections/heartbeating.rb +82 -0
- data/lib/onstomp/connections/serializers/stomp_1.rb +166 -0
- data/lib/onstomp/connections/serializers/stomp_1_0.rb +41 -0
- data/lib/onstomp/connections/serializers/stomp_1_1.rb +134 -0
- data/lib/onstomp/connections/serializers.rb +9 -0
- data/lib/onstomp/connections/stomp_1.rb +69 -0
- data/lib/onstomp/connections/stomp_1_0.rb +28 -0
- data/lib/onstomp/connections/stomp_1_1.rb +65 -0
- data/lib/onstomp/connections.rb +119 -0
- data/lib/onstomp/interfaces/client_configurable.rb +55 -0
- data/lib/onstomp/interfaces/client_events.rb +168 -0
- data/lib/onstomp/interfaces/connection_events.rb +62 -0
- data/lib/onstomp/interfaces/event_manager.rb +69 -0
- data/lib/onstomp/interfaces/frame_methods.rb +190 -0
- data/lib/onstomp/interfaces/receipt_manager.rb +33 -0
- data/lib/onstomp/interfaces/subscription_manager.rb +48 -0
- data/lib/onstomp/interfaces/uri_configurable.rb +106 -0
- data/lib/onstomp/interfaces.rb +14 -0
- data/lib/onstomp/version.rb +13 -0
- data/lib/onstomp.rb +147 -0
- data/onstomp.gemspec +29 -0
- data/spec/onstomp/client_spec.rb +265 -0
- data/spec/onstomp/components/frame_headers_spec.rb +163 -0
- data/spec/onstomp/components/frame_spec.rb +144 -0
- data/spec/onstomp/components/nil_processor_spec.rb +32 -0
- data/spec/onstomp/components/scopes/header_scope_spec.rb +27 -0
- data/spec/onstomp/components/scopes/receipt_scope_spec.rb +33 -0
- data/spec/onstomp/components/scopes/transaction_scope_spec.rb +227 -0
- data/spec/onstomp/components/scopes_spec.rb +63 -0
- data/spec/onstomp/components/subscription_spec.rb +58 -0
- data/spec/onstomp/components/threaded_processor_spec.rb +92 -0
- data/spec/onstomp/components/uri_spec.rb +33 -0
- data/spec/onstomp/connections/base_spec.rb +349 -0
- data/spec/onstomp/connections/heartbeating_spec.rb +132 -0
- data/spec/onstomp/connections/serializers/stomp_1_0_spec.rb +50 -0
- data/spec/onstomp/connections/serializers/stomp_1_1_spec.rb +99 -0
- data/spec/onstomp/connections/serializers/stomp_1_spec.rb +104 -0
- data/spec/onstomp/connections/stomp_1_0_spec.rb +54 -0
- data/spec/onstomp/connections/stomp_1_1_spec.rb +137 -0
- data/spec/onstomp/connections/stomp_1_spec.rb +113 -0
- data/spec/onstomp/connections_spec.rb +135 -0
- data/spec/onstomp/interfaces/client_events_spec.rb +108 -0
- data/spec/onstomp/interfaces/connection_events_spec.rb +55 -0
- data/spec/onstomp/interfaces/event_manager_spec.rb +72 -0
- data/spec/onstomp/interfaces/frame_methods_spec.rb +109 -0
- data/spec/onstomp/interfaces/receipt_manager_spec.rb +53 -0
- data/spec/onstomp/interfaces/subscription_manager_spec.rb +64 -0
- data/spec/onstomp_spec.rb +15 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/support/custom_argument_matchers.rb +51 -0
- data/spec/support/frame_matchers.rb +88 -0
- data/spec/support/shared_frame_method_examples.rb +116 -0
- data/yard_extensions.rb +32 -0
- metadata +219 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
# Frame serializer / parser for STOMP 1.1 connections.
|
|
4
|
+
class OnStomp::Connections::Serializers::Stomp_1_1
|
|
5
|
+
include OnStomp::Connections::Serializers::Stomp_1
|
|
6
|
+
|
|
7
|
+
# Mapping of characters to their appropriate escape sequences. This
|
|
8
|
+
# is used when escaping headers for frames being written to the stream.
|
|
9
|
+
CHARACTER_ESCAPES = { ':' => "\\c", "\n" => "\\n", "\\" => "\\\\" }
|
|
10
|
+
|
|
11
|
+
# Mapping of escape sequences to their appropriate characters. This
|
|
12
|
+
# is used when unescaping headers being read from the stream.
|
|
13
|
+
ESCAPE_SEQUENCES = Hash[CHARACTER_ESCAPES.map { |k,v| [v,k] }]
|
|
14
|
+
|
|
15
|
+
# Creates a new serializer and calls {#reset_parser}
|
|
16
|
+
def initialize
|
|
17
|
+
reset_parser
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Converts a {OnStomp::Components::Frame frame} to a string
|
|
21
|
+
# @param [OnStomp::Components::Frame] frame
|
|
22
|
+
# @return [String]
|
|
23
|
+
def frame_to_string frame
|
|
24
|
+
frame_to_string_base(make_ct(frame)) do |k,v|
|
|
25
|
+
"#{escape_header k}:#{escape_header v}\n"
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Escapes a header name or value by replacing special characters with
|
|
30
|
+
# their appropriate escape sequences. The header will also be encoded to
|
|
31
|
+
# 'UTF-8' when using Ruby 1.9+
|
|
32
|
+
# @param [String] s header name or value
|
|
33
|
+
# @return [String]
|
|
34
|
+
def escape_header s
|
|
35
|
+
encode_header(s).gsub(/[:\n\\\\]/) { |c| CHARACTER_ESCAPES[c] }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Unescapes a header name or pair parsed from the read buffer by converting
|
|
39
|
+
# known escape sequences into their special characters. The header string
|
|
40
|
+
# will have a 'UTF-8' encoding forced upon it when using Ruby 1.9+, as per
|
|
41
|
+
# the STOMP 1.1 spec.
|
|
42
|
+
# @param [String] s header name or value
|
|
43
|
+
# @return [String]
|
|
44
|
+
# @raise [OnStomp::InvalidHeaderEscapeSequenceError]
|
|
45
|
+
# if an unknown escape sequence is present in the header name or value
|
|
46
|
+
def unescape_header s
|
|
47
|
+
force_header_encoding(s).gsub(/\\.?/) do |c|
|
|
48
|
+
ESCAPE_SEQUENCES[c] || raise(OnStomp::InvalidHeaderEscapeSequenceError, "#{c}")
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Splits a header line into a header name / header value pair at the first
|
|
53
|
+
# ':' character {#unescape_header unescapes} them, and returns the pair.
|
|
54
|
+
# @param [String] str header line to split
|
|
55
|
+
# @return [[String, String]]
|
|
56
|
+
# @raise [OnStomp::MalformedHeaderError] if the header line
|
|
57
|
+
# lacks a ':' character
|
|
58
|
+
def split_header(str)
|
|
59
|
+
col = str.index(':')
|
|
60
|
+
unless col
|
|
61
|
+
raise OnStomp::MalformedHeaderError, "unterminated header: '#{str}'"
|
|
62
|
+
end
|
|
63
|
+
[ unescape_header(str[0...col]),
|
|
64
|
+
unescape_header(str[(col+1)..-1]) ]
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Forces the frame's body to match the charset specified in its +content-type+
|
|
68
|
+
# header, if applicable.
|
|
69
|
+
# @param [OnStomp::Components::Frame] frame
|
|
70
|
+
def prepare_parsed_frame frame
|
|
71
|
+
force_body_encoding frame
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
if RUBY_VERSION >= "1.9"
|
|
75
|
+
# Encodes the given string to 'UTF-8'
|
|
76
|
+
# @note No-op for Ruby 1.8.x
|
|
77
|
+
# @param [String] s
|
|
78
|
+
# @return [String]
|
|
79
|
+
def encode_header(s)
|
|
80
|
+
s.encoding.name == 'UTF-8' ? s : s.encode('UTF-8')
|
|
81
|
+
end
|
|
82
|
+
# Forces the encoding of the given string to 'UTF-8'
|
|
83
|
+
# @note No-op for Ruby 1.8.x
|
|
84
|
+
# @param [String] s
|
|
85
|
+
# @return [String]
|
|
86
|
+
def force_header_encoding(s); s.tap { s.force_encoding('UTF-8') }; end
|
|
87
|
+
# Forces the encoding of the given frame's body to match its charset.
|
|
88
|
+
# @note No-op for Ruby 1.8.x
|
|
89
|
+
# @param [OnStomp::Components::Frame] f
|
|
90
|
+
# @return [OnStomp::Components::Frame]
|
|
91
|
+
def force_body_encoding f
|
|
92
|
+
type, subtype, charset = f.content_type
|
|
93
|
+
charset ||= (type == 'text') ? 'UTF-8' : 'ASCII-8BIT'
|
|
94
|
+
f.body.force_encoding(charset)
|
|
95
|
+
f
|
|
96
|
+
end
|
|
97
|
+
# Set an appropriate +content-type+ header with +charset+ parameter for
|
|
98
|
+
# frames with a text body
|
|
99
|
+
# @note No-op for Ruby 1.8.x
|
|
100
|
+
# @param [OnStomp::Components::Frame] f
|
|
101
|
+
# @return [OnStomp::Components::Frame]
|
|
102
|
+
def make_ct f
|
|
103
|
+
return f if f.body.nil?
|
|
104
|
+
t, st = f.content_type
|
|
105
|
+
enc = f.body.encoding.name
|
|
106
|
+
if enc != 'ASCII-8BIT'
|
|
107
|
+
f[:'content-type'] = "#{t||'text'}/#{st||'plain'};charset=#{enc}"
|
|
108
|
+
end
|
|
109
|
+
f
|
|
110
|
+
end
|
|
111
|
+
else
|
|
112
|
+
# Encodes the given string to 'UTF-8'
|
|
113
|
+
# @note No-op for Ruby 1.8.x
|
|
114
|
+
# @param [String] s
|
|
115
|
+
# @return [String]
|
|
116
|
+
def encode_header(s); s; end
|
|
117
|
+
# Forces the encoding of the given string to 'UTF-8'
|
|
118
|
+
# @note No-op for Ruby 1.8.x
|
|
119
|
+
# @param [String] s
|
|
120
|
+
# @return [String]
|
|
121
|
+
def force_header_encoding(s); s; end
|
|
122
|
+
# Forces the encoding of the given frame's body to match its charset.
|
|
123
|
+
# @note No-op for Ruby 1.8.x
|
|
124
|
+
# @param [OnStomp::Components::Frame] f
|
|
125
|
+
# @return [OnStomp::Components::Frame]
|
|
126
|
+
def force_body_encoding(f); f; end
|
|
127
|
+
# Set an appropriate +content-type+ header with +charset+ parameter for
|
|
128
|
+
# frames with a text body
|
|
129
|
+
# @note No-op for Ruby 1.8.x
|
|
130
|
+
# @param [OnStomp::Components::Frame] f
|
|
131
|
+
# @return [OnStomp::Components::Frame]
|
|
132
|
+
def make_ct(f); f; end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
# Namespace for {OnStomp::Components::Frame frame} serializers / parsers.
|
|
4
|
+
module OnStomp::Connections::Serializers
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
require 'onstomp/connections/serializers/stomp_1'
|
|
8
|
+
require 'onstomp/connections/serializers/stomp_1_0'
|
|
9
|
+
require 'onstomp/connections/serializers/stomp_1_1'
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
# Connection behavior common to both STOMP 1.0 and STOMP 1.1 connections
|
|
4
|
+
module OnStomp::Connections::Stomp_1
|
|
5
|
+
# Creates a CONNECT frame
|
|
6
|
+
# @return [OnStomp::Components::Frame] CONNECT frame
|
|
7
|
+
def connect_frame *h
|
|
8
|
+
create_frame 'CONNECT', h
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Creates a SEND frame
|
|
12
|
+
# @return [OnStomp::Components::Frame] SEND frame
|
|
13
|
+
def send_frame d, b, h
|
|
14
|
+
create_frame 'SEND', [h, {:destination => d}], b
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Creates a BEGIN frame
|
|
18
|
+
# @return [OnStomp::Components::Frame] BEGIN frame
|
|
19
|
+
def begin_frame tx, h
|
|
20
|
+
create_transaction_frame 'BEGIN', tx, h
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Creates a COMMIT frame
|
|
24
|
+
# @return [OnStomp::Components::Frame] COMMIT frame
|
|
25
|
+
def commit_frame tx, h
|
|
26
|
+
create_transaction_frame 'COMMIT', tx, h
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Creates an ABORT frame
|
|
30
|
+
# @return [OnStomp::Components::Frame] ABORT frame
|
|
31
|
+
def abort_frame tx, h
|
|
32
|
+
create_transaction_frame 'ABORT', tx, h
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Creates a DISCONNECT frame
|
|
36
|
+
# @return [OnStomp::Components::Frame] DISCONNECT frame
|
|
37
|
+
def disconnect_frame h
|
|
38
|
+
create_frame 'DISCONNECT', [h]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Creates a SUBSCRIBE frame
|
|
42
|
+
# @return [OnStomp::Components::Frame] SUBSCRIBE frame
|
|
43
|
+
def subscribe_frame d, h
|
|
44
|
+
create_frame 'SUBSCRIBE', [{:id => OnStomp.next_serial}, h, {:destination => d}]
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Creates an UNSUBSCRIBE frame
|
|
48
|
+
# @return [OnStomp::Components::Frame] UNSUBSCRIBE frame
|
|
49
|
+
def unsubscribe_frame f, h
|
|
50
|
+
id = f.is_a?(OnStomp::Components::Frame) ? f[:id] : f
|
|
51
|
+
create_frame('UNSUBSCRIBE', [{:id => id}, h]).tap do |f|
|
|
52
|
+
raise ArgumentError, 'subscription ID could not be determined' unless f.header?(:id)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
def create_transaction_frame command, tx, headers
|
|
58
|
+
create_frame command, [headers, {:transaction => tx}]
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def create_frame command, layered_headers, body=nil
|
|
62
|
+
headers = layered_headers.inject({}) do |final, h|
|
|
63
|
+
h = OnStomp.keys_to_sym(h).delete_if { |k,v| final.key?(k) && (v.nil? || v.empty?) }
|
|
64
|
+
final.merge!(h)
|
|
65
|
+
final
|
|
66
|
+
end
|
|
67
|
+
OnStomp::Components::Frame.new(command, headers, body)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
# A STOMP 1.0 specific connection
|
|
4
|
+
class OnStomp::Connections::Stomp_1_0 < OnStomp::Connections::Base
|
|
5
|
+
include OnStomp::Connections::Stomp_1
|
|
6
|
+
# The serializer that will convert {OnStomp::Components::Frame frames} into
|
|
7
|
+
# raw bytes and will convert raw bytes into {OnStomp::Components::Frame frames}
|
|
8
|
+
# @return [OnStomp::Connections::Serializers::Stomp_1_0]
|
|
9
|
+
attr_reader :serializer
|
|
10
|
+
|
|
11
|
+
# Calls {OnStomp::Connections::Base#initialize} and creates a STOMP 1.0
|
|
12
|
+
# serializer
|
|
13
|
+
def initialize socket, client
|
|
14
|
+
super
|
|
15
|
+
@serializer = OnStomp::Connections::Serializers::Stomp_1_0.new
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Creates an ACK frame
|
|
19
|
+
# @return [OnStomp::Components::Frame] ACK frame
|
|
20
|
+
def ack_frame *args
|
|
21
|
+
headers = args.last.is_a?(Hash) ? args.pop : {}
|
|
22
|
+
m_id = args.shift
|
|
23
|
+
m_id = m_id[:'message-id'] if m_id.is_a?(OnStomp::Components::Frame)
|
|
24
|
+
create_frame('ACK', [{:'message-id' => m_id}, headers]).tap do |f|
|
|
25
|
+
raise ArgumentError, 'no message-id to ACK' unless f.header?(:'message-id')
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
# A STOMP 1.1 specific connection
|
|
4
|
+
class OnStomp::Connections::Stomp_1_1 < OnStomp::Connections::Base
|
|
5
|
+
include OnStomp::Connections::Stomp_1
|
|
6
|
+
include OnStomp::Connections::Heartbeating
|
|
7
|
+
# The serializer that will convert {OnStomp::Components::Frame frames} into
|
|
8
|
+
# raw bytes and will convert raw bytes into {OnStomp::Components::Frame frames}
|
|
9
|
+
# @return [OnStomp::Connections::Serializers::Stomp_1_1]
|
|
10
|
+
attr_reader :serializer
|
|
11
|
+
|
|
12
|
+
# Calls {OnStomp::Connections::Base#initialize} and creates a STOMP 1.0
|
|
13
|
+
# serializer
|
|
14
|
+
def initialize socket, client
|
|
15
|
+
super
|
|
16
|
+
@serializer = OnStomp::Connections::Serializers::Stomp_1_1.new
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Calls {OnStomp::Connections::Base#configure} then configures heartbeating
|
|
20
|
+
# parameters.
|
|
21
|
+
# @param [OnStomp::Components::Frame] connected
|
|
22
|
+
# @param [{Symbol => Proc}] con_cbs
|
|
23
|
+
def configure connected, con_cbs
|
|
24
|
+
super
|
|
25
|
+
configure_heartbeating client.heartbeats, connected.heart_beat
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Returns true if {OnStomp::Connections::Base#connected?} is true and
|
|
29
|
+
# we have a {#pulse?}
|
|
30
|
+
def connected?
|
|
31
|
+
super && pulse?
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Creates an ACK frame
|
|
35
|
+
# @return [OnStomp::Components::Frame] ACK frame
|
|
36
|
+
def ack_frame *args
|
|
37
|
+
create_ack_or_nack 'ACK', args
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Creates an NACK frame
|
|
41
|
+
# @return [OnStomp::Components::Frame] NACK frame
|
|
42
|
+
def nack_frame *args
|
|
43
|
+
create_ack_or_nack 'NACK', args
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Creates a heartbeat frame (serialized as a single "\n" character)
|
|
47
|
+
# @return [OnStomp::Components::Frame] heartbeat frame
|
|
48
|
+
def heartbeat_frame
|
|
49
|
+
OnStomp::Components::Frame.new
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
def create_ack_or_nack(command, args)
|
|
54
|
+
headers = args.last.is_a?(Hash) ? args.pop : {}
|
|
55
|
+
m_id = args.shift
|
|
56
|
+
sub_id = args.shift
|
|
57
|
+
if m_id.is_a?(OnStomp::Components::Frame)
|
|
58
|
+
sub_id = m_id[:subscription]
|
|
59
|
+
m_id = m_id[:'message-id']
|
|
60
|
+
end
|
|
61
|
+
create_frame(command, [{:'message-id' => m_id, :subscription => sub_id }, headers]).tap do |f|
|
|
62
|
+
raise ::ArgumentError, 'missing message-id or subscription headers' unless f.headers?(:'message-id', :subscription)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
# Namespace for protocol specific connections used to communicate with
|
|
4
|
+
# STOMP brokers.
|
|
5
|
+
module OnStomp::Connections
|
|
6
|
+
# Default SSL options to use when establishing an SSL connection.
|
|
7
|
+
DEFAULT_SSL_OPTIONS = {
|
|
8
|
+
:verify_mode => OpenSSL::SSL::VERIFY_PEER |
|
|
9
|
+
OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT,
|
|
10
|
+
:ca_file => nil,
|
|
11
|
+
:ca_path => nil,
|
|
12
|
+
:cert => nil,
|
|
13
|
+
:key => nil,
|
|
14
|
+
:post_connection_check => true
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
# Returns a list of supported protocol versions
|
|
18
|
+
# @return [Array<String>]
|
|
19
|
+
def self.supported
|
|
20
|
+
PROTOCOL_VERSIONS.keys.sort
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Filters a list of supplied versions to only those
|
|
24
|
+
# that are supported. Results are in the same order as they are found
|
|
25
|
+
# in {.supported}. If none of the supplied versions are supported, an
|
|
26
|
+
# empty list is returned.
|
|
27
|
+
# @param [Array<String>] vers versions to filter
|
|
28
|
+
# @return [Array<String>]
|
|
29
|
+
def self.select_supported vers
|
|
30
|
+
vers = Array(vers)
|
|
31
|
+
supported.select { |v| vers.include? v }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Creates an initial {OnStomp::Connections::Stomp_1_0 connection} to
|
|
35
|
+
# the client's broker uri, performs the CONNECT/CONNECTED frame exchange,
|
|
36
|
+
# and returns a {OnStomp::Connections::Base connection} suitable for the
|
|
37
|
+
# negotiated STOMP protocol version.
|
|
38
|
+
# @param [OnStomp::Client] client
|
|
39
|
+
# @param [{#to_sym => #to_s}] u_head user specified headers for CONNECT frame
|
|
40
|
+
# @param [{#to_sym => #to_s}] c_head client specified headers for CONNECT frame
|
|
41
|
+
# @param [{Symbol => Proc}] con_cbs event callbacks to install on the final
|
|
42
|
+
# connection
|
|
43
|
+
# @return [OnStomp::Connections::Base] instance of Base subclass suited for
|
|
44
|
+
# negotiated protocol version
|
|
45
|
+
# @raise [OnStomp::OnStompError] if negotiating the connection raises an
|
|
46
|
+
# such an error.
|
|
47
|
+
def self.connect client, u_head, c_head, con_cbs
|
|
48
|
+
init_con = create_connection('1.0', nil, client)
|
|
49
|
+
ver, connected = init_con.connect client, u_head, c_head
|
|
50
|
+
begin
|
|
51
|
+
negotiate_connection(ver, init_con, client).tap do |final_con|
|
|
52
|
+
final_con.configure connected, con_cbs
|
|
53
|
+
end
|
|
54
|
+
rescue OnStomp::OnStompError
|
|
55
|
+
# Perform a blocking close.
|
|
56
|
+
init_con.close true
|
|
57
|
+
raise
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
private
|
|
62
|
+
def self.negotiate_connection vers, con, client
|
|
63
|
+
supports_protocol?(vers,con) ? con :
|
|
64
|
+
create_connection(vers, con.socket, client)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def self.supports_protocol? ver, con
|
|
68
|
+
con.is_a? PROTOCOL_VERSIONS[ver]
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def self.create_connection ver, sock, client
|
|
72
|
+
unless sock
|
|
73
|
+
meth = client.ssl ? :ssl :
|
|
74
|
+
client.uri.respond_to?(:onstomp_socket_type) ?
|
|
75
|
+
client.uri.onstomp_socket_type : :tcp
|
|
76
|
+
sock = __send__(:"create_socket_#{meth}", client)
|
|
77
|
+
end
|
|
78
|
+
PROTOCOL_VERSIONS[ver].new sock, client
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def self.create_socket_tcp client
|
|
82
|
+
TCPSocket.new(client.uri.host || 'localhost', client.uri.port)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def self.create_socket_ssl client
|
|
86
|
+
uri = client.uri
|
|
87
|
+
host = uri.host || 'localhost'
|
|
88
|
+
ssl_opts = client.ssl.is_a?(Hash) ? client.ssl : {}
|
|
89
|
+
ssl_opts = DEFAULT_SSL_OPTIONS.merge(ssl_opts)
|
|
90
|
+
context = OpenSSL::SSL::SSLContext.new
|
|
91
|
+
post_check = ssl_opts.delete(:post_connection_check)
|
|
92
|
+
post_check_host = (post_check == true) ? host : post_check
|
|
93
|
+
DEFAULT_SSL_OPTIONS.keys.each do |k|
|
|
94
|
+
context.__send__(:"#{k}=", ssl_opts[k]) if ssl_opts.key?(k)
|
|
95
|
+
end
|
|
96
|
+
tcp_sock = create_socket_tcp(client)
|
|
97
|
+
socket = OpenSSL::SSL::SSLSocket.new(tcp_sock, context)
|
|
98
|
+
socket.sync_close = true
|
|
99
|
+
socket.connect
|
|
100
|
+
socket.post_connection_check(post_check_host) if post_check_host
|
|
101
|
+
socket
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
require 'onstomp/connections/serializers'
|
|
106
|
+
require 'onstomp/connections/base'
|
|
107
|
+
require 'onstomp/connections/stomp_1'
|
|
108
|
+
require 'onstomp/connections/heartbeating'
|
|
109
|
+
require 'onstomp/connections/stomp_1_0'
|
|
110
|
+
require 'onstomp/connections/stomp_1_1'
|
|
111
|
+
|
|
112
|
+
module OnStomp::Connections
|
|
113
|
+
# A mapping of protocol versions to the connection classes that support
|
|
114
|
+
# them.
|
|
115
|
+
PROTOCOL_VERSIONS = {
|
|
116
|
+
'1.0' => OnStomp::Connections::Stomp_1_0,
|
|
117
|
+
'1.1' => OnStomp::Connections::Stomp_1_1
|
|
118
|
+
}
|
|
119
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
# Module for configurable attributes specific to {OnStomp::Client client} objects.
|
|
4
|
+
module OnStomp::Interfaces::ClientConfigurable
|
|
5
|
+
# Includes {OnStomp::Interfaces::UriConfigurable} into +base+ and
|
|
6
|
+
# extends {OnStomp::Interfaces::ClientConfigurable::ClassMethods}
|
|
7
|
+
# @param [Module] base
|
|
8
|
+
def self.included(base)
|
|
9
|
+
base.__send__ :include, OnStomp::Interfaces::UriConfigurable
|
|
10
|
+
base.extend ClassMethods
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Provides attribute methods for {OnStomp::Client client} objects.
|
|
14
|
+
module ClassMethods
|
|
15
|
+
# Creates a readable and writeable attribute with the given name that
|
|
16
|
+
# defaults to the {OnStomp::Connections.supported supported} protocol
|
|
17
|
+
# versions and is {OnStomp::Connections.select_supported filtered} to
|
|
18
|
+
# those versions when assigned. Corresponds to which protocol versions
|
|
19
|
+
# should be used for a given client's connection.
|
|
20
|
+
# @param [Symbol] nm name of attribute
|
|
21
|
+
def attr_configurable_protocols nm
|
|
22
|
+
attr_configurable_arr(nm, :default => OnStomp::Connections.supported) do |vers|
|
|
23
|
+
OnStomp::Connections.select_supported(vers).tap do |valid|
|
|
24
|
+
raise OnStomp::UnsupportedProtocolVersionError, vers.inspect if valid.empty?
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Creates a readable and writeable attribute with the given name that
|
|
30
|
+
# defaults to [0, 0] and is mapped to a pair of non-negative integers
|
|
31
|
+
# when assigned. Corresponds to what heartbeating strategy should be used
|
|
32
|
+
# for a given client's connection where [0, 0] indicates no heartbeating
|
|
33
|
+
# should be performed.
|
|
34
|
+
# @note This attribute is only useful with STOMP 1.1 connections.
|
|
35
|
+
# @param [Symbol] nm name of attribute
|
|
36
|
+
def attr_configurable_client_beats nm
|
|
37
|
+
attr_configurable_arr(nm, :default => [0,0]) do |val|
|
|
38
|
+
val.map { |b| bi = b.to_i; bi < 0 ? 0 : bi }
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Creates a readable and writeable attribute with the given name that
|
|
43
|
+
# defaults to the {OnStomp::Components::ThreadedProcessor} and if set
|
|
44
|
+
# to nil will instead use {OnStomp::Components::NilProcessor}. Corresponds
|
|
45
|
+
# the the class to use when create new processor instances when a client
|
|
46
|
+
# is connected.
|
|
47
|
+
# @param [Symbol] nm name of attribute
|
|
48
|
+
def attr_configurable_processor nm
|
|
49
|
+
attr_configurable_class(nm,
|
|
50
|
+
:default => OnStomp::Components::ThreadedProcessor) do |pr|
|
|
51
|
+
pr || OnStomp::Components::NilProcessor
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
# Mixin for {OnStomp::Client client} events
|
|
4
|
+
# There are a few special event methods that will be passed on
|
|
5
|
+
# to the client's connection, they are:
|
|
6
|
+
# * +on_connection_established+ => {OnStomp::Interfaces::ConnectionEvents#on_established}
|
|
7
|
+
# * +on_connection_died+ => {OnStomp::Interfaces::ConnectionEvents#on_died}
|
|
8
|
+
# * +on_connection_terminated+ => {OnStomp::Interfaces::ConnectionEvents#on_terminated}
|
|
9
|
+
# * +on_connection_closed+ => {OnStomp::Interfaces::ConnectionEvents#on_closed}
|
|
10
|
+
module OnStomp::Interfaces::ClientEvents
|
|
11
|
+
include OnStomp::Interfaces::EventManager
|
|
12
|
+
|
|
13
|
+
# @group Client Frame Event Bindings
|
|
14
|
+
|
|
15
|
+
# Binds a callback to be invoked when an ACK frame is transmitted
|
|
16
|
+
# @yield [frame, client] callback invoked when event is triggered
|
|
17
|
+
# @yieldparam [OnStomp::Components::Frame] frame
|
|
18
|
+
# @yieldparam [OnStomp::Client] client
|
|
19
|
+
create_event_methods :ack, :before, :on
|
|
20
|
+
# Binds a callback to be invoked when a NACK frame is transmitted
|
|
21
|
+
# @yield [frame, client] callback invoked when event is triggered
|
|
22
|
+
# @yieldparam [OnStomp::Components::Frame] frame
|
|
23
|
+
# @yieldparam [OnStomp::Client] client
|
|
24
|
+
create_event_methods :nack, :before, :on
|
|
25
|
+
# Binds a callback to be invoked when a BEGIN frame is transmitted
|
|
26
|
+
# @yield [frame, client] callback invoked when event is triggered
|
|
27
|
+
# @yieldparam [OnStomp::Components::Frame] frame
|
|
28
|
+
# @yieldparam [OnStomp::Client] client
|
|
29
|
+
create_event_methods :begin, :before, :on
|
|
30
|
+
# Binds a callback to be invoked when an ABORT frame is transmitted
|
|
31
|
+
# @yield [frame, client] callback invoked when event is triggered
|
|
32
|
+
# @yieldparam [OnStomp::Components::Frame] frame
|
|
33
|
+
# @yieldparam [OnStomp::Client] client
|
|
34
|
+
create_event_methods :abort, :before, :on
|
|
35
|
+
# Binds a callback to be invoked when a COMMIT frame is transmitted
|
|
36
|
+
# @yield [frame, client] callback invoked when event is triggered
|
|
37
|
+
# @yieldparam [OnStomp::Components::Frame] frame
|
|
38
|
+
# @yieldparam [OnStomp::Client] client
|
|
39
|
+
create_event_methods :commit, :before, :on
|
|
40
|
+
# Binds a callback to be invoked when a SEND frame is transmitted
|
|
41
|
+
# @yield [frame, client] callback invoked when event is triggered
|
|
42
|
+
# @yieldparam [OnStomp::Components::Frame] frame
|
|
43
|
+
# @yieldparam [OnStomp::Client] client
|
|
44
|
+
create_event_methods :send, :before, :on
|
|
45
|
+
# Binds a callback to be invoked when a SUBSCRIBE frame is transmitted
|
|
46
|
+
# @yield [frame, client] callback invoked when event is triggered
|
|
47
|
+
# @yieldparam [OnStomp::Components::Frame] frame
|
|
48
|
+
# @yieldparam [OnStomp::Client] client
|
|
49
|
+
create_event_methods :subscribe, :before, :on
|
|
50
|
+
# Binds a callback to be invoked when an UNSUBSCRIBE frame is transmitted
|
|
51
|
+
# @yield [frame, client] callback invoked when event is triggered
|
|
52
|
+
# @yieldparam [OnStomp::Components::Frame] frame
|
|
53
|
+
# @yieldparam [OnStomp::Client] client
|
|
54
|
+
create_event_methods :unsubscribe, :before, :on
|
|
55
|
+
# Binds a callback to be invoked when a DISCONNECT frame is transmitted
|
|
56
|
+
# @yield [frame, client] callback invoked when event is triggered
|
|
57
|
+
# @yieldparam [OnStomp::Components::Frame] frame
|
|
58
|
+
# @yieldparam [OnStomp::Client] client
|
|
59
|
+
create_event_methods :disconnect, :before, :on
|
|
60
|
+
# Binds a callback to be invoked when a client heartbeat is transmitted
|
|
61
|
+
# @yield [frame, client] callback invoked when event is triggered
|
|
62
|
+
# @yieldparam [OnStomp::Components::Frame] frame
|
|
63
|
+
# @yieldparam [OnStomp::Client] client
|
|
64
|
+
create_event_methods :client_beat, :before, :on
|
|
65
|
+
|
|
66
|
+
# @group Broker Frame Event Bindings
|
|
67
|
+
|
|
68
|
+
# Binds a callback to be invoked when an ERROR frame is received
|
|
69
|
+
# @yield [frame, client] callback invoked when event is triggered
|
|
70
|
+
# @yieldparam [OnStomp::Components::Frame] frame
|
|
71
|
+
# @yieldparam [OnStomp::Client] client
|
|
72
|
+
create_event_methods :error, :before, :on
|
|
73
|
+
# Binds a callback to be invoked when a MESSAGE frame is received
|
|
74
|
+
# @yield [frame, client] callback invoked when event is triggered
|
|
75
|
+
# @yieldparam [OnStomp::Components::Frame] frame
|
|
76
|
+
# @yieldparam [OnStomp::Client] client
|
|
77
|
+
create_event_methods :message, :before, :on
|
|
78
|
+
# Binds a callback to be invoked when a RECEIPT frame is received
|
|
79
|
+
# @yield [frame, client] callback invoked when event is triggered
|
|
80
|
+
# @yieldparam [OnStomp::Components::Frame] frame
|
|
81
|
+
# @yieldparam [OnStomp::Client] client
|
|
82
|
+
create_event_methods :receipt, :before, :on
|
|
83
|
+
# Binds a callback to be invoked when a broker heartbeat is received
|
|
84
|
+
# @yield [frame, client] callback invoked when event is triggered
|
|
85
|
+
# @yieldparam [OnStomp::Components::Frame] frame
|
|
86
|
+
# @yieldparam [OnStomp::Client] client
|
|
87
|
+
create_event_methods :broker_beat, :before, :on
|
|
88
|
+
|
|
89
|
+
# @group Frame Exchange Event Bindings
|
|
90
|
+
|
|
91
|
+
# Binds a callback to be invoked when any frame is transmitted
|
|
92
|
+
# @yield [frame, client] callback invoked when event is triggered
|
|
93
|
+
# @yieldparam [OnStomp::Components::Frame] frame
|
|
94
|
+
# @yieldparam [OnStomp::Client] client
|
|
95
|
+
create_event_methods :transmitting, :before, :after
|
|
96
|
+
# Binds a callback to be invoked when any frame is received
|
|
97
|
+
# @yield [frame, client] callback invoked when event is triggered
|
|
98
|
+
# @yieldparam [OnStomp::Components::Frame] frame
|
|
99
|
+
# @yieldparam [OnStomp::Client] client
|
|
100
|
+
create_event_methods :receiving, :before, :after
|
|
101
|
+
|
|
102
|
+
# @endgroup
|
|
103
|
+
|
|
104
|
+
# Helpers for setting up connection events through a client
|
|
105
|
+
[:established, :terminated, :died, :closed].each do |ev|
|
|
106
|
+
module_eval <<-EOS
|
|
107
|
+
def on_connection_#{ev}(&cb)
|
|
108
|
+
if connection
|
|
109
|
+
connection.on_#{ev}(&cb)
|
|
110
|
+
else
|
|
111
|
+
pending_connection_events[:on_#{ev}] << cb
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
EOS
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Returns a hash of event bindings that should be set on a
|
|
118
|
+
# {OnStomp::Connections::Base connection}, but were set on the client
|
|
119
|
+
# because the connection does not exist yet.
|
|
120
|
+
# @return [{Symbol => Array<Proc>}]
|
|
121
|
+
def pending_connection_events
|
|
122
|
+
@pending_connection_events ||= Hash.new { |h,k| h[k] = [] }
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Triggers an event named by the supplied frame, prefixed with the supplied
|
|
126
|
+
# prefix. If the supplied frame is a 'heart-beat', origin will be used to
|
|
127
|
+
# dispatch appropriate heart-beat event (client_beat or broker_beat)
|
|
128
|
+
# @param [OnStomp::Components::Frame] f the frame trigger this event
|
|
129
|
+
# @param [:on, :before, etc] pref the prefix for the event name
|
|
130
|
+
# @param [:client, :broker] origin
|
|
131
|
+
def trigger_frame_event f, pref, origin
|
|
132
|
+
e = f.command ? :"#{pref}_#{f.command.downcase}" :
|
|
133
|
+
:"#{pref}_#{origin}_beat"
|
|
134
|
+
trigger_event e, f, self
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Triggers the :before_receiving event and the
|
|
138
|
+
# +before+ prefixed frame specific event (eg: +:before_error+).
|
|
139
|
+
# @param [OnStomp::Components::Frame] f
|
|
140
|
+
def trigger_before_receiving f
|
|
141
|
+
trigger_event :before_receiving, f, self
|
|
142
|
+
trigger_frame_event f, :before, :broker
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Triggers the :after_receiving event and the
|
|
146
|
+
# +on+ prefixed frame specific event (eg: +:on_message+)
|
|
147
|
+
# @param [OnStomp::Components::Frame] f
|
|
148
|
+
def trigger_after_receiving f
|
|
149
|
+
trigger_event :after_receiving, f, self
|
|
150
|
+
trigger_frame_event f, :on, :broker
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# Triggers the :before_transmitting event and the
|
|
154
|
+
# +before+ prefixed frame specific event (eg: +:before_disconnect+).
|
|
155
|
+
# @param [OnStomp::Components::Frame] f
|
|
156
|
+
def trigger_before_transmitting f
|
|
157
|
+
trigger_event :before_transmitting, f, self
|
|
158
|
+
trigger_frame_event f, :before, :client
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Triggers the :after_transmitting event and the
|
|
162
|
+
# +on+ prefixed frame specific event (eg: +:on_send+).
|
|
163
|
+
# @param [OnStomp::Components::Frame] f
|
|
164
|
+
def trigger_after_transmitting f
|
|
165
|
+
trigger_event :after_transmitting, f, self
|
|
166
|
+
trigger_frame_event f, :on, :client
|
|
167
|
+
end
|
|
168
|
+
end
|