amq-client 0.7.0.alpha3 → 0.7.0.alpha4
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/Gemfile +4 -3
- data/amq-client.gemspec +1 -1
- data/examples/coolio_adapter/example_helper.rb +1 -1
- data/examples/eventmachine_adapter/connection_loss_handler.rb +43 -0
- data/lib/amq/client.rb +55 -0
- data/lib/amq/client/adapter.rb +17 -49
- data/lib/amq/client/adapters/coolio.rb +16 -66
- data/lib/amq/client/adapters/event_machine.rb +130 -45
- data/lib/amq/client/adapters/socket.rb +9 -8
- data/lib/amq/client/channel.rb +23 -5
- data/lib/amq/client/connection.rb +6 -5
- data/lib/amq/client/entity.rb +55 -40
- data/lib/amq/client/version.rb +1 -1
- data/spec/benchmarks/adapters.rb +1 -1
- data/spec/integration/coolio/basic_ack_spec.rb +1 -4
- data/spec/integration/coolio/basic_get_spec.rb +1 -1
- data/spec/integration/coolio/basic_return_spec.rb +1 -1
- data/spec/integration/coolio/channel_close_spec.rb +1 -1
- data/spec/integration/coolio/channel_flow_spec.rb +1 -1
- data/spec/integration/coolio/spec_helper.rb +1 -1
- data/spec/integration/coolio/tx_commit_spec.rb +1 -1
- data/spec/integration/coolio/tx_rollback_spec.rb +1 -1
- data/spec/regression/bad_frame_slicing_in_adapters_spec.rb +1 -1
- data/spec/spec_helper.rb +1 -0
- data/spec/unit/client_spec.rb +74 -0
- metadata +209 -187
data/.gitignore
CHANGED
data/Gemfile
CHANGED
@@ -3,16 +3,17 @@
|
|
3
3
|
source :rubygems
|
4
4
|
|
5
5
|
# Use local clones if possible.
|
6
|
+
# If you want to use your local copy, just symlink it to vendor.
|
6
7
|
def custom_gem(name, options = Hash.new)
|
7
|
-
local_path = File.expand_path("
|
8
|
-
if
|
8
|
+
local_path = File.expand_path("../vendor/#{name}", __FILE__)
|
9
|
+
if File.exist?(local_path)
|
9
10
|
gem name, options.merge(:path => local_path).delete_if { |key, _| [:git, :branch].include?(key) }
|
10
11
|
else
|
11
12
|
gem name, options
|
12
13
|
end
|
13
14
|
end
|
14
15
|
|
15
|
-
gem "eventmachine"
|
16
|
+
gem "eventmachine"
|
16
17
|
# cool.io uses iobuffer that won't compile on JRuby
|
17
18
|
# (and, probably, Windows)
|
18
19
|
gem "cool.io", :platform => :ruby
|
data/amq-client.gemspec
CHANGED
@@ -17,7 +17,7 @@ end
|
|
17
17
|
|
18
18
|
|
19
19
|
def amq_client_example(description = "", &block)
|
20
|
-
AMQ::Client::
|
20
|
+
AMQ::Client::CoolioClient.connect(:port => 5672, :vhost => "/amq_client_testbed") do |client|
|
21
21
|
begin
|
22
22
|
puts
|
23
23
|
puts
|
@@ -0,0 +1,43 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
__dir = File.join(File.dirname(File.expand_path(__FILE__)))
|
5
|
+
require File.join(__dir, "example_helper")
|
6
|
+
|
7
|
+
|
8
|
+
EM.run do
|
9
|
+
reconnector = Proc.new { |client, settings|
|
10
|
+
puts "Asked to reconnect to #{settings[:host]}:#{settings[:port]}"
|
11
|
+
|
12
|
+
|
13
|
+
}
|
14
|
+
|
15
|
+
|
16
|
+
AMQ::Client::EventMachineClient.connect(:port => 5672,
|
17
|
+
:vhost => "/amq_client_testbed",
|
18
|
+
:user => "amq_client_gem",
|
19
|
+
:password => "amq_client_gem_password",
|
20
|
+
:connection_timeout => 0.3,
|
21
|
+
:on_tcp_connection_failure => Proc.new { |settings| puts "Failed to connect, this was NOT expected"; EM.stop }) do |client|
|
22
|
+
|
23
|
+
client.on_tcp_connection_loss do |cl, settings|
|
24
|
+
puts "tcp_connection_loss handler kicks in"
|
25
|
+
cl.reconnect(1)
|
26
|
+
end
|
27
|
+
|
28
|
+
show_stopper = Proc.new {
|
29
|
+
client.disconnect {
|
30
|
+
puts "Disconnected. Exiting…"
|
31
|
+
EM.stop
|
32
|
+
}
|
33
|
+
}
|
34
|
+
|
35
|
+
Signal.trap "INT", show_stopper
|
36
|
+
Signal.trap "TERM", show_stopper
|
37
|
+
|
38
|
+
EM.add_timer(30, show_stopper)
|
39
|
+
|
40
|
+
|
41
|
+
puts "Connected, authenticated. To really exercise this example, shut AMQP broker down for a few seconds. If you don't it will exit gracefully in 30 seconds."
|
42
|
+
end
|
43
|
+
end
|
data/lib/amq/client.rb
CHANGED
@@ -3,6 +3,9 @@
|
|
3
3
|
require "amq/client/version"
|
4
4
|
require "amq/client/exceptions"
|
5
5
|
require "amq/client/adapter"
|
6
|
+
require "amq/client/channel"
|
7
|
+
require "amq/client/exchange"
|
8
|
+
require "amq/client/queue"
|
6
9
|
|
7
10
|
begin
|
8
11
|
require "amq/protocol/client"
|
@@ -13,3 +16,55 @@ rescue LoadError => exception
|
|
13
16
|
raise exception
|
14
17
|
end
|
15
18
|
end
|
19
|
+
|
20
|
+
module AMQ
|
21
|
+
module Client
|
22
|
+
# List all the available as a hash of {adapter_name: metadata},
|
23
|
+
# where metadata are hash with :path and :const_name keys.
|
24
|
+
#
|
25
|
+
# @example
|
26
|
+
# AMQ::Client.adapters[:socket]
|
27
|
+
# # => {path: full_path, const_name: "SocketClient"}}
|
28
|
+
# @return [Hash]
|
29
|
+
# @api public
|
30
|
+
def self.adapters
|
31
|
+
@adapters ||= begin
|
32
|
+
root = File.expand_path("../client/adapters", __FILE__)
|
33
|
+
Dir.glob("#{root}/*.rb").inject(Hash.new) do |buffer, path|
|
34
|
+
name = path.match(/([^\/]+)\.rb$/)[1]
|
35
|
+
const_base = name.to_s.gsub(/(^|_)(.)/) { $2.upcase! }
|
36
|
+
meta = {:path => path, :const_name => "#{const_base}Client"}
|
37
|
+
buffer.merge!(name.to_sym => meta)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Establishes connection to AMQ broker using given adapter
|
43
|
+
# (defaults to the socket adapter) and returns it. The new
|
44
|
+
# connection object is yielded to the block if it is given.
|
45
|
+
#
|
46
|
+
# @example
|
47
|
+
# AMQ::Client.connect(adapter: "socket") do |client|
|
48
|
+
# # Use the client.
|
49
|
+
# end
|
50
|
+
# @param [Hash] Connection parameters, including :adapter to use.
|
51
|
+
# @api public
|
52
|
+
def self.connect(settings = nil, &block)
|
53
|
+
adapter = (settings && settings.delete(:adapter)) || :socket
|
54
|
+
adapter = load_adapter(adapter)
|
55
|
+
adapter.connect(settings, &block)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Loads adapter from amq/client/adapters.
|
59
|
+
#
|
60
|
+
# @raise [InvalidAdapterNameError] When loading attempt failed (LoadError was raised).
|
61
|
+
def self.load_adapter(adapter)
|
62
|
+
meta = self.adapters[adapter.to_sym]
|
63
|
+
|
64
|
+
require meta[:path]
|
65
|
+
const_get(meta[:const_name])
|
66
|
+
rescue LoadError
|
67
|
+
raise InvalidAdapterNameError.new(adapter)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/lib/amq/client/adapter.rb
CHANGED
@@ -4,6 +4,7 @@ require "amq/client/logging"
|
|
4
4
|
require "amq/client/settings"
|
5
5
|
require "amq/client/entity"
|
6
6
|
require "amq/client/connection"
|
7
|
+
require "amq/client/channel"
|
7
8
|
|
8
9
|
module AMQ
|
9
10
|
# For overview of AMQP client adapters API, see {AMQ::Client::Adapter}
|
@@ -104,55 +105,25 @@ module AMQ
|
|
104
105
|
end
|
105
106
|
|
106
107
|
|
107
|
-
# @example Registering Channel implementation
|
108
|
-
# Adapter.register_entity(:channel, Channel)
|
109
|
-
# # ... so then I can do:
|
110
|
-
# channel = client.channel(1)
|
111
|
-
# # instead of:
|
112
|
-
# channel = Channel.new(client, 1)
|
113
|
-
def register_entity(name, klass)
|
114
|
-
define_method(name) do |*args, &block|
|
115
|
-
klass.new(self, *args, &block)
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
108
|
# Establishes connection to AMQ broker and returns it. New connection object is yielded to
|
120
109
|
# the block if it is given.
|
121
110
|
#
|
111
|
+
# @example Specifying adapter via the :adapter option
|
112
|
+
# AMQ::Client::Adapter.connect(adapter: "socket")
|
113
|
+
# @example Specifying using custom adapter class
|
114
|
+
# AMQ::Client::SocketClient.connect
|
122
115
|
# @param [Hash] Connection parameters, including :adapter to use.
|
123
116
|
# @api public
|
124
117
|
def connect(settings = nil, &block)
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
adapter = self
|
129
|
-
end
|
118
|
+
# TODO: this doesn't look very nice, do we need it?
|
119
|
+
# Let's make it an instance thing by instance = self.new(settings)
|
120
|
+
@settings = settings = Settings.configure(settings)
|
130
121
|
|
131
|
-
|
132
|
-
instance
|
133
|
-
instance.
|
134
|
-
# We don't need anything more, once the server receives the preable, he sends Connection.Start, we just have to reply.
|
122
|
+
instance = self.new
|
123
|
+
instance.establish_connection(settings)
|
124
|
+
instance.register_connection_callback(&block)
|
135
125
|
|
136
|
-
|
137
|
-
block.call(instance)
|
138
|
-
|
139
|
-
instance.disconnect
|
140
|
-
else
|
141
|
-
instance
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
|
146
|
-
# Loads adapter from amq/client/adapters.
|
147
|
-
#
|
148
|
-
# @raise [InvalidAdapterNameError] When loading attempt failed (LoadError was raised).
|
149
|
-
def load_adapter(adapter)
|
150
|
-
require "amq/client/adapters/#{adapter}"
|
151
|
-
|
152
|
-
const_name = adapter.to_s.gsub(/(^|_)(.)/) { $2.upcase! }
|
153
|
-
const_get(const_name)
|
154
|
-
rescue LoadError
|
155
|
-
raise InvalidAdapterNameError.new(adapter)
|
126
|
+
instance
|
156
127
|
end
|
157
128
|
|
158
129
|
# @see AMQ::Client::Adapter
|
@@ -184,17 +155,14 @@ module AMQ
|
|
184
155
|
|
185
156
|
include AMQ::Client::StatusMixin
|
186
157
|
|
158
|
+
extend RegisterEntityMixin
|
159
|
+
|
160
|
+
register_entity :channel, AMQ::Client::Channel
|
187
161
|
|
188
162
|
#
|
189
163
|
# API
|
190
164
|
#
|
191
165
|
|
192
|
-
def self.load_adapter(adapter)
|
193
|
-
ClassMethods.load_adapter(adapter)
|
194
|
-
end
|
195
|
-
|
196
|
-
|
197
|
-
|
198
166
|
def initialize(*args)
|
199
167
|
super(*args)
|
200
168
|
|
@@ -229,6 +197,8 @@ module AMQ
|
|
229
197
|
# @todo This method should await broker's response with Close-Ok. {http://github.com/michaelklishin MK}.
|
230
198
|
# @see #close_connection
|
231
199
|
def disconnect(reply_code = 200, reply_text = "Goodbye", &block)
|
200
|
+
@intentionally_closing_connection = true
|
201
|
+
|
232
202
|
self.on_disconnection(&block)
|
233
203
|
closing!
|
234
204
|
self.connection.close(reply_code, reply_text)
|
@@ -352,5 +322,3 @@ module AMQ
|
|
352
322
|
end # Adapter
|
353
323
|
end # Client
|
354
324
|
end # AMQ
|
355
|
-
|
356
|
-
require "amq/client/channel"
|
@@ -4,12 +4,11 @@
|
|
4
4
|
|
5
5
|
require "cool.io"
|
6
6
|
require "amq/client"
|
7
|
-
|
8
7
|
require "amq/client/framing/string/frame"
|
9
8
|
|
10
9
|
module AMQ
|
11
10
|
module Client
|
12
|
-
class
|
11
|
+
class CoolioClient
|
13
12
|
class Socket < ::Coolio::TCPSocket
|
14
13
|
attr_accessor :adapter
|
15
14
|
|
@@ -48,27 +47,31 @@ module AMQ
|
|
48
47
|
end
|
49
48
|
end
|
50
49
|
|
50
|
+
#
|
51
51
|
# Behaviors
|
52
|
+
#
|
53
|
+
|
52
54
|
include AMQ::Client::Adapter
|
55
|
+
include AMQ::Client::CallbacksMixin
|
53
56
|
|
54
57
|
self.sync = false
|
55
58
|
|
59
|
+
#
|
56
60
|
# API
|
61
|
+
#
|
62
|
+
|
57
63
|
attr_accessor :socket
|
58
64
|
attr_accessor :callbacks
|
59
65
|
attr_accessor :connections
|
60
66
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
instance.on_connection(&block)
|
70
|
-
instance
|
71
|
-
end
|
67
|
+
def establish_connection(settings)
|
68
|
+
socket = Socket.connect(self, settings[:host], settings[:port])
|
69
|
+
socket.attach(Cool.io::Loop.default)
|
70
|
+
self.socket = socket
|
71
|
+
end
|
72
|
+
|
73
|
+
def register_connection_callback(&block)
|
74
|
+
self.on_connection(&block)
|
72
75
|
end
|
73
76
|
|
74
77
|
def initialize
|
@@ -138,59 +141,6 @@ module AMQ
|
|
138
141
|
end
|
139
142
|
|
140
143
|
|
141
|
-
|
142
|
-
#
|
143
|
-
# Callbacks
|
144
|
-
#
|
145
|
-
|
146
|
-
def redefine_callback(event, callable = nil, &block)
|
147
|
-
f = (callable || block)
|
148
|
-
# yes, re-assign!
|
149
|
-
@callbacks[event] = [f]
|
150
|
-
|
151
|
-
self
|
152
|
-
end
|
153
|
-
|
154
|
-
def define_callback(event, callable = nil, &block)
|
155
|
-
f = (callable || block)
|
156
|
-
@callbacks[event] ||= []
|
157
|
-
|
158
|
-
@callbacks[event] << f if f
|
159
|
-
|
160
|
-
self
|
161
|
-
end # define_callback(event, &block)
|
162
|
-
alias append_callback define_callback
|
163
|
-
|
164
|
-
def prepend_callback(event, &block)
|
165
|
-
@callbacks[event] ||= []
|
166
|
-
@callbacks[event].unshift(block)
|
167
|
-
|
168
|
-
self
|
169
|
-
end # prepend_callback(event, &block)
|
170
|
-
|
171
|
-
|
172
|
-
def exec_callback(name, *args, &block)
|
173
|
-
callbacks = Array(self.callbacks[name])
|
174
|
-
callbacks.map { |c| c.call(*args, &block) } if callbacks.any?
|
175
|
-
end
|
176
|
-
|
177
|
-
def exec_callback_once(name, *args, &block)
|
178
|
-
callbacks = Array(self.callbacks.delete(name))
|
179
|
-
callbacks.map { |c| c.call(*args, &block) } if callbacks.any?
|
180
|
-
end
|
181
|
-
|
182
|
-
def exec_callback_yielding_self(name, *args, &block)
|
183
|
-
callbacks = Array(self.callbacks[name])
|
184
|
-
callbacks.map { |c| c.call(self, *args, &block) } if callbacks.any?
|
185
|
-
end
|
186
|
-
|
187
|
-
def exec_callback_once_yielding_self(name, *args, &block)
|
188
|
-
callbacks = Array(self.callbacks.delete(name))
|
189
|
-
callbacks.map { |c| c.call(self, *args, &block) } if callbacks.any?
|
190
|
-
end
|
191
|
-
|
192
|
-
|
193
|
-
|
194
144
|
protected
|
195
145
|
|
196
146
|
def post_init
|
@@ -1,12 +1,9 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
+
require "eventmachine"
|
3
4
|
require "amq/client"
|
4
|
-
require "amq/client/channel"
|
5
|
-
require "amq/client/exchange"
|
6
5
|
require "amq/client/framing/string/frame"
|
7
6
|
|
8
|
-
require "eventmachine"
|
9
|
-
|
10
7
|
module AMQ
|
11
8
|
module Client
|
12
9
|
class EventMachineClient < EM::Connection
|
@@ -23,27 +20,52 @@ module AMQ
|
|
23
20
|
|
24
21
|
self.sync = false
|
25
22
|
|
26
|
-
register_entity :channel, AMQ::Client::Channel
|
27
|
-
register_entity :exchange, AMQ::Client::Exchange
|
28
|
-
|
29
23
|
#
|
30
24
|
# API
|
31
25
|
#
|
32
26
|
|
33
27
|
def self.connect(settings = nil, &block)
|
34
|
-
settings =
|
28
|
+
@settings = settings = Settings.configure(settings)
|
29
|
+
|
35
30
|
instance = EventMachine.connect(settings[:host], settings[:port], self, settings)
|
31
|
+
instance.register_connection_callback(&block)
|
32
|
+
|
33
|
+
instance
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
def reconnect(period = 5, force = false)
|
38
|
+
if @reconnecting and not force
|
39
|
+
EventMachine::Timer.new(period) {
|
40
|
+
reconnect(period, true)
|
41
|
+
}
|
42
|
+
return
|
43
|
+
end
|
44
|
+
|
45
|
+
if !@reconnecting
|
46
|
+
@reconnecting = true
|
47
|
+
@connections.each { |c| c.handle_connection_interruption }
|
48
|
+
self.reset
|
49
|
+
end
|
50
|
+
|
51
|
+
self.reconnect(@settings[:host], @settings[:port])
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
def establish_connection(settings)
|
56
|
+
# Unfortunately there doesn't seem to be any sane way
|
57
|
+
# how to get EventMachine connect to the instance level.
|
58
|
+
end
|
36
59
|
|
60
|
+
def register_connection_callback(&block)
|
37
61
|
unless block.nil?
|
38
62
|
# delay calling block we were given till after we receive
|
39
63
|
# connection.open-ok. Connection will notify us when
|
40
64
|
# that happens.
|
41
|
-
|
42
|
-
block.call(
|
65
|
+
self.on_connection do
|
66
|
+
block.call(self)
|
43
67
|
end
|
44
68
|
end
|
45
|
-
|
46
|
-
instance
|
47
69
|
end
|
48
70
|
|
49
71
|
|
@@ -53,24 +75,21 @@ module AMQ
|
|
53
75
|
def initialize(*args)
|
54
76
|
super(*args)
|
55
77
|
|
78
|
+
@connections = Array.new
|
79
|
+
# track TCP connection state, used to detect initial TCP connection failures.
|
80
|
+
@tcp_connection_established = false
|
81
|
+
@tcp_connection_failed = false
|
82
|
+
@intentionally_closing_connection = false
|
83
|
+
|
56
84
|
# EventMachine::Connection's and Adapter's constructors arity
|
57
85
|
# make it easier to use *args. MK.
|
58
|
-
@settings
|
59
|
-
@connections = Array.new
|
86
|
+
@settings = args.first
|
60
87
|
@on_possible_authentication_failure = @settings[:on_possible_authentication_failure]
|
61
|
-
@on_tcp_connection_failure = @settings[:on_tcp_connection_failure] || Proc.new { |settings|
|
88
|
+
@on_tcp_connection_failure = @settings[:on_tcp_connection_failure] || Proc.new { |settings|
|
89
|
+
raise AMQ::Client::TCPConnectionFailed.new(settings)
|
90
|
+
}
|
62
91
|
|
63
|
-
|
64
|
-
@connection_deferrable = Deferrable.new
|
65
|
-
@disconnection_deferrable = Deferrable.new
|
66
|
-
|
67
|
-
@authenticating = false
|
68
|
-
|
69
|
-
# succeeds when connection is open, that is, vhost is selected
|
70
|
-
# and client is given green light to proceed.
|
71
|
-
@connection_opened_deferrable = Deferrable.new
|
72
|
-
|
73
|
-
@tcp_connection_established = false
|
92
|
+
self.reset
|
74
93
|
|
75
94
|
if self.heartbeat_interval > 0
|
76
95
|
@last_server_heartbeat = Time.now
|
@@ -79,10 +98,6 @@ module AMQ
|
|
79
98
|
end # initialize(*args)
|
80
99
|
|
81
100
|
|
82
|
-
def establish_connection(settings)
|
83
|
-
# an intentional no-op
|
84
|
-
end
|
85
|
-
|
86
101
|
alias send_raw send_data
|
87
102
|
|
88
103
|
|
@@ -111,8 +126,6 @@ module AMQ
|
|
111
126
|
# to take some time and to not be worth in as long as #post_init
|
112
127
|
# works fine. MK.
|
113
128
|
upgrade_to_tls_if_necessary
|
114
|
-
|
115
|
-
self.handshake
|
116
129
|
rescue Exception => error
|
117
130
|
raise error
|
118
131
|
end # post_init
|
@@ -121,8 +134,22 @@ module AMQ
|
|
121
134
|
def connection_completed
|
122
135
|
# we only can safely set this value here because EventMachine is a lovely piece of
|
123
136
|
# software that calls #post_init before #unbind even when TCP connection
|
124
|
-
# fails.
|
125
|
-
@tcp_connection_established
|
137
|
+
# fails. MK.
|
138
|
+
@tcp_connection_established = true
|
139
|
+
# again, this is because #unbind is called in different situations
|
140
|
+
# and there is no easy way to tell initial connection failure
|
141
|
+
# from connection loss. Not in EventMachine 0.12.x, anyway. MK.
|
142
|
+
@had_successfull_connected_before = true
|
143
|
+
|
144
|
+
@reconnecting = false
|
145
|
+
|
146
|
+
self.handshake
|
147
|
+
end
|
148
|
+
|
149
|
+
def close_connection(*args)
|
150
|
+
@intentionally_closing_connection = true
|
151
|
+
|
152
|
+
super(*args)
|
126
153
|
end
|
127
154
|
|
128
155
|
# Called by EventMachine reactor when
|
@@ -132,19 +159,22 @@ module AMQ
|
|
132
159
|
# * There is a network connection issue
|
133
160
|
# * Initial TCP connection fails
|
134
161
|
def unbind
|
135
|
-
if !@tcp_connection_established
|
162
|
+
if !@tcp_connection_established && !@had_successfull_connected_before
|
163
|
+
@tcp_connection_failed = true
|
136
164
|
self.tcp_connection_failed
|
137
165
|
end
|
138
166
|
|
139
167
|
closing!
|
140
|
-
|
141
168
|
@tcp_connection_established = false
|
142
169
|
|
143
|
-
@connections.each { |c| c.
|
170
|
+
@connections.each { |c| c.handle_connection_interruption }
|
144
171
|
@disconnection_deferrable.succeed
|
145
172
|
|
146
173
|
closed!
|
147
174
|
|
175
|
+
|
176
|
+
self.tcp_connection_lost if !@intentionally_closing_connection && @had_successfull_connected_before
|
177
|
+
|
148
178
|
# since AMQP spec dictates that authentication failure is a protocol exception
|
149
179
|
# and protocol exceptions result in connection closure, check whether we are
|
150
180
|
# in the authentication stage. If so, it is likely to signal an authentication
|
@@ -170,23 +200,35 @@ module AMQ
|
|
170
200
|
end
|
171
201
|
end
|
172
202
|
|
173
|
-
|
174
|
-
|
203
|
+
# Defines a callback that will be executed when AMQP connection is considered open,
|
204
|
+
# after client and broker has agreed on max channel identifier and maximum allowed frame
|
205
|
+
# size. You can define more than one callback.
|
206
|
+
#
|
207
|
+
# @see #on_open
|
208
|
+
# @api public
|
175
209
|
def on_connection(&block)
|
176
210
|
@connection_deferrable.callback(&block)
|
177
211
|
end # on_connection(&block)
|
178
212
|
|
179
|
-
#
|
213
|
+
# Called by AMQ::Client::Connection after we receive connection.open-ok.
|
214
|
+
# @api public
|
180
215
|
def connection_successful
|
181
216
|
@connection_deferrable.succeed
|
182
217
|
end # connection_successful
|
183
218
|
|
184
219
|
|
185
|
-
|
220
|
+
# Defines a callback that will be executed when AMQP connection is considered open,
|
221
|
+
# before client and broker has agreed on max channel identifier and maximum allowed frame
|
222
|
+
# size. You can define more than one callback.
|
223
|
+
#
|
224
|
+
# @see #on_connection
|
225
|
+
# @api public
|
186
226
|
def on_open(&block)
|
187
227
|
@connection_opened_deferrable.callback(&block)
|
188
228
|
end # on_open(&block)
|
189
229
|
|
230
|
+
# Called by AMQ::Client::Connection after we receive connection.tune.
|
231
|
+
# @api public
|
190
232
|
def open_successful
|
191
233
|
@authenticating = false
|
192
234
|
@connection_opened_deferrable.succeed
|
@@ -195,31 +237,61 @@ module AMQ
|
|
195
237
|
end # open_successful
|
196
238
|
|
197
239
|
|
198
|
-
|
240
|
+
# Defines a callback that will be run when broker confirms connection termination
|
241
|
+
# (client receives connection.close-ok). You can define more than one callback.
|
242
|
+
#
|
243
|
+
# @api public
|
199
244
|
def on_disconnection(&block)
|
200
245
|
@disconnection_deferrable.callback(&block)
|
201
246
|
end # on_disconnection(&block)
|
202
247
|
|
203
|
-
#
|
248
|
+
# Called by AMQ::Client::Connection after we receive connection.close-ok.
|
249
|
+
#
|
250
|
+
# @api public
|
204
251
|
def disconnection_successful
|
205
252
|
@disconnection_deferrable.succeed
|
206
253
|
|
207
254
|
self.close_connection
|
255
|
+
self.reset
|
208
256
|
closed!
|
209
257
|
end # disconnection_successful
|
210
258
|
|
211
259
|
|
212
|
-
|
260
|
+
# Defines a callback that will be run when initial TCP connection fails.
|
261
|
+
# You can define only one callback.
|
262
|
+
#
|
263
|
+
# @api public
|
213
264
|
def on_tcp_connection_failure(&block)
|
214
265
|
@on_tcp_connection_failure = block
|
215
266
|
end
|
216
267
|
|
268
|
+
# Called when initial TCP connection fails.
|
269
|
+
# @api public
|
217
270
|
def tcp_connection_failed
|
218
271
|
@on_tcp_connection_failure.call(@settings) if @on_tcp_connection_failure
|
219
272
|
end
|
220
273
|
|
221
274
|
|
275
|
+
# Defines a callback that will be run when initial TCP connection fails.
|
276
|
+
# You can define only one callback.
|
277
|
+
#
|
278
|
+
# @api public
|
279
|
+
def on_tcp_connection_loss(&block)
|
280
|
+
@on_tcp_connection_loss = block
|
281
|
+
end
|
282
|
+
|
283
|
+
# Called when initial TCP connection fails.
|
284
|
+
# @api public
|
285
|
+
def tcp_connection_lost
|
286
|
+
@on_tcp_connection_loss.call(self, @settings) if @on_tcp_connection_loss
|
287
|
+
end
|
288
|
+
|
289
|
+
|
222
290
|
|
291
|
+
# Defines a callback that will be run when TCP connection is closed before authentication
|
292
|
+
# finishes. Usually this means authentication failure. You can define only one callback.
|
293
|
+
#
|
294
|
+
# @api public
|
223
295
|
def on_possible_authentication_failure(&block)
|
224
296
|
@on_possible_authentication_failure = block
|
225
297
|
end
|
@@ -243,6 +315,19 @@ module AMQ
|
|
243
315
|
@size = 0
|
244
316
|
@payload = ""
|
245
317
|
@frames = Array.new
|
318
|
+
|
319
|
+
@chunk_buffer = ""
|
320
|
+
@connection_deferrable = Deferrable.new
|
321
|
+
@disconnection_deferrable = Deferrable.new
|
322
|
+
# succeeds when connection is open, that is, vhost is selected
|
323
|
+
# and client is given green light to proceed.
|
324
|
+
@connection_opened_deferrable = Deferrable.new
|
325
|
+
|
326
|
+
# used to track down whether authentication succeeded. AMQP 0.9.1 dictates
|
327
|
+
# that on authentication failure broker must close TCP connection without sending
|
328
|
+
# any more data. This is why we need to explicitly track whether we are past
|
329
|
+
# authentication stage to signal possible authentication failures.
|
330
|
+
@authenticating = false
|
246
331
|
end
|
247
332
|
|
248
333
|
# @see http://tools.ietf.org/rfc/rfc2595.txt RFC 2595
|
@@ -272,7 +357,7 @@ module AMQ
|
|
272
357
|
start_tls(tls_options)
|
273
358
|
elsif tls_options
|
274
359
|
start_tls
|
275
|
-
end
|
360
|
+
end
|
276
361
|
end
|
277
362
|
end # EventMachineClient
|
278
363
|
end # Client
|