amq-client 0.7.0.alpha25 → 0.7.0.alpha26
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/.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
|