gorgon 0.5.0.rc1 → 0.6.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. data/Gemfile.lock +2 -4
  2. data/gorgon.gemspec +0 -1
  3. data/lib/gorgon/amqp_service.rb +5 -5
  4. data/lib/gorgon/gem_command_handler.rb +2 -1
  5. data/lib/gorgon/listener.rb +7 -5
  6. data/lib/gorgon/originator_protocol.rb +1 -0
  7. data/lib/gorgon/version.rb +1 -1
  8. data/lib/gorgon/worker_manager.rb +5 -2
  9. data/lib/gorgon_amq-protocol/.gitignore +15 -0
  10. data/lib/gorgon_amq-protocol/.gitmodules +3 -0
  11. data/lib/gorgon_amq-protocol/.rspec +3 -0
  12. data/lib/gorgon_amq-protocol/.travis.yml +19 -0
  13. data/lib/gorgon_amq-protocol/lib/gorgon_amq/bit_set.rb +82 -0
  14. data/lib/gorgon_amq-protocol/lib/gorgon_amq/endianness.rb +15 -0
  15. data/lib/gorgon_amq-protocol/lib/gorgon_amq/int_allocator.rb +96 -0
  16. data/lib/gorgon_amq-protocol/lib/gorgon_amq/pack.rb +53 -0
  17. data/lib/gorgon_amq-protocol/lib/gorgon_amq/protocol.rb +4 -0
  18. data/lib/gorgon_amq-protocol/lib/gorgon_amq/protocol/client.rb +2322 -0
  19. data/lib/gorgon_amq-protocol/lib/gorgon_amq/protocol/constants.rb +22 -0
  20. data/lib/gorgon_amq-protocol/lib/gorgon_amq/protocol/exceptions.rb +60 -0
  21. data/lib/gorgon_amq-protocol/lib/gorgon_amq/protocol/float_32bit.rb +14 -0
  22. data/lib/gorgon_amq-protocol/lib/gorgon_amq/protocol/frame.rb +210 -0
  23. data/lib/gorgon_amq-protocol/lib/gorgon_amq/protocol/table.rb +142 -0
  24. data/lib/gorgon_amq-protocol/lib/gorgon_amq/protocol/table_value_decoder.rb +190 -0
  25. data/lib/gorgon_amq-protocol/lib/gorgon_amq/protocol/table_value_encoder.rb +123 -0
  26. data/lib/gorgon_amq-protocol/lib/gorgon_amq/protocol/type_constants.rb +26 -0
  27. data/lib/gorgon_amq-protocol/lib/gorgon_amq/protocol/version.rb +5 -0
  28. data/lib/gorgon_amq-protocol/lib/gorgon_amq/settings.rb +114 -0
  29. data/lib/gorgon_amq-protocol/lib/gorgon_amq/uri.rb +37 -0
  30. data/lib/gorgon_bunny/lib/gorgon_amq/protocol/extensions.rb +16 -0
  31. data/lib/gorgon_bunny/lib/gorgon_bunny.rb +89 -0
  32. data/lib/gorgon_bunny/lib/gorgon_bunny/authentication/credentials_encoder.rb +55 -0
  33. data/lib/gorgon_bunny/lib/gorgon_bunny/authentication/external_mechanism_encoder.rb +27 -0
  34. data/lib/gorgon_bunny/lib/gorgon_bunny/authentication/plain_mechanism_encoder.rb +19 -0
  35. data/lib/gorgon_bunny/lib/gorgon_bunny/channel.rb +1875 -0
  36. data/lib/gorgon_bunny/lib/gorgon_bunny/channel_id_allocator.rb +80 -0
  37. data/lib/gorgon_bunny/lib/gorgon_bunny/compatibility.rb +24 -0
  38. data/lib/gorgon_bunny/lib/gorgon_bunny/concurrent/atomic_fixnum.rb +74 -0
  39. data/lib/gorgon_bunny/lib/gorgon_bunny/concurrent/condition.rb +66 -0
  40. data/lib/gorgon_bunny/lib/gorgon_bunny/concurrent/continuation_queue.rb +41 -0
  41. data/lib/gorgon_bunny/lib/gorgon_bunny/concurrent/linked_continuation_queue.rb +61 -0
  42. data/lib/gorgon_bunny/lib/gorgon_bunny/concurrent/synchronized_sorted_set.rb +56 -0
  43. data/lib/gorgon_bunny/lib/gorgon_bunny/consumer.rb +123 -0
  44. data/lib/gorgon_bunny/lib/gorgon_bunny/consumer_tag_generator.rb +23 -0
  45. data/lib/gorgon_bunny/lib/gorgon_bunny/consumer_work_pool.rb +94 -0
  46. data/lib/gorgon_bunny/lib/gorgon_bunny/delivery_info.rb +93 -0
  47. data/lib/gorgon_bunny/lib/gorgon_bunny/exceptions.rb +236 -0
  48. data/lib/gorgon_bunny/lib/gorgon_bunny/exchange.rb +271 -0
  49. data/lib/gorgon_bunny/lib/gorgon_bunny/framing.rb +56 -0
  50. data/lib/gorgon_bunny/lib/gorgon_bunny/heartbeat_sender.rb +70 -0
  51. data/lib/gorgon_bunny/lib/gorgon_bunny/message_properties.rb +119 -0
  52. data/lib/gorgon_bunny/lib/gorgon_bunny/queue.rb +387 -0
  53. data/lib/gorgon_bunny/lib/gorgon_bunny/reader_loop.rb +116 -0
  54. data/lib/gorgon_bunny/lib/gorgon_bunny/return_info.rb +74 -0
  55. data/lib/gorgon_bunny/lib/gorgon_bunny/session.rb +1044 -0
  56. data/lib/gorgon_bunny/lib/gorgon_bunny/socket.rb +83 -0
  57. data/lib/gorgon_bunny/lib/gorgon_bunny/ssl_socket.rb +57 -0
  58. data/lib/gorgon_bunny/lib/gorgon_bunny/system_timer.rb +20 -0
  59. data/lib/gorgon_bunny/lib/gorgon_bunny/test_kit.rb +27 -0
  60. data/lib/gorgon_bunny/lib/gorgon_bunny/timeout.rb +18 -0
  61. data/lib/gorgon_bunny/lib/gorgon_bunny/transport.rb +398 -0
  62. data/lib/gorgon_bunny/lib/gorgon_bunny/version.rb +6 -0
  63. data/lib/gorgon_bunny/lib/gorgon_bunny/versioned_delivery_tag.rb +28 -0
  64. data/spec/crash_reporter_spec.rb +1 -1
  65. data/spec/gem_command_handler_spec.rb +2 -2
  66. data/spec/listener_spec.rb +5 -5
  67. data/spec/worker_manager_spec.rb +3 -3
  68. metadata +56 -17
@@ -0,0 +1,23 @@
1
+ module GorgonBunny
2
+ # Used to generate consumer tags in the client
3
+ class ConsumerTagGenerator
4
+
5
+ #
6
+ # API
7
+ #
8
+
9
+ # @return [String] Generated consumer tag
10
+ def generate
11
+ "#{Kernel.rand}-#{Time.now.to_i * 1000}-#{Kernel.rand(999_999_999_999)}"
12
+ end # generate
13
+
14
+
15
+ # Unique string supposed to be used as a consumer tag.
16
+ #
17
+ # @return [String] Unique string.
18
+ # @api public
19
+ def generate_prefixed(name = "bunny")
20
+ "#{name}-#{Time.now.to_i * 1000}-#{Kernel.rand(999_999_999_999)}"
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,94 @@
1
+ require "thread"
2
+
3
+ module GorgonBunny
4
+ # Thread pool that dispatches consumer deliveries. Not supposed to be shared between channels
5
+ # or threads.
6
+ #
7
+ # Every channel its own consumer pool.
8
+ #
9
+ # @private
10
+ class ConsumerWorkPool
11
+
12
+ #
13
+ # API
14
+ #
15
+
16
+ attr_reader :threads
17
+ attr_reader :size
18
+
19
+ def initialize(size = 1)
20
+ @size = size
21
+ @queue = ::Queue.new
22
+ end
23
+
24
+
25
+ def submit(callable = nil, &block)
26
+ @queue.push(callable || block)
27
+ end
28
+
29
+ def start
30
+ @threads = []
31
+
32
+ @size.times do
33
+ t = Thread.new(&method(:run_loop))
34
+ @threads << t
35
+ end
36
+
37
+ @running = true
38
+ end
39
+
40
+ def running?
41
+ @running
42
+ end
43
+
44
+ def shutdown
45
+ @running = false
46
+
47
+ @size.times do
48
+ submit do |*args|
49
+ throw :terminate
50
+ end
51
+ end
52
+ end
53
+
54
+ def join(timeout = nil)
55
+ @threads.each { |t| t.join(timeout) }
56
+ end
57
+
58
+ def pause
59
+ @running = false
60
+
61
+ @threads.each { |t| t.stop }
62
+ end
63
+
64
+ def resume
65
+ @running = true
66
+
67
+ @threads.each { |t| t.run }
68
+ end
69
+
70
+ def kill
71
+ @running = false
72
+
73
+ @threads.each { |t| t.kill }
74
+ end
75
+
76
+ protected
77
+
78
+ def run_loop
79
+ catch(:terminate) do
80
+ loop do
81
+ callable = @queue.pop
82
+
83
+ begin
84
+ callable.call
85
+ rescue Exception => e
86
+ # TODO
87
+ puts e.class.name
88
+ puts e.message
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,93 @@
1
+ require "gorgon_bunny/versioned_delivery_tag"
2
+
3
+ module GorgonBunny
4
+ # Wraps {GorgonAMQ::Protocol::Basic::Deliver} to
5
+ # provide access to the delivery properties as immutable hash as
6
+ # well as methods.
7
+ class DeliveryInfo
8
+
9
+ #
10
+ # Behaviors
11
+ #
12
+
13
+ include Enumerable
14
+
15
+ #
16
+ # API
17
+ #
18
+
19
+ # @return [GorgonBunny::Consumer] Consumer this delivery is for
20
+ attr_reader :consumer
21
+ # @return [GorgonBunny::Channel] Channel this delivery is on
22
+ attr_reader :channel
23
+
24
+ # @private
25
+ def initialize(basic_deliver, consumer, channel)
26
+ @basic_deliver = basic_deliver
27
+ @hash = {
28
+ :consumer_tag => basic_deliver.consumer_tag,
29
+ :delivery_tag => VersionedDeliveryTag.new(basic_deliver.delivery_tag, channel.recoveries_counter),
30
+ :redelivered => basic_deliver.redelivered,
31
+ :exchange => basic_deliver.exchange,
32
+ :routing_key => basic_deliver.routing_key,
33
+ :consumer => consumer,
34
+ :channel => channel
35
+ }
36
+ @consumer = consumer
37
+ @channel = channel
38
+ end
39
+
40
+ # Iterates over the delivery properties
41
+ # @see Enumerable#each
42
+ def each(*args, &block)
43
+ @hash.each(*args, &block)
44
+ end
45
+
46
+ # Accesses delivery properties by key
47
+ # @see Hash#[]
48
+ def [](k)
49
+ @hash[k]
50
+ end
51
+
52
+ # @return [Hash] Hash representation of this delivery info
53
+ def to_hash
54
+ @hash
55
+ end
56
+
57
+ # @private
58
+ def to_s
59
+ to_hash.to_s
60
+ end
61
+
62
+ # @private
63
+ def inspect
64
+ to_hash.inspect
65
+ end
66
+
67
+ # @return [String] Consumer tag this delivery is for
68
+ def consumer_tag
69
+ @basic_deliver.consumer_tag
70
+ end
71
+
72
+ # @return [String] Delivery identifier that is used to acknowledge, reject and nack deliveries
73
+ def delivery_tag
74
+ @basic_deliver.delivery_tag
75
+ end
76
+
77
+ # @return [Boolean] true if this delivery is a redelivery (the message was requeued at least once)
78
+ def redelivered
79
+ @basic_deliver.redelivered
80
+ end
81
+ alias redelivered? redelivered
82
+
83
+ # @return [String] Name of the exchange this message was published to
84
+ def exchange
85
+ @basic_deliver.exchange
86
+ end
87
+
88
+ # @return [String] Routing key this message was published with
89
+ def routing_key
90
+ @basic_deliver.routing_key
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,236 @@
1
+ module GorgonBunny
2
+ # Base class for all GorgonBunny exceptions
3
+ # @api public
4
+ class Exception < ::StandardError
5
+ end
6
+
7
+ # Indicates a network failure. If automatic network
8
+ # recovery mode is enabled, these will be typically handled
9
+ # by the client itself.
10
+ #
11
+ # @api public
12
+ class NetworkFailure < Exception
13
+ attr_reader :cause
14
+
15
+ def initialize(message, cause)
16
+ super(message)
17
+ @cause = cause
18
+ end
19
+ end
20
+
21
+ # Base class for all channel level exceptions
22
+ class ChannelLevelException < Exception
23
+ attr_reader :channel, :channel_close
24
+
25
+ def initialize(message, ch, channel_close)
26
+ super(message)
27
+
28
+ @channel = ch
29
+ @channel_close = channel_close
30
+ end
31
+ end
32
+
33
+ # Base class for all connection level exceptions
34
+ class ConnectionLevelException < Exception
35
+ attr_reader :connection, :connection_close
36
+
37
+ def initialize(message, connection, connection_close)
38
+ super(message)
39
+
40
+ @connection = connection
41
+ @connection_close = connection_close
42
+ end
43
+ end
44
+
45
+
46
+ # Raised when TCP connection to RabbitMQ fails because of an unresolved
47
+ # hostname, connectivity problem, etc
48
+ class TCPConnectionFailed < Exception
49
+ attr_reader :hostname, :port
50
+
51
+ def initialize(e, hostname, port)
52
+ m = case e
53
+ when String then
54
+ e
55
+ when Exception then
56
+ e.message
57
+ end
58
+ super("Could not establish TCP connection to #{hostname}:#{port}: #{m}")
59
+ end
60
+ end
61
+
62
+ # Raised when a frame is sent over an already closed connection
63
+ class ConnectionClosedError < Exception
64
+ def initialize(frame)
65
+ if frame.respond_to?(:method_class)
66
+ super("Trying to send frame through a closed connection. Frame is #{frame.inspect}, method class is #{frame.method_class}")
67
+ else
68
+ super("Trying to send frame through a closed connection. Frame is #{frame.inspect}")
69
+ end
70
+ end
71
+ end
72
+
73
+ class ShutdownSignal < Exception
74
+ end
75
+
76
+ # Raised when RabbitMQ closes TCP connection before finishing connection
77
+ # sequence properly. This typically indicates an authentication issue.
78
+ class PossibleAuthenticationFailureError < Exception
79
+
80
+ #
81
+ # API
82
+ #
83
+
84
+ attr_reader :username, :vhost
85
+
86
+ def initialize(username, vhost, password_length)
87
+ @username = username
88
+ @vhost = vhost
89
+
90
+ super("Authentication with RabbitMQ failed. Please check your connection settings. Username: #{username}, vhost: #{vhost}, password length: #{password_length}")
91
+ end # initialize(settings)
92
+ end # PossibleAuthenticationFailureError
93
+
94
+
95
+ # Raised when RabbitMQ closes TCP connection due to an authentication failure.
96
+ # Relies on RabbitMQ 3.2 Authentication Failure Notifications extension:
97
+ # http://www.rabbitmq.com/auth-notification.html
98
+ class AuthenticationFailureError < PossibleAuthenticationFailureError
99
+
100
+ #
101
+ # API
102
+ #
103
+
104
+ attr_reader :username, :vhost
105
+
106
+ def initialize(username, vhost, password_length)
107
+ @username = username
108
+ @vhost = vhost
109
+
110
+ super(username, vhost, password_length)
111
+ end # initialize(settings)
112
+ end # AuthenticationFailureError
113
+
114
+
115
+ # backwards compatibility
116
+ # @private
117
+ ConnectionError = TCPConnectionFailed
118
+ # @private
119
+ ServerDownError = TCPConnectionFailed
120
+
121
+ # Raised when a channel is closed forcefully using rabbitmqctl
122
+ # or the management UI plugin
123
+ class ForcedChannelCloseError < ChannelLevelException; end
124
+ # Raised when a connection is closed forcefully using rabbitmqctl
125
+ # or the management UI plugin
126
+ class ForcedConnectionCloseError < ConnectionLevelException; end
127
+ # @private
128
+ class MessageError < ConnectionLevelException; end
129
+ # @private
130
+ class ProtocolError < ConnectionLevelException; end
131
+ # Raised when RabbitMQ reports and internal error
132
+ class InternalError < ConnectionLevelException; end
133
+
134
+ # Raised when read or write I/O operations time out (but only if
135
+ # a connection is configured to use them)
136
+ class ClientTimeout < Timeout::Error; end
137
+ # Raised on initial TCP connection timeout
138
+ class ConnectionTimeout < Timeout::Error; end
139
+
140
+
141
+ # Base exception class for data consistency and framing errors.
142
+ class InconsistentDataError < Exception
143
+ end
144
+
145
+ # Raised by adapters when frame does not end with {final octet GorgonAMQ::Protocol::Frame::FINAL_OCTET}.
146
+ # This suggest that there is a bug in adapter or AMQ broker implementation.
147
+ #
148
+ # @see https://www.rabbitmq.com/resources/specs/amqp0-9-1.pdf AMQP 0.9.1 specification (Section 2.3)
149
+ class NoFinalOctetError < InconsistentDataError
150
+ def initialize
151
+ super("Frame doesn't end with #{GorgonAMQ::Protocol::Frame::FINAL_OCTET} as it must, which means the size is miscalculated.")
152
+ end
153
+ end
154
+
155
+ # Raised by adapters when actual frame payload size in bytes is not equal
156
+ # to the size specified in that frame's header.
157
+ # This suggest that there is a bug in adapter or AMQ broker implementation.
158
+ #
159
+ # @see https://www.rabbitmq.com/resources/specs/amqp0-9-1.pdf AMQP 0.9.1 specification (Section 2.3)
160
+ class BadLengthError < InconsistentDataError
161
+ def initialize(expected_length, actual_length)
162
+ super("Frame payload should be #{expected_length} long, but it's #{actual_length} long.")
163
+ end
164
+ end
165
+
166
+ # Raised when a closed channel is used
167
+ class ChannelAlreadyClosed < Exception
168
+ attr_reader :channel
169
+
170
+ def initialize(message, ch)
171
+ super(message)
172
+
173
+ @channel = ch
174
+ end
175
+ end
176
+
177
+ # Raised when RabbitMQ responds with 406 PRECONDITION_FAILED
178
+ class PreconditionFailed < ChannelLevelException
179
+ end
180
+
181
+ # Raised when RabbitMQ responds with 404 NOT_FOUND
182
+ class NotFound < ChannelLevelException
183
+ end
184
+
185
+ # Raised when RabbitMQ responds with 405 RESOUCE_LOCKED
186
+ class ResourceLocked < ChannelLevelException
187
+ end
188
+
189
+ # Raised when RabbitMQ responds with 403 ACCESS_REFUSED
190
+ class AccessRefused < ChannelLevelException
191
+ end
192
+
193
+ # Raised when RabbitMQ responds with 504 CHANNEL_ERROR
194
+ class ChannelError < ConnectionLevelException
195
+ end
196
+
197
+ # Raised when RabbitMQ responds with 503 COMMAND_INVALID
198
+ class CommandInvalid < ConnectionLevelException
199
+ end
200
+
201
+ # Raised when RabbitMQ responds with 501 FRAME_ERROR
202
+ class FrameError < ConnectionLevelException
203
+ end
204
+
205
+ # Raised when RabbitMQ responds with 505 UNEXPECTED_FRAME
206
+ class UnexpectedFrame < ConnectionLevelException
207
+ end
208
+
209
+ # Raised when RabbitMQ responds with 506 RESOURCE_ERROR
210
+ class ResourceError < ConnectionLevelException
211
+ end
212
+
213
+ # @private
214
+ class NetworkErrorWrapper < Exception
215
+ attr_reader :other
216
+
217
+ def initialize(other)
218
+ super(other.message)
219
+ @other = other
220
+ end
221
+ end
222
+
223
+ # Raised when RabbitMQ responds with 302 CONNECTION_FORCED
224
+ # (which means the connection was closed using rabbitmqctl or
225
+ # RabbitMQ management UI)
226
+ class ConnectionForced < ConnectionLevelException
227
+ end
228
+
229
+ # @private
230
+ class MissingTLSCertificateFile < Exception
231
+ end
232
+
233
+ # @private
234
+ class MissingTLSKeyFile < Exception
235
+ end
236
+ end