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,190 @@
1
+ # encoding: binary
2
+
3
+ require "gorgon_amq/endianness"
4
+ require "gorgon_amq/protocol/client"
5
+ require "gorgon_amq/protocol/type_constants"
6
+ require "gorgon_amq/protocol/table"
7
+ require "gorgon_amq/protocol/float_32bit"
8
+
9
+ module GorgonAMQ
10
+ module Protocol
11
+
12
+ class TableValueDecoder
13
+
14
+ #
15
+ # Behaviors
16
+ #
17
+
18
+ include TypeConstants
19
+
20
+
21
+ #
22
+ # API
23
+ #
24
+
25
+ def self.decode_array(data, initial_offset)
26
+ array_length = data.slice(initial_offset, 4).unpack(PACK_UINT32).first
27
+
28
+ ary = Array.new
29
+ offset = initial_offset + 4
30
+
31
+ while offset <= (initial_offset + array_length)
32
+ type, offset = decode_value_type(data, offset)
33
+
34
+ i = case type
35
+ when TYPE_STRING
36
+ v, offset = decode_string(data, offset)
37
+ v
38
+ when TYPE_INTEGER
39
+ v, offset = decode_integer(data, offset)
40
+ v
41
+ when TYPE_DECIMAL
42
+ v, offset = decode_big_decimal(data, offset)
43
+ v
44
+ when TYPE_TIME
45
+ v, offset = decode_time(data, offset)
46
+ v
47
+ when TYPE_HASH
48
+ v, offset = decode_hash(data, offset)
49
+ v
50
+ when TYPE_BOOLEAN
51
+ v, offset = decode_boolean(data, offset)
52
+ v
53
+ when TYPE_SIGNED_8BIT then
54
+ v, offset = decode_short_short(data, offset)
55
+ v
56
+ when TYPE_SIGNED_16BIT then
57
+ v, offset = decode_short(data, offset)
58
+ v
59
+ when TYPE_SIGNED_64BIT then
60
+ v, offset = decode_long(data, offset)
61
+ v
62
+ when TYPE_32BIT_FLOAT then
63
+ v, offset = decode_32bit_float(data, offset)
64
+ v
65
+ when TYPE_64BIT_FLOAT then
66
+ v, offset = decode_64bit_float(data, offset)
67
+ v
68
+ when TYPE_VOID
69
+ nil
70
+ when TYPE_ARRAY
71
+ v, offset = TableValueDecoder.decode_array(data, offset)
72
+ v
73
+ else
74
+ raise ArgumentError.new("unsupported type in a table value: #{type.inspect}, do not know how to decode!")
75
+ end
76
+
77
+ ary << i
78
+ end
79
+
80
+
81
+ [ary, initial_offset + array_length + 4]
82
+ end # self.decode_array(data, initial_offset)
83
+
84
+
85
+ def self.decode_string(data, offset)
86
+ length = data.slice(offset, 4).unpack(PACK_UINT32).first
87
+ offset += 4
88
+ v = data.slice(offset, length)
89
+ offset += length
90
+
91
+ [v, offset]
92
+ end # self.decode_string(data, offset)
93
+
94
+
95
+ def self.decode_integer(data, offset)
96
+ v = data.slice(offset, 4).unpack(PACK_UINT32).first
97
+ offset += 4
98
+
99
+ [v, offset]
100
+ end # self.decode_integer(data, offset)
101
+
102
+
103
+ if GorgonAMQ::Endianness.big_endian?
104
+ def self.decode_long(data, offset)
105
+ v = data.slice(offset, 8).unpack(PACK_INT64)
106
+
107
+ offset += 8
108
+ [v, offset]
109
+ end
110
+ else
111
+ def self.decode_long(data, offset)
112
+ slice = data.slice(offset, 8).bytes.to_a.reverse.map(&:chr).join
113
+ v = slice.unpack(PACK_INT64).first
114
+
115
+ offset += 8
116
+ [v, offset]
117
+ end
118
+ end
119
+
120
+
121
+ def self.decode_big_decimal(data, offset)
122
+ decimals, raw = data.slice(offset, 5).unpack(PACK_UCHAR_UINT32)
123
+ offset += 5
124
+ v = BigDecimal.new(raw.to_s) * (BigDecimal.new(TEN) ** -decimals)
125
+
126
+ [v, offset]
127
+ end # self.decode_big_decimal(data, offset)
128
+
129
+
130
+ def self.decode_time(data, offset)
131
+ timestamp = data.slice(offset, 8).unpack(PACK_UINT32_X2).last
132
+ v = Time.at(timestamp)
133
+ offset += 8
134
+
135
+ [v, offset]
136
+ end # self.decode_time(data, offset)
137
+
138
+
139
+ def self.decode_boolean(data, offset)
140
+ integer = data.slice(offset, 2).unpack(PACK_CHAR).first # 0 or 1
141
+ offset += 1
142
+ [(integer == 1), offset]
143
+ end # self.decode_boolean(data, offset)
144
+
145
+
146
+ def self.decode_32bit_float(data, offset)
147
+ v = data.slice(offset, 4).unpack(PACK_32BIT_FLOAT).first
148
+ offset += 4
149
+
150
+ [v, offset]
151
+ end # self.decode_32bit_float(data, offset)
152
+
153
+
154
+ def self.decode_64bit_float(data, offset)
155
+ v = data.slice(offset, 8).unpack(PACK_64BIT_FLOAT).first
156
+ offset += 8
157
+
158
+ [v, offset]
159
+ end # self.decode_64bit_float(data, offset)
160
+
161
+
162
+ def self.decode_value_type(data, offset)
163
+ [data.slice(offset, 1), offset + 1]
164
+ end # self.decode_value_type(data, offset)
165
+
166
+
167
+
168
+ def self.decode_hash(data, offset)
169
+ length = data.slice(offset, 4).unpack(PACK_UINT32).first
170
+ v = Table.decode(data.slice(offset, length + 4))
171
+ offset += 4 + length
172
+
173
+ [v, offset]
174
+ end # self.decode_hash(data, offset)
175
+
176
+
177
+ def self.decode_short_short(data, offset)
178
+ v = data.slice(offset, 1).unpack(PACK_INT8).first
179
+ offset += 1
180
+ [v, offset]
181
+ end
182
+
183
+ def self.decode_short(data, offset)
184
+ v = GorgonAMQ::Hacks.unpack_int16_big_endian(data.slice(offset, 2)).first
185
+ offset += 2
186
+ [v, offset]
187
+ end
188
+ end # TableValueDecoder
189
+ end # Protocol
190
+ end # AMQ
@@ -0,0 +1,123 @@
1
+ # encoding: binary
2
+
3
+ require "gorgon_amq/protocol/client"
4
+ require "gorgon_amq/protocol/type_constants"
5
+ require "gorgon_amq/protocol/table"
6
+ require "date"
7
+
8
+ require "gorgon_amq/protocol/float_32bit"
9
+
10
+ module GorgonAMQ
11
+ module Protocol
12
+
13
+ class TableValueEncoder
14
+
15
+ #
16
+ # Behaviors
17
+ #
18
+
19
+ include TypeConstants
20
+
21
+ #
22
+ # API
23
+ #
24
+
25
+ def self.encode(value)
26
+ accumulator = String.new
27
+
28
+ case value
29
+ when String then
30
+ accumulator << TYPE_STRING
31
+ accumulator << [value.bytesize].pack(PACK_UINT32)
32
+ accumulator << value
33
+ when Symbol then
34
+ v = value.to_s
35
+ accumulator << TYPE_STRING
36
+ accumulator << [v.bytesize].pack(PACK_UINT32)
37
+ accumulator << v
38
+ when Integer then
39
+ accumulator << TYPE_INTEGER
40
+ accumulator << [value].pack(PACK_UINT32)
41
+ when GorgonAMQ::Protocol::Float32Bit then
42
+ accumulator << TYPE_32BIT_FLOAT
43
+ accumulator << [value.value].pack(PACK_32BIT_FLOAT)
44
+ when Float then
45
+ accumulator << TYPE_64BIT_FLOAT
46
+ accumulator << [value].pack(PACK_64BIT_FLOAT)
47
+ when true, false then
48
+ accumulator << TYPE_BOOLEAN
49
+ accumulator << (value ? BOOLEAN_TRUE : BOOLEAN_FALSE)
50
+ when Time then
51
+ accumulator << TYPE_TIME
52
+ accumulator << [value.to_i].pack(PACK_INT64).reverse # FIXME: there has to be a more efficient way
53
+ when nil then
54
+ accumulator << TYPE_VOID
55
+ when Array then
56
+ accumulator << TYPE_ARRAY
57
+ accumulator << [self.array_size(value)].pack(PACK_UINT32)
58
+
59
+ value.each { |v| accumulator << self.encode(v) }
60
+ when Hash then
61
+ accumulator << TYPE_HASH
62
+ accumulator << GorgonAMQ::Protocol::Table.encode(value)
63
+ else
64
+ # We don't want to require these libraries.
65
+ if defined?(BigDecimal) && value.is_a?(BigDecimal)
66
+ accumulator << TYPE_DECIMAL
67
+ if value.exponent < 0
68
+ decimals = -value.exponent
69
+ raw = (value * (decimals ** 10)).to_i
70
+ accumulator << [decimals + 1, raw].pack(PACK_UCHAR_UINT32) # somewhat like floating point
71
+ else
72
+ # per spec, the "decimals" octet is unsigned (!)
73
+ accumulator << [0, value.to_i].pack(PACK_UCHAR_UINT32)
74
+ end
75
+ else
76
+ raise ArgumentError.new("Unsupported value #{value.inspect} of type #{value.class.name}")
77
+ end # if
78
+ end # case
79
+
80
+ accumulator
81
+ end # self.encode(value)
82
+
83
+
84
+
85
+
86
+ def self.field_value_size(value)
87
+ # the type tag takes 1 byte
88
+ acc = 1
89
+
90
+ case value
91
+ when String then
92
+ acc += (value.bytesize + 4)
93
+ when Integer then
94
+ acc += 4
95
+ when Float then
96
+ acc += 8
97
+ when Time, DateTime then
98
+ acc += 8
99
+ when true, false then
100
+ acc += 1
101
+ when nil then
102
+ # nothing, type tag alone is enough
103
+ when Hash then
104
+ acc += (4 + Table.hash_size(value))
105
+ when Array then
106
+ acc += (4 + self.array_size(value))
107
+ end
108
+
109
+ acc
110
+ end # self.field_value_size(value)
111
+
112
+
113
+ def self.array_size(value)
114
+ acc = 0
115
+ value.each { |v| acc += self.field_value_size(v) }
116
+
117
+ acc
118
+ end # self.array_size(value)
119
+
120
+ end # TableValueEncoder
121
+
122
+ end # Protocol
123
+ end # AMQ
@@ -0,0 +1,26 @@
1
+ # encoding: binary
2
+
3
+ module GorgonAMQ
4
+ module Protocol
5
+ module TypeConstants
6
+ TYPE_STRING = 'S'.freeze
7
+ TYPE_INTEGER = 'I'.freeze
8
+ TYPE_TIME = 'T'.freeze
9
+ TYPE_DECIMAL = 'D'.freeze
10
+ TYPE_HASH = 'F'.freeze
11
+ TYPE_ARRAY = 'A'.freeze
12
+ TYPE_SIGNED_8BIT = 'b'.freeze
13
+ TYPE_64BIT_FLOAT = 'd'.freeze
14
+ TYPE_32BIT_FLOAT = 'f'.freeze
15
+ TYPE_SIGNED_64BIT = 'l'.freeze
16
+ TYPE_SIGNED_16BIT = 's'.freeze
17
+ TYPE_BOOLEAN = 't'.freeze
18
+ TYPE_BYTE_ARRAY = 'x'.freeze
19
+ TYPE_VOID = 'V'.freeze
20
+ TEN = '10'.freeze
21
+
22
+ BOOLEAN_TRUE = "\x01".freeze
23
+ BOOLEAN_FALSE = "\x00".freeze
24
+ end # TypeConstants
25
+ end # Protocol
26
+ end # AMQ
@@ -0,0 +1,5 @@
1
+ module GorgonAMQ
2
+ module Protocol
3
+ VERSION = "1.8.0"
4
+ end # Protocol
5
+ end # AMQ
@@ -0,0 +1,114 @@
1
+ # encoding: utf-8
2
+
3
+ require "gorgon_amq/protocol/client"
4
+ require "gorgon_amq/uri"
5
+
6
+ module GorgonAMQ
7
+ module Settings
8
+
9
+ # @private
10
+ AMQPS = "amqps".freeze
11
+
12
+ # Default connection settings used by AMQ clients
13
+ #
14
+ # @see GorgonAMQ::Client::Settings.configure
15
+ def self.default
16
+ @default ||= {
17
+ # server
18
+ :host => "127.0.0.1",
19
+ :port => GorgonAMQ::Protocol::DEFAULT_PORT,
20
+
21
+ # login
22
+ :user => "guest",
23
+ :pass => "guest",
24
+ :vhost => "/",
25
+
26
+ # ssl
27
+ :ssl => false,
28
+
29
+ :frame_max => (128 * 1024),
30
+ :heartbeat => 0
31
+ }
32
+ end
33
+
34
+
35
+ # Merges given configuration parameters with defaults and returns
36
+ # the result.
37
+ #
38
+ # @param [Hash] Configuration parameters to use.
39
+ #
40
+ # @option settings [String] :host ("127.0.0.1") Hostname AMQ broker runs on.
41
+ # @option settings [String] :port (5672) Port AMQ broker listens on.
42
+ # @option settings [String] :vhost ("/") Virtual host to use.
43
+ # @option settings [String] :user ("guest") Username to use for authentication.
44
+ # @option settings [String] :pass ("guest") Password to use for authentication.
45
+ # @option settings [String] :ssl (false) Should be use TLS (SSL) for connection?
46
+ # @option settings [String] :timeout (nil) Connection timeout.
47
+ # @option settings [String] :broker (nil) Broker name (use if you intend to use broker-specific features).
48
+ # @option settings [Fixnum] :frame_max (131072) Maximum frame size to use. If broker cannot support frames this large, broker's maximum value will be used instead.
49
+ #
50
+ # @return [Hash] Merged configuration parameters.
51
+ def self.configure(settings = nil)
52
+ case settings
53
+ when Hash then
54
+ if username = (settings.delete(:username) || settings.delete(:user))
55
+ settings[:user] ||= username
56
+ end
57
+
58
+ if password = (settings.delete(:password) || settings.delete(:pass))
59
+ settings[:pass] ||= password
60
+ end
61
+
62
+
63
+ self.default.merge(settings)
64
+ when String then
65
+ settings = self.parse_amqp_url(settings)
66
+ self.default.merge(settings)
67
+ when NilClass then
68
+ self.default
69
+ end
70
+ end
71
+
72
+ # Parses AMQP connection URI and returns its components as a hash.
73
+ #
74
+ # h2. vhost naming schemes
75
+ #
76
+ # It is convenient to be able to specify the AMQP connection
77
+ # parameters as a URI string, and various "amqp" URI schemes
78
+ # exist. Unfortunately, there is no standard for these URIs, so
79
+ # while the schemes share the basic idea, they differ in some
80
+ # details. This implementation aims to encourage URIs that work
81
+ # as widely as possible.
82
+ #
83
+ # The URI scheme should be "amqp", or "amqps" if SSL is required.
84
+ #
85
+ # The host, port, username and password are represented in the
86
+ # authority component of the URI in the same way as in http URIs.
87
+ #
88
+ # The vhost is obtained from the first segment of the path, with the
89
+ # leading slash removed. The path should contain only a single
90
+ # segment (i.e, the only slash in it should be the leading one).
91
+ # If the vhost is to include slashes or other reserved URI
92
+ # characters, these should be percent-escaped.
93
+ #
94
+ # @example How vhost is parsed
95
+ #
96
+ # GorgonAMQ::Settings.parse_amqp_url("amqp://dev.rabbitmq.com") # => vhost is nil, so default (/) will be used
97
+ # GorgonAMQ::Settings.parse_amqp_url("amqp://dev.rabbitmq.com/") # => vhost is an empty string
98
+ # GorgonAMQ::Settings.parse_amqp_url("amqp://dev.rabbitmq.com/%2Fvault") # => vhost is /vault
99
+ # GorgonAMQ::Settings.parse_amqp_url("amqp://dev.rabbitmq.com/production") # => vhost is production
100
+ # GorgonAMQ::Settings.parse_amqp_url("amqp://dev.rabbitmq.com/a.b.c") # => vhost is a.b.c
101
+ # GorgonAMQ::Settings.parse_amqp_url("amqp://dev.rabbitmq.com/foo/bar") # => ArgumentError
102
+ #
103
+ #
104
+ # @param [String] connection_string AMQP connection URI, à la JDBC connection string. For example: amqp://bus.megacorp.internal:5877.
105
+ # @return [Hash] Connection parameters (:username, :password, :vhost, :host, :port, :ssl)
106
+ #
107
+ # @raise [ArgumentError] When connection URI schema is not amqp or amqps, or the path contains multiple segments
108
+ #
109
+ # @api public
110
+ def self.parse_amqp_url(connection_string)
111
+ GorgonAMQ::URI.parse(connection_string)
112
+ end
113
+ end
114
+ end