amq-client 0.7.0.alpha25 → 0.7.0.alpha26
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +0 -1
- data/examples/coolio_adapter/basic_consume.rb +19 -15
- data/examples/coolio_adapter/channel_close.rb +4 -4
- data/examples/eventmachine_adapter/authentication/plain_password_with_default_role_credentials.rb +1 -1
- data/examples/eventmachine_adapter/basic_consume.rb +19 -17
- data/examples/eventmachine_adapter/basic_publish.rb +5 -5
- data/examples/eventmachine_adapter/channel_close.rb +4 -4
- data/examples/eventmachine_adapter/exchange_declare.rb +8 -8
- data/examples/eventmachine_adapter/kitchen_sink1.rb +10 -10
- data/lib/amq/client/adapter.rb +225 -38
- data/lib/amq/client/adapters/coolio.rb +72 -54
- data/lib/amq/client/adapters/event_machine.rb +67 -60
- data/lib/amq/client/callbacks.rb +18 -9
- data/lib/amq/client/channel.rb +44 -45
- data/lib/amq/client/entity.rb +4 -4
- data/lib/amq/client/exchange.rb +13 -21
- data/lib/amq/client/queue.rb +31 -31
- data/lib/amq/client/settings.rb +12 -1
- data/lib/amq/client/version.rb +1 -1
- data/spec/integration/coolio/connection_close_spec.rb +5 -5
- data/spec/integration/coolio/connection_start_spec.rb +9 -6
- data/spec/integration/eventmachine/channel_close_spec.rb +4 -4
- data/spec/integration/eventmachine/connection_close_spec.rb +5 -5
- data/spec/unit/client/entity_spec.rb +3 -8
- metadata +5 -6
- data/lib/amq/client/connection.rb +0 -246
data/.travis.yml
CHANGED
@@ -4,13 +4,13 @@
|
|
4
4
|
__dir = File.dirname(File.expand_path(__FILE__))
|
5
5
|
require File.join(__dir, "example_helper")
|
6
6
|
|
7
|
-
amq_client_example "Set a queue up for message delivery" do |
|
8
|
-
channel = AMQ::Client::Channel.new(
|
7
|
+
amq_client_example "Set a queue up for message delivery" do |connection|
|
8
|
+
channel = AMQ::Client::Channel.new(connection, 1)
|
9
9
|
channel.open do
|
10
10
|
puts "Channel #{channel.id} is now open!"
|
11
11
|
end
|
12
12
|
|
13
|
-
queue = AMQ::Client::Queue.new(
|
13
|
+
queue = AMQ::Client::Queue.new(connection, channel)
|
14
14
|
queue.declare(false, false, false, true) do
|
15
15
|
puts "Server-named, auto-deletable Queue #{queue.name.inspect} is ready"
|
16
16
|
end
|
@@ -19,30 +19,34 @@ amq_client_example "Set a queue up for message delivery" do |client|
|
|
19
19
|
puts "Queue #{queue.name} is now bound to amq.fanout"
|
20
20
|
end
|
21
21
|
|
22
|
-
|
23
|
-
|
22
|
+
show_stopper = Proc.new {
|
23
|
+
connection.disconnect do
|
24
|
+
puts
|
25
|
+
puts "AMQP connection is now properly closed"
|
26
|
+
Coolio::Loop.default.stop
|
27
|
+
end
|
28
|
+
}
|
29
|
+
|
30
|
+
|
31
|
+
queue.consume(true) do |consume_ok|
|
32
|
+
puts "Subscribed for messages routed to #{queue.name}, consumer tag is #{consume_ok.consumer_tag}, using no-ack mode"
|
24
33
|
puts
|
25
34
|
|
26
|
-
queue.on_delivery do |header, payload
|
35
|
+
queue.on_delivery do |basic_deliver, header, payload|
|
27
36
|
puts "Got a delivery:"
|
28
|
-
puts " Delivery tag: #{delivery_tag}"
|
37
|
+
puts " Delivery tag: #{basic_deliver.delivery_tag}"
|
29
38
|
puts " Header: #{header.inspect}"
|
30
39
|
puts " Payload: #{payload.inspect}"
|
40
|
+
|
41
|
+
show_stopper.call if basic_deliver.delivery_tag == 100
|
31
42
|
end
|
32
43
|
|
33
|
-
exchange = AMQ::Client::Exchange.new(
|
44
|
+
exchange = AMQ::Client::Exchange.new(connection, channel, "amq.fanout", :fanout)
|
34
45
|
100.times do |i|
|
35
46
|
exchange.publish("Message ##{i}")
|
36
47
|
end
|
37
48
|
end
|
38
49
|
|
39
|
-
show_stopper = Proc.new {
|
40
|
-
client.disconnect do
|
41
|
-
puts
|
42
|
-
puts "AMQP connection is now properly closed"
|
43
|
-
Coolio::Loop.default.stop
|
44
|
-
end
|
45
|
-
}
|
46
50
|
|
47
51
|
Signal.trap "INT", show_stopper
|
48
52
|
Signal.trap "TERM", show_stopper
|
@@ -4,17 +4,17 @@
|
|
4
4
|
__dir = File.dirname(File.expand_path(__FILE__))
|
5
5
|
require File.join(__dir, "example_helper")
|
6
6
|
|
7
|
-
amq_client_example "Open and then close AMQ channel" do |
|
8
|
-
puts "AMQP connection is open: #{
|
7
|
+
amq_client_example "Open and then close AMQ channel" do |connection|
|
8
|
+
puts "AMQP connection is open: #{connection.server_properties.inspect}"
|
9
9
|
|
10
|
-
channel = AMQ::Client::Channel.new(
|
10
|
+
channel = AMQ::Client::Channel.new(connection, 1)
|
11
11
|
channel.open do
|
12
12
|
puts "Channel #{channel.id} is now open!"
|
13
13
|
puts "Lets close it."
|
14
14
|
channel.close do
|
15
15
|
puts "Closed channel ##{channel.id}"
|
16
16
|
puts
|
17
|
-
|
17
|
+
connection.disconnect do
|
18
18
|
puts
|
19
19
|
puts "AMQP connection is now properly closed"
|
20
20
|
Coolio::Loop.default.stop
|
data/examples/eventmachine_adapter/authentication/plain_password_with_default_role_credentials.rb
CHANGED
@@ -8,7 +8,7 @@ EM.run do
|
|
8
8
|
AMQ::Client::EventMachineClient.connect(:port => 5672, :vhost => "/amq_client_testbed", :user => "guest", :password => "guest") do |client|
|
9
9
|
puts "Connected, authenticated"
|
10
10
|
|
11
|
-
puts client.authenticating?
|
11
|
+
puts "client.authenticating? => #{client.authenticating?}"
|
12
12
|
|
13
13
|
|
14
14
|
show_stopper = Proc.new {
|
@@ -4,13 +4,13 @@
|
|
4
4
|
__dir = File.dirname(File.expand_path(__FILE__))
|
5
5
|
require File.join(__dir, "example_helper")
|
6
6
|
|
7
|
-
amq_client_example "Set a queue up for message delivery" do |
|
8
|
-
channel = AMQ::Client::Channel.new(
|
7
|
+
amq_client_example "Set a queue up for message delivery" do |connection|
|
8
|
+
channel = AMQ::Client::Channel.new(connection, 1)
|
9
9
|
channel.open do
|
10
10
|
puts "Channel #{channel.id} is now open!"
|
11
11
|
end
|
12
12
|
|
13
|
-
queue = AMQ::Client::Queue.new(
|
13
|
+
queue = AMQ::Client::Queue.new(connection, channel)
|
14
14
|
queue.declare(false, false, false, true) do
|
15
15
|
puts "Server-named, auto-deletable Queue #{queue.name.inspect} is ready"
|
16
16
|
end
|
@@ -19,33 +19,35 @@ amq_client_example "Set a queue up for message delivery" do |client|
|
|
19
19
|
puts "Queue #{queue.name} is now bound to amq.fanout"
|
20
20
|
end
|
21
21
|
|
22
|
-
|
23
|
-
|
22
|
+
show_stopper = Proc.new {
|
23
|
+
connection.disconnect do
|
24
|
+
puts
|
25
|
+
puts "AMQP connection is now properly closed"
|
26
|
+
EM.stop
|
27
|
+
end
|
28
|
+
}
|
29
|
+
|
30
|
+
queue.consume(true) do |consume_ok|
|
31
|
+
puts "Subscribed for messages routed to #{queue.name}, consumer tag is #{consume_ok.consumer_tag}, using no-ack mode"
|
24
32
|
puts
|
25
33
|
|
26
|
-
queue.on_delivery do |
|
34
|
+
queue.on_delivery do |basic_deliver, header, payload|
|
27
35
|
puts "Got a delivery:"
|
28
|
-
puts " Delivery tag: #{
|
36
|
+
puts " Delivery tag: #{basic_deliver.delivery_tag}"
|
29
37
|
puts " Header: #{header.inspect}"
|
30
38
|
puts " Payload: #{payload.inspect}"
|
39
|
+
|
40
|
+
show_stopper.call if basic_deliver.delivery_tag == 100
|
31
41
|
end
|
32
42
|
|
33
|
-
exchange = AMQ::Client::Exchange.new(
|
43
|
+
exchange = AMQ::Client::Exchange.new(connection, channel, "amq.fanout", :fanout)
|
34
44
|
100.times do |i|
|
35
45
|
exchange.publish("Message ##{i}")
|
36
46
|
end
|
37
47
|
end
|
38
48
|
|
39
|
-
show_stopper = Proc.new {
|
40
|
-
client.disconnect do
|
41
|
-
puts
|
42
|
-
puts "AMQP connection is now properly closed"
|
43
|
-
EM.stop
|
44
|
-
end
|
45
|
-
}
|
46
|
-
|
47
49
|
Signal.trap "INT", show_stopper
|
48
50
|
Signal.trap "TERM", show_stopper
|
49
51
|
|
50
|
-
|
52
|
+
EM.add_timer(1, show_stopper)
|
51
53
|
end
|
@@ -4,15 +4,15 @@
|
|
4
4
|
__dir = File.dirname(File.expand_path(__FILE__))
|
5
5
|
require File.join(__dir, "example_helper")
|
6
6
|
|
7
|
-
amq_client_example "Publish 100 messages using basic.publish" do |
|
8
|
-
puts "AMQP connection is open: #{
|
7
|
+
amq_client_example "Publish 100 messages using basic.publish" do |connection|
|
8
|
+
puts "AMQP connection is open: #{connection.server_properties.inspect}"
|
9
9
|
|
10
|
-
channel = AMQ::Client::Channel.new(
|
10
|
+
channel = AMQ::Client::Channel.new(connection, 1)
|
11
11
|
channel.open do
|
12
12
|
puts "Channel #{channel.id} is now open!"
|
13
13
|
end
|
14
14
|
|
15
|
-
exchange = AMQ::Client::Exchange.new(
|
15
|
+
exchange = AMQ::Client::Exchange.new(connection, channel, "amqclient.adapters.em.exchange1", :fanout)
|
16
16
|
exchange.declare do
|
17
17
|
100.times do
|
18
18
|
# exchange.publish("à bientôt!")
|
@@ -24,7 +24,7 @@ amq_client_example "Publish 100 messages using basic.publish" do |client|
|
|
24
24
|
end
|
25
25
|
|
26
26
|
show_stopper = Proc.new {
|
27
|
-
|
27
|
+
connection.disconnect do
|
28
28
|
puts
|
29
29
|
puts "AMQP connection is now properly closed"
|
30
30
|
EM.stop
|
@@ -4,17 +4,17 @@
|
|
4
4
|
__dir = File.dirname(File.expand_path(__FILE__))
|
5
5
|
require File.join(__dir, "example_helper")
|
6
6
|
|
7
|
-
amq_client_example "Open and then close AMQ channel" do |
|
8
|
-
puts "AMQP connection is open: #{
|
7
|
+
amq_client_example "Open and then close AMQ channel" do |connection|
|
8
|
+
puts "AMQP connection is open: #{connection.server_properties.inspect}"
|
9
9
|
|
10
|
-
channel = AMQ::Client::Channel.new(
|
10
|
+
channel = AMQ::Client::Channel.new(connection, 1)
|
11
11
|
channel.open do
|
12
12
|
puts "Channel #{channel.id} is now open!"
|
13
13
|
puts "Lets close it."
|
14
14
|
channel.close do
|
15
15
|
puts "Closed channel ##{channel.id}"
|
16
16
|
puts
|
17
|
-
|
17
|
+
connection.disconnect do
|
18
18
|
puts
|
19
19
|
puts "AMQP connection is now properly closed"
|
20
20
|
EM.stop
|
@@ -4,24 +4,24 @@
|
|
4
4
|
__dir = File.dirname(File.expand_path(__FILE__))
|
5
5
|
require File.join(__dir, "example_helper")
|
6
6
|
|
7
|
-
amq_client_example "Declare a new fanout exchange" do |
|
8
|
-
puts "AMQP connection is open: #{
|
7
|
+
amq_client_example "Declare a new fanout exchange" do |connection|
|
8
|
+
puts "AMQP connection is open: #{connection.server_properties.inspect}"
|
9
9
|
|
10
|
-
channel = AMQ::Client::Channel.new(
|
10
|
+
channel = AMQ::Client::Channel.new(connection, 1)
|
11
11
|
channel.open do
|
12
12
|
puts "Channel #{channel.id} is now open!"
|
13
13
|
|
14
14
|
exchange_name = "amqclient.adapters.em.exchange"
|
15
15
|
|
16
16
|
10.times do
|
17
|
-
exchange = AMQ::Client::Exchange.new(
|
17
|
+
exchange = AMQ::Client::Exchange.new(connection, channel, exchange_name, :fanout)
|
18
18
|
exchange.declare
|
19
19
|
end
|
20
20
|
|
21
|
-
exchange2 = AMQ::Client::Exchange.new(
|
21
|
+
exchange2 = AMQ::Client::Exchange.new(connection, channel, exchange_name + "-2", :fanout)
|
22
22
|
exchange2.declare
|
23
23
|
|
24
|
-
AMQ::Client::Exchange.new(
|
24
|
+
AMQ::Client::Exchange.new(connection, channel, exchange_name, :fanout).declare do |exchange, declare_ok|
|
25
25
|
puts "Channel is aware of the following exchanges: #{channel.exchanges.map { |e| e.name }.join(', ')}"
|
26
26
|
|
27
27
|
exchange.delete do
|
@@ -29,7 +29,7 @@ amq_client_example "Declare a new fanout exchange" do |client|
|
|
29
29
|
exchange2.delete do
|
30
30
|
puts "Exchange #{exchange2.name} was successfully deleted"
|
31
31
|
|
32
|
-
|
32
|
+
connection.disconnect do
|
33
33
|
puts
|
34
34
|
puts "AMQP connection is now properly closed"
|
35
35
|
EM.stop
|
@@ -39,7 +39,7 @@ amq_client_example "Declare a new fanout exchange" do |client|
|
|
39
39
|
end
|
40
40
|
|
41
41
|
show_stopper = Proc.new {
|
42
|
-
|
42
|
+
connection.disconnect do
|
43
43
|
puts
|
44
44
|
puts "AMQP connection is now properly closed"
|
45
45
|
EM.stop
|
@@ -4,37 +4,37 @@
|
|
4
4
|
__dir = File.dirname(File.expand_path(__FILE__))
|
5
5
|
require File.join(__dir, "example_helper")
|
6
6
|
|
7
|
-
amq_client_example "An example that combines several AMQ operations" do |
|
8
|
-
puts "AMQP connection is open: #{
|
7
|
+
amq_client_example "An example that combines several AMQ operations" do |connection|
|
8
|
+
puts "AMQP connection is open: #{connection.server_properties.inspect}"
|
9
9
|
|
10
|
-
channel = AMQ::Client::Channel.new(
|
10
|
+
channel = AMQ::Client::Channel.new(connection, 1)
|
11
11
|
channel.open do
|
12
12
|
puts "Channel #{channel.id} is now open!"
|
13
13
|
end
|
14
14
|
|
15
|
-
queue = AMQ::Client::Queue.new(
|
15
|
+
queue = AMQ::Client::Queue.new(connection, channel, "amqclient.queue2")
|
16
16
|
queue.declare(false, false, false, true) do
|
17
17
|
puts "Queue #{queue.name.inspect} is now declared!"
|
18
18
|
end
|
19
19
|
|
20
|
-
exchange = AMQ::Client::Exchange.new(
|
20
|
+
exchange = AMQ::Client::Exchange.new(connection, channel, "amqclient.adapters.em.exchange1", :fanout)
|
21
21
|
exchange.declare { puts "Exchange #{exchange.name.inspect} is now declared!" }
|
22
22
|
|
23
|
-
queue.consume do |
|
24
|
-
puts
|
23
|
+
queue.consume do |consume_ok|
|
24
|
+
puts "basic.consume_ok callback has fired for #{queue.name}, consumer_tag = #{consume_ok.consumer_tag}"
|
25
25
|
end
|
26
26
|
|
27
27
|
|
28
28
|
show_stopper = Proc.new {
|
29
29
|
puts
|
30
30
|
puts "Deleting queue #{queue.name}"
|
31
|
-
queue.delete do |
|
31
|
+
queue.delete do |delete_ok|
|
32
32
|
puts
|
33
|
-
puts "Deleted #{queue.name}. It had #{message_count} messages in it."
|
33
|
+
puts "Deleted #{queue.name}. It had #{delete_ok.message_count} messages in it."
|
34
34
|
puts
|
35
35
|
puts "Deleting exchange #{exchange.name}"
|
36
36
|
exchange.delete do
|
37
|
-
|
37
|
+
connection.disconnect do
|
38
38
|
puts
|
39
39
|
puts "AMQP connection is now properly closed"
|
40
40
|
EM.stop
|
data/lib/amq/client/adapter.rb
CHANGED
@@ -3,12 +3,13 @@
|
|
3
3
|
require "amq/client/logging"
|
4
4
|
require "amq/client/settings"
|
5
5
|
require "amq/client/entity"
|
6
|
-
require "amq/client/connection"
|
7
6
|
require "amq/client/channel"
|
8
7
|
|
9
8
|
module AMQ
|
10
9
|
# For overview of AMQP client adapters API, see {AMQ::Client::Adapter}
|
11
10
|
module Client
|
11
|
+
|
12
|
+
|
12
13
|
# Base adapter class. Specific implementations (for example, EventMachine-based, Cool.io-based or
|
13
14
|
# sockets-based) subclass it and must implement Adapter API methods:
|
14
15
|
#
|
@@ -20,22 +21,69 @@ module AMQ
|
|
20
21
|
module Adapter
|
21
22
|
|
22
23
|
def self.included(host)
|
23
|
-
host.extend
|
24
|
+
host.extend ClassMethods
|
25
|
+
host.extend ProtocolMethodHandlers
|
24
26
|
|
25
27
|
host.class_eval do
|
26
|
-
attr_accessor :logger, :settings, :connection
|
27
28
|
|
28
|
-
#
|
29
|
-
|
29
|
+
#
|
30
|
+
# Behaviors
|
31
|
+
#
|
32
|
+
|
33
|
+
include Entity
|
34
|
+
|
30
35
|
|
31
|
-
|
32
|
-
|
36
|
+
|
37
|
+
#
|
38
|
+
# API
|
39
|
+
#
|
40
|
+
|
41
|
+
attr_accessor :logger
|
42
|
+
attr_accessor :settings
|
43
|
+
attr_accessor :connection
|
33
44
|
|
34
45
|
# The locale defines the language in which the server will send reply texts.
|
35
46
|
#
|
36
47
|
# @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.4.2.2)
|
37
48
|
attr_accessor :locale
|
38
49
|
|
50
|
+
# Client capabilities
|
51
|
+
#
|
52
|
+
# @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.4.2.2.1)
|
53
|
+
attr_accessor :client_properties
|
54
|
+
|
55
|
+
# Server capabilities
|
56
|
+
#
|
57
|
+
# @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.4.2.1.3)
|
58
|
+
attr_reader :server_properties
|
59
|
+
|
60
|
+
# Authentication mechanism used.
|
61
|
+
#
|
62
|
+
# @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.4.2.2)
|
63
|
+
attr_reader :mechanism
|
64
|
+
|
65
|
+
# Channels within this connection.
|
66
|
+
#
|
67
|
+
# @see http://bit.ly/hw2ELX AMQP 0.9.1 specification (Section 2.2.5)
|
68
|
+
attr_reader :channels
|
69
|
+
|
70
|
+
# Maximum channel number that the server permits this connection to use.
|
71
|
+
# Usable channel numbers are in the range 1..channel_max.
|
72
|
+
# Zero indicates no specified limit.
|
73
|
+
#
|
74
|
+
# @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Sections 1.4.2.5.1 and 1.4.2.6.1)
|
75
|
+
attr_accessor :channel_max
|
76
|
+
|
77
|
+
# Maximum frame size that the server permits this connection to use.
|
78
|
+
#
|
79
|
+
# @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Sections 1.4.2.5.2 and 1.4.2.6.2)
|
80
|
+
attr_accessor :frame_max
|
81
|
+
|
82
|
+
|
83
|
+
attr_reader :known_hosts
|
84
|
+
|
85
|
+
|
86
|
+
|
39
87
|
# @api plugin
|
40
88
|
# @see #disconnect
|
41
89
|
# @note Adapters must implement this method but it is NOT supposed to be used directly.
|
@@ -92,9 +140,7 @@ module AMQ
|
|
92
140
|
# @param [Hash] Connection parameters, including :adapter to use.
|
93
141
|
# @api public
|
94
142
|
def connect(settings = nil, &block)
|
95
|
-
|
96
|
-
# Let's make it an instance thing by instance = self.new(settings)
|
97
|
-
@settings = settings = Settings.configure(settings)
|
143
|
+
@settings = Settings.configure(settings)
|
98
144
|
|
99
145
|
instance = self.new
|
100
146
|
instance.establish_connection(settings)
|
@@ -132,20 +178,11 @@ module AMQ
|
|
132
178
|
|
133
179
|
register_entity :channel, AMQ::Client::Channel
|
134
180
|
|
181
|
+
|
135
182
|
#
|
136
183
|
# API
|
137
184
|
#
|
138
185
|
|
139
|
-
def initialize(*args)
|
140
|
-
super(*args)
|
141
|
-
|
142
|
-
self.logger = self.class.logger
|
143
|
-
self.settings = self.class.settings
|
144
|
-
|
145
|
-
@frames = Array.new
|
146
|
-
end
|
147
|
-
|
148
|
-
|
149
186
|
|
150
187
|
# Establish socket connection to the server.
|
151
188
|
#
|
@@ -154,30 +191,26 @@ module AMQ
|
|
154
191
|
raise MissingInterfaceMethodError.new("AMQ::Client#establish_connection(settings)")
|
155
192
|
end
|
156
193
|
|
157
|
-
def handshake(mechanism = "PLAIN", response = "\0guest\0guest", locale = "en_GB")
|
158
|
-
self.send_preamble
|
159
|
-
self.connection = AMQ::Client::Connection.new(self, mechanism, response, locale)
|
160
|
-
end
|
161
|
-
|
162
194
|
# Properly close connection with AMQ broker, as described in
|
163
195
|
# section 2.2.4 of the {http://bit.ly/hw2ELX AMQP 0.9.1 specification}.
|
164
196
|
#
|
165
197
|
# @api plugin
|
166
198
|
# @see #close_connection
|
167
|
-
def disconnect(reply_code = 200, reply_text = "Goodbye", &block)
|
199
|
+
def disconnect(reply_code = 200, reply_text = "Goodbye", class_id = 0, method_id = 0, &block)
|
168
200
|
@intentionally_closing_connection = true
|
169
|
-
|
170
201
|
self.on_disconnection(&block)
|
171
|
-
closing!
|
172
202
|
|
173
203
|
# ruby-amqp/amqp#66, MK.
|
174
|
-
if self.
|
175
|
-
|
204
|
+
if self.open?
|
205
|
+
closing!
|
206
|
+
self.send Protocol::Connection::Close.encode(reply_code, reply_text, class_id, method_id)
|
207
|
+
elsif self.closing?
|
208
|
+
# no-op
|
176
209
|
else
|
177
210
|
self.disconnection_successful
|
178
211
|
end
|
179
212
|
end
|
180
|
-
|
213
|
+
|
181
214
|
|
182
215
|
# Sends AMQ protocol header (also known as preamble).
|
183
216
|
#
|
@@ -188,19 +221,61 @@ module AMQ
|
|
188
221
|
self.send_raw(AMQ::Protocol::PREAMBLE)
|
189
222
|
end
|
190
223
|
|
224
|
+
# Sends frame to the peer, checking that connection is open.
|
225
|
+
#
|
226
|
+
# @raise [ConnectionClosedError]
|
191
227
|
def send(frame)
|
192
|
-
if
|
228
|
+
if closed?
|
193
229
|
raise ConnectionClosedError.new(frame)
|
194
230
|
else
|
195
231
|
self.send_raw(frame.encode)
|
196
232
|
end
|
197
233
|
end
|
198
234
|
|
235
|
+
# Sends multiple frames, one by one.
|
236
|
+
#
|
237
|
+
# @api public
|
199
238
|
def send_frameset(frames)
|
200
239
|
frames.each { |frame| self.send(frame) }
|
201
240
|
end # send_frameset(frames)
|
202
241
|
|
203
242
|
|
243
|
+
|
244
|
+
# Returns heartbeat interval this client uses, in seconds.
|
245
|
+
# This value may or may not be used depending on broker capabilities.
|
246
|
+
# Zero means the server does not want a heartbeat.
|
247
|
+
#
|
248
|
+
# @return [Fixnum] Heartbeat interval this client uses, in seconds.
|
249
|
+
# @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.4.2.6)
|
250
|
+
def heartbeat_interval
|
251
|
+
@settings[:heartbeat] || @settings[:heartbeat_interval] || 0
|
252
|
+
end # heartbeat_interval
|
253
|
+
|
254
|
+
|
255
|
+
def vhost
|
256
|
+
@settings.fetch(:vhost, "/")
|
257
|
+
end # vhost
|
258
|
+
|
259
|
+
|
260
|
+
# Called when previously established TCP connection fails.
|
261
|
+
# @api public
|
262
|
+
def tcp_connection_lost
|
263
|
+
@on_tcp_connection_loss.call(self, @settings) if @on_tcp_connection_loss
|
264
|
+
end
|
265
|
+
|
266
|
+
# Called when initial TCP connection fails.
|
267
|
+
# @api public
|
268
|
+
def tcp_connection_failed
|
269
|
+
@on_tcp_connection_failure.call(@settings) if @on_tcp_connection_failure
|
270
|
+
end
|
271
|
+
|
272
|
+
|
273
|
+
|
274
|
+
#
|
275
|
+
# Implementation
|
276
|
+
#
|
277
|
+
|
278
|
+
|
204
279
|
# Sends opaque data to AMQ broker over active connection.
|
205
280
|
#
|
206
281
|
# @note This must be implemented by all AMQP clients.
|
@@ -209,6 +284,35 @@ module AMQ
|
|
209
284
|
raise MissingInterfaceMethodError.new("AMQ::Client#send_raw(data)")
|
210
285
|
end
|
211
286
|
|
287
|
+
# Sends connection preamble to the broker.
|
288
|
+
def handshake
|
289
|
+
@authenticating = true
|
290
|
+
self.send_preamble
|
291
|
+
end
|
292
|
+
|
293
|
+
|
294
|
+
# Sends connection.open to the server.
|
295
|
+
#
|
296
|
+
# @api plugin
|
297
|
+
# @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.4.2.7)
|
298
|
+
def open(vhost = "/")
|
299
|
+
self.send Protocol::Connection::Open.encode(vhost)
|
300
|
+
end
|
301
|
+
|
302
|
+
# Resets connection state.
|
303
|
+
#
|
304
|
+
# @api plugin
|
305
|
+
def reset_state!
|
306
|
+
# no-op by default
|
307
|
+
end # reset_state!
|
308
|
+
|
309
|
+
# @api plugin
|
310
|
+
# @see http://tools.ietf.org/rfc/rfc2595.txt RFC 2595
|
311
|
+
def encode_credentials(username, password)
|
312
|
+
"\0#{username}\0#{password}"
|
313
|
+
end # encode_credentials(username, password)
|
314
|
+
|
315
|
+
|
212
316
|
def receive_frame(frame)
|
213
317
|
@frames << frame
|
214
318
|
if frameset_complete?(@frames)
|
@@ -251,17 +355,100 @@ module AMQ
|
|
251
355
|
end # send_heartbeat
|
252
356
|
|
253
357
|
|
254
|
-
|
255
|
-
#
|
358
|
+
|
359
|
+
# Handles Connection.Start.
|
256
360
|
#
|
257
|
-
# @
|
361
|
+
# @api plugin
|
362
|
+
# @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.4.2.1.)
|
363
|
+
def start_ok(method)
|
364
|
+
@server_properties = method.server_properties
|
365
|
+
|
366
|
+
username = @settings[:user] || @settings[:username]
|
367
|
+
password = @settings[:pass] || @settings[:password]
|
368
|
+
|
369
|
+
# It's not clear whether we should transition to :opening state here
|
370
|
+
# or in #open but in case authentication fails, it would be strange to have
|
371
|
+
# @status undefined. So lets do this. MK.
|
372
|
+
opening!
|
373
|
+
|
374
|
+
self.send Protocol::Connection::StartOk.encode(@client_properties, @mechanism, self.encode_credentials(username, password), @locale)
|
375
|
+
end
|
376
|
+
|
377
|
+
|
378
|
+
# Handles Connection.Open-Ok.
|
379
|
+
#
|
380
|
+
# @api plugin
|
381
|
+
# @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.4.2.8.)
|
382
|
+
def handle_open_ok(method)
|
383
|
+
@known_hosts = method.known_hosts
|
384
|
+
|
385
|
+
opened!
|
386
|
+
self.connection_successful if self.respond_to?(:connection_successful)
|
387
|
+
end
|
388
|
+
|
389
|
+
# Handles Connection.Tune-Ok.
|
390
|
+
#
|
391
|
+
# @api plugin
|
258
392
|
# @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.4.2.6)
|
259
|
-
def
|
260
|
-
@
|
261
|
-
|
393
|
+
def handle_tune(method)
|
394
|
+
@channel_max = method.channel_max
|
395
|
+
@frame_max = method.frame_max
|
396
|
+
@heartbeat_interval = self.heartbeat_interval || method.heartbeat
|
397
|
+
|
398
|
+
self.send Protocol::Connection::TuneOk.encode(@channel_max, [settings[:frame_max], @frame_max].min, @heartbeat_interval)
|
399
|
+
end # handle_tune(method)
|
400
|
+
|
401
|
+
|
402
|
+
# Handles connection.close. When broker detects a connection level exception, this method is called.
|
403
|
+
#
|
404
|
+
# @api plugin
|
405
|
+
# @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.5.2.9)
|
406
|
+
def handle_close(method)
|
407
|
+
self.handle_connection_interruption
|
408
|
+
|
409
|
+
closed!
|
410
|
+
# TODO: use proper exception class, provide protocol class (we know method.class_id and method.method_id) as well!
|
411
|
+
error = RuntimeError.new(method.reply_text)
|
412
|
+
self.error(error)
|
413
|
+
end
|
414
|
+
|
415
|
+
|
416
|
+
# Handles Connection.Close-Ok.
|
417
|
+
#
|
418
|
+
# @api plugin
|
419
|
+
# @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.4.2.10)
|
420
|
+
def handle_close_ok(method)
|
421
|
+
closed!
|
422
|
+
self.disconnection_successful
|
423
|
+
end # handle_close_ok(method)
|
424
|
+
|
425
|
+
# @api plugin
|
426
|
+
def handle_connection_interruption
|
427
|
+
@channels.each { |n, c| c.handle_connection_interruption }
|
428
|
+
end # handle_connection_interruption
|
429
|
+
|
430
|
+
|
262
431
|
|
263
432
|
protected
|
264
433
|
|
434
|
+
# Returns next frame from buffer whenever possible
|
435
|
+
#
|
436
|
+
# @api private
|
437
|
+
def get_next_frame
|
438
|
+
return nil unless @chunk_buffer.size > 7 # otherwise, cannot read the length
|
439
|
+
# octet + short
|
440
|
+
offset = 3 # 1 + 2
|
441
|
+
# length
|
442
|
+
payload_length = @chunk_buffer[offset, 4].unpack('N')[0]
|
443
|
+
# 4 bytes for long payload length, 1 byte final octet
|
444
|
+
frame_length = offset + 4 + payload_length + 1
|
445
|
+
if frame_length <= @chunk_buffer.size
|
446
|
+
@chunk_buffer.slice!(0, frame_length)
|
447
|
+
else
|
448
|
+
nil
|
449
|
+
end
|
450
|
+
end # def get_next_frame
|
451
|
+
|
265
452
|
# Utility methods
|
266
453
|
|
267
454
|
# Determines, whether the received frameset is ready to be further processed
|