amqp-client 0.3.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,15 +1,111 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AMQP
4
- Message = Struct.new(:channel, :delivery_tag, :exchange_name, :routing_key, :properties, :body, :redelivered, :consumer_tag) do
5
- def ack
6
- channel.basic_ack(delivery_tag)
4
+ class Client
5
+ # A message delivered from the broker
6
+ class Message
7
+ # @api private
8
+ def initialize(channel, consumer_tag, delivery_tag, exchange, routing_key, redelivered)
9
+ @channel = channel
10
+ @consumer_tag = consumer_tag
11
+ @delivery_tag = delivery_tag
12
+ @exchange = exchange
13
+ @routing_key = routing_key
14
+ @redelivered = redelivered
15
+ @properties = nil
16
+ @body = ""
17
+ end
18
+
19
+ # The channel the message was deliviered to
20
+ # @return [Connection::Channel]
21
+ attr_reader :channel
22
+
23
+ # The tag of the consumer the message was deliviered to
24
+ # @return [String]
25
+ # @return [nil] If the message was polled and not deliviered to a consumer
26
+ attr_reader :consumer_tag
27
+
28
+ # The delivery tag of the message, used for acknowledge or reject the message
29
+ # @return [Integer]
30
+ attr_reader :delivery_tag
31
+
32
+ # Name of the exchange the message was published to
33
+ # @return [String]
34
+ attr_reader :exchange
35
+
36
+ # The routing key the message was published with
37
+ # @return [String]
38
+ attr_reader :routing_key
39
+
40
+ # True if the message have been delivered before
41
+ # @return [Boolean]
42
+ attr_reader :redelivered
43
+
44
+ # Message properties
45
+ # @return [Properties]
46
+ attr_accessor :properties
47
+
48
+ # The message body
49
+ # @return [String]
50
+ attr_accessor :body
51
+
52
+ # Acknowledge the message
53
+ # @return [nil]
54
+ def ack
55
+ @channel.basic_ack(@delivery_tag)
56
+ end
57
+
58
+ # Reject the message
59
+ # @param requeue [Boolean] If true the message will be put back into the queue again, ready to be redelivered
60
+ # @return [nil]
61
+ def reject(requeue: false)
62
+ @channel.basic_reject(@delivery_tag, requeue: requeue)
63
+ end
64
+
65
+ # @see #exchange
66
+ # @deprecated
67
+ # @!attribute [r] exchange_name
68
+ # @return [String]
69
+ def exchange_name
70
+ @exchange
71
+ end
7
72
  end
8
73
 
9
- def reject(requeue: false)
10
- channel.basic_reject(delivery_tag, requeue)
74
+ # A published message returned by the broker due to some error
75
+ class ReturnMessage
76
+ # @api private
77
+ def initialize(reply_code, reply_text, exchange, routing_key)
78
+ @reply_code = reply_code
79
+ @reply_text = reply_text
80
+ @exchange = exchange
81
+ @routing_key = routing_key
82
+ @properties = nil
83
+ @body = ""
84
+ end
85
+
86
+ # Error code
87
+ # @return [Integer]
88
+ attr_reader :reply_code
89
+
90
+ # Description on why the message was returned
91
+ # @return [String]
92
+ attr_reader :reply_text
93
+
94
+ # Name of the exchange the message was published to
95
+ # @return [String]
96
+ attr_reader :exchange
97
+
98
+ # The routing key the message was published with
99
+ # @return [String]
100
+ attr_reader :routing_key
101
+
102
+ # Message properties
103
+ # @return [Properties]
104
+ attr_accessor :properties
105
+
106
+ # The message body
107
+ # @return [String]
108
+ attr_accessor :body
11
109
  end
12
110
  end
13
-
14
- ReturnMessage = Struct.new(:reply_code, :reply_text, :exchange, :routing_key, :properties, :body)
15
111
  end
@@ -3,200 +3,264 @@
3
3
  require_relative "./table"
4
4
 
5
5
  module AMQP
6
- # Encode/decode AMQP Properties
7
- Properties = Struct.new(:content_type, :content_encoding, :headers, :delivery_mode, :priority, :correlation_id,
8
- :reply_to, :expiration, :message_id, :timestamp, :type, :user_id, :app_id,
9
- keyword_init: true) do
10
- def encode
11
- flags = 0
12
- arr = [flags]
13
- fmt = String.new("S>")
14
-
15
- if content_type
16
- content_type.is_a?(String) || raise(ArgumentError, "content_type must be a string")
17
-
18
- flags |= (1 << 15)
19
- arr << content_type.bytesize << content_type
20
- fmt << "Ca*"
6
+ class Client
7
+ # Encode/decode AMQP Properties
8
+ class Properties
9
+ def initialize(content_type: nil, content_encoding: nil, headers: nil, delivery_mode: nil, priority: nil, correlation_id: nil,
10
+ reply_to: nil, expiration: nil, message_id: nil, timestamp: nil, type: nil, user_id: nil, app_id: nil)
11
+ @content_type = content_type
12
+ @content_encoding = content_encoding
13
+ @headers = headers
14
+ @delivery_mode = delivery_mode
15
+ @priority = priority
16
+ @correlation_id = correlation_id
17
+ @reply_to = reply_to
18
+ @expiration = expiration
19
+ @message_id = message_id
20
+ @timestamp = timestamp
21
+ @type = type
22
+ @user_id = user_id
23
+ @app_id = app_id
21
24
  end
22
25
 
23
- if content_encoding
24
- content_encoding.is_a?(String) || raise(ArgumentError, "content_encoding must be a string")
26
+ # Content type of the message body
27
+ # @return [String, nil]
28
+ attr_accessor :content_type
29
+ # Content encoding of the body
30
+ # @return [String, nil]
31
+ attr_accessor :content_encoding
32
+ # Custom headers
33
+ # @return [Hash<String, Object>, nil]
34
+ attr_accessor :headers
35
+ # 2 for persisted message, transient messages for all other values
36
+ # @return [Integer, nil]
37
+ attr_accessor :delivery_mode
38
+ # A priority of the message (between 0 and 255)
39
+ # @return [Integer, nil]
40
+ attr_accessor :priority
41
+ # A correlation id, most often used used for RPC communication
42
+ # @return [Integer, nil]
43
+ attr_accessor :correlation_id
44
+ # Queue to reply RPC responses to
45
+ # @return [String, nil]
46
+ attr_accessor :reply_to
47
+ # Number of seconds the message will stay in the queue
48
+ # @return [Integer]
49
+ # @return [String]
50
+ # @return [nil]
51
+ attr_accessor :expiration
52
+ # @return [String, nil]
53
+ attr_accessor :message_id
54
+ # User-definable, but often used for the time the message was originally generated
55
+ # @return [Date, nil]
56
+ attr_accessor :timestamp
57
+ # User-definable, but can can indicate what kind of message this is
58
+ # @return [String, nil]
59
+ attr_accessor :type
60
+ # User-definable, but can be used to verify that this is the user that published the message
61
+ # @return [String, nil]
62
+ attr_accessor :user_id
63
+ # User-definable, but often indicates which app that generated the message
64
+ # @return [String, nil]
65
+ attr_accessor :app_id
25
66
 
26
- flags |= (1 << 14)
27
- arr << content_encoding.bytesize << content_encoding
28
- fmt << "Ca*"
29
- end
67
+ # Encode properties into a byte array
68
+ # @return [String] byte array
69
+ def encode
70
+ flags = 0
71
+ arr = [flags]
72
+ fmt = StringIO.new(String.new("S>", capacity: 35))
73
+ fmt.pos = 2
30
74
 
31
- if headers
32
- headers.is_a?(Hash) || raise(ArgumentError, "headers must be a hash")
75
+ if content_type
76
+ content_type.is_a?(String) || raise(ArgumentError, "content_type must be a string")
33
77
 
34
- flags |= (1 << 13)
35
- tbl = Table.encode(headers)
36
- arr << tbl.bytesize << tbl
37
- fmt << "L>a*"
38
- end
78
+ flags |= (1 << 15)
79
+ arr << content_type.bytesize << content_type
80
+ fmt << "Ca*"
81
+ end
39
82
 
40
- if delivery_mode
41
- headers.is_a?(Integer) || raise(ArgumentError, "delivery_mode must be an int")
83
+ if content_encoding
84
+ content_encoding.is_a?(String) || raise(ArgumentError, "content_encoding must be a string")
42
85
 
43
- flags |= (1 << 12)
44
- arr << delivery_mode
45
- fmt << "C"
46
- end
86
+ flags |= (1 << 14)
87
+ arr << content_encoding.bytesize << content_encoding
88
+ fmt << "Ca*"
89
+ end
47
90
 
48
- if priority
49
- priority.is_a?(Integer) || raise(ArgumentError, "priority must be an int")
50
- flags |= (1 << 11)
51
- arr << priority
52
- fmt << "C"
53
- end
91
+ if headers
92
+ headers.is_a?(Hash) || raise(ArgumentError, "headers must be a hash")
54
93
 
55
- if correlation_id
56
- priority.is_a?(String) || raise(ArgumentError, "correlation_id must be a string")
94
+ flags |= (1 << 13)
95
+ tbl = Table.encode(headers)
96
+ arr << tbl.bytesize << tbl
97
+ fmt << "L>a*"
98
+ end
57
99
 
58
- flags |= (1 << 10)
59
- arr << correlation_id.bytesize << correlation_id
60
- fmt << "Ca*"
61
- end
100
+ if delivery_mode
101
+ delivery_mode.is_a?(Integer) || raise(ArgumentError, "delivery_mode must be an int")
102
+ delivery_mode.between?(0, 2) || raise(ArgumentError, "delivery_mode must be be between 0 and 2")
62
103
 
63
- if reply_to
64
- reply_to.is_a?(String) || raise(ArgumentError, "reply_to must be a string")
104
+ flags |= (1 << 12)
105
+ arr << delivery_mode
106
+ fmt << "C"
107
+ end
65
108
 
66
- flags |= (1 << 9)
67
- arr << reply_to.bytesize << reply_to
68
- fmt << "Ca*"
69
- end
109
+ if priority
110
+ priority.is_a?(Integer) || raise(ArgumentError, "priority must be an int")
111
+ flags |= (1 << 11)
112
+ arr << priority
113
+ fmt << "C"
114
+ end
70
115
 
71
- if expiration
72
- expiration = expiration.to_s if expiration.is_a?(Integer)
73
- expiration.is_a?(String) || raise(ArgumentError, "expiration must be a string or integer")
116
+ if correlation_id
117
+ priority.is_a?(String) || raise(ArgumentError, "correlation_id must be a string")
74
118
 
75
- flags |= (1 << 8)
76
- arr << expiration.bytesize << expiration
77
- fmt << "Ca*"
78
- end
119
+ flags |= (1 << 10)
120
+ arr << correlation_id.bytesize << correlation_id
121
+ fmt << "Ca*"
122
+ end
79
123
 
80
- if message_id
81
- message_id.is_a?(String) || raise(ArgumentError, "message_id must be a string")
124
+ if reply_to
125
+ reply_to.is_a?(String) || raise(ArgumentError, "reply_to must be a string")
82
126
 
83
- flags |= (1 << 7)
84
- arr << message_id.bytesize << message_id
85
- fmt << "Ca*"
86
- end
127
+ flags |= (1 << 9)
128
+ arr << reply_to.bytesize << reply_to
129
+ fmt << "Ca*"
130
+ end
87
131
 
88
- if timestamp
89
- timestamp.is_a?(Time) || raise(ArgumentError, "timestamp must be a time")
132
+ if expiration
133
+ self.expiration = expiration.to_s if expiration.is_a?(Integer)
134
+ expiration.is_a?(String) || raise(ArgumentError, "expiration must be a string or integer")
90
135
 
91
- flags |= (1 << 6)
92
- arr << timestamp.to_i
93
- fmt << "Q>"
94
- end
136
+ flags |= (1 << 8)
137
+ arr << expiration.bytesize << expiration
138
+ fmt << "Ca*"
139
+ end
95
140
 
96
- if type
97
- type.is_a?(String) || raise(ArgumentError, "type must be a string")
141
+ if message_id
142
+ message_id.is_a?(String) || raise(ArgumentError, "message_id must be a string")
98
143
 
99
- flags |= (1 << 5)
100
- arr << type.bytesize << type
101
- fmt << "Ca*"
102
- end
144
+ flags |= (1 << 7)
145
+ arr << message_id.bytesize << message_id
146
+ fmt << "Ca*"
147
+ end
103
148
 
104
- if user_id
105
- user_id.is_a?(String) || raise(ArgumentError, "user_id must be a string")
149
+ if timestamp
150
+ timestamp.is_a?(Integer) || timestamp.is_a?(Time) || raise(ArgumentError, "timestamp must be an Integer or a Time")
106
151
 
107
- flags |= (1 << 4)
108
- arr << user_id.bytesize << user_id
109
- fmt << "Ca*"
110
- end
152
+ flags |= (1 << 6)
153
+ arr << timestamp.to_i
154
+ fmt << "Q>"
155
+ end
111
156
 
112
- if app_id
113
- app_id.is_a?(String) || raise(ArgumentError, "app_id must be a string")
157
+ if type
158
+ type.is_a?(String) || raise(ArgumentError, "type must be a string")
114
159
 
115
- flags |= (1 << 3)
116
- arr << app_id.bytesize << app_id
117
- fmt << "Ca*"
118
- end
160
+ flags |= (1 << 5)
161
+ arr << type.bytesize << type
162
+ fmt << "Ca*"
163
+ end
119
164
 
120
- arr[0] = flags
121
- arr.pack(fmt)
122
- end
165
+ if user_id
166
+ user_id.is_a?(String) || raise(ArgumentError, "user_id must be a string")
123
167
 
124
- def self.decode(bytes)
125
- h = new
126
- flags = bytes.unpack1("S>")
127
- pos = 2
128
- if (flags & 0x8000).positive?
129
- len = bytes[pos].ord
130
- pos += 1
131
- h[:content_type] = bytes.byteslice(pos, len).force_encoding("utf-8")
132
- pos += len
133
- end
134
- if (flags & 0x4000).positive?
135
- len = bytes[pos].ord
136
- pos += 1
137
- h[:content_encoding] = bytes.byteslice(pos, len).force_encoding("utf-8")
138
- pos += len
139
- end
140
- if (flags & 0x2000).positive?
141
- len = bytes.byteslice(pos, 4).unpack1("L>")
142
- pos += 4
143
- h[:headers] = Table.decode(bytes.byteslice(pos, len))
144
- pos += len
145
- end
146
- if (flags & 0x1000).positive?
147
- h[:delivery_mode] = bytes[pos].ord
148
- pos += 1
149
- end
150
- if (flags & 0x0800).positive?
151
- h[:priority] = bytes[pos].ord
152
- pos += 1
153
- end
154
- if (flags & 0x0400).positive?
155
- len = bytes[pos].ord
156
- pos += 1
157
- h[:correlation_id] = bytes.byteslice(pos, len).force_encoding("utf-8")
158
- pos += len
159
- end
160
- if (flags & 0x0200).positive?
161
- len = bytes[pos].ord
162
- pos += 1
163
- h[:reply_to] = bytes.byteslice(pos, len).force_encoding("utf-8")
164
- pos += len
165
- end
166
- if (flags & 0x0100).positive?
167
- len = bytes[pos].ord
168
- pos += 1
169
- h[:expiration] = bytes.byteslice(pos, len).force_encoding("utf-8")
170
- pos += len
171
- end
172
- if (flags & 0x0080).positive?
173
- len = bytes[pos].ord
174
- pos += 1
175
- h[:message_id] = bytes.byteslice(pos, len).force_encoding("utf-8")
176
- pos += len
177
- end
178
- if (flags & 0x0040).positive?
179
- h[:timestamp] = Time.at(bytes.byteslice(pos, 8).unpack1("Q>"))
180
- pos += 8
181
- end
182
- if (flags & 0x0020).positive?
183
- len = bytes[pos].ord
184
- pos += 1
185
- h[:type] = bytes.byteslice(pos, len).force_encoding("utf-8")
186
- pos += len
187
- end
188
- if (flags & 0x0010).positive?
189
- len = bytes[pos].ord
190
- pos += 1
191
- h[:user_id] = bytes.byteslice(pos, len).force_encoding("utf-8")
192
- pos += len
168
+ flags |= (1 << 4)
169
+ arr << user_id.bytesize << user_id
170
+ fmt << "Ca*"
171
+ end
172
+
173
+ if app_id
174
+ app_id.is_a?(String) || raise(ArgumentError, "app_id must be a string")
175
+
176
+ flags |= (1 << 3)
177
+ arr << app_id.bytesize << app_id
178
+ fmt << "Ca*"
179
+ end
180
+
181
+ arr[0] = flags
182
+ arr.pack(fmt.string)
193
183
  end
194
- if (flags & 0x0008).positive?
195
- len = bytes[pos].ord
196
- pos += 1
197
- h[:app_id] = bytes.byteslice(pos, len).force_encoding("utf-8")
184
+
185
+ # Decode a byte array
186
+ # @return [Properties]
187
+ def self.decode(bytes)
188
+ p = new
189
+ flags = bytes.unpack1("S>")
190
+ pos = 2
191
+ if (flags & 0x8000).positive?
192
+ len = bytes.getbyte(pos)
193
+ pos += 1
194
+ p.content_type = bytes.byteslice(pos, len).force_encoding("utf-8")
195
+ pos += len
196
+ end
197
+ if (flags & 0x4000).positive?
198
+ len = bytes.getbyte(pos)
199
+ pos += 1
200
+ p.content_encoding = bytes.byteslice(pos, len).force_encoding("utf-8")
201
+ pos += len
202
+ end
203
+ if (flags & 0x2000).positive?
204
+ len = bytes.byteslice(pos, 4).unpack1("L>")
205
+ pos += 4
206
+ p.headers = Table.decode(bytes.byteslice(pos, len))
207
+ pos += len
208
+ end
209
+ if (flags & 0x1000).positive?
210
+ p.delivery_mode = bytes.getbyte(pos)
211
+ pos += 1
212
+ end
213
+ if (flags & 0x0800).positive?
214
+ p.priority = bytes.getbyte(pos)
215
+ pos += 1
216
+ end
217
+ if (flags & 0x0400).positive?
218
+ len = bytes.getbyte(pos)
219
+ pos += 1
220
+ p.correlation_id = bytes.byteslice(pos, len).force_encoding("utf-8")
221
+ pos += len
222
+ end
223
+ if (flags & 0x0200).positive?
224
+ len = bytes.getbyte(pos)
225
+ pos += 1
226
+ p.reply_to = bytes.byteslice(pos, len).force_encoding("utf-8")
227
+ pos += len
228
+ end
229
+ if (flags & 0x0100).positive?
230
+ len = bytes.getbyte(pos)
231
+ pos += 1
232
+ p.expiration = bytes.byteslice(pos, len).force_encoding("utf-8")
233
+ pos += len
234
+ end
235
+ if (flags & 0x0080).positive?
236
+ len = bytes.getbyte(pos)
237
+ pos += 1
238
+ p.message_id = bytes.byteslice(pos, len).force_encoding("utf-8")
239
+ pos += len
240
+ end
241
+ if (flags & 0x0040).positive?
242
+ p.timestamp = Time.at(bytes.byteslice(pos, 8).unpack1("Q>"))
243
+ pos += 8
244
+ end
245
+ if (flags & 0x0020).positive?
246
+ len = bytes.getbyte(pos)
247
+ pos += 1
248
+ p.type = bytes.byteslice(pos, len).force_encoding("utf-8")
249
+ pos += len
250
+ end
251
+ if (flags & 0x0010).positive?
252
+ len = bytes.getbyte(pos)
253
+ pos += 1
254
+ p.user_id = bytes.byteslice(pos, len).force_encoding("utf-8")
255
+ pos += len
256
+ end
257
+ if (flags & 0x0008).positive?
258
+ len = bytes.getbyte(pos)
259
+ pos += 1
260
+ p.app_id = bytes.byteslice(pos, len).force_encoding("utf-8")
261
+ end
262
+ p
198
263
  end
199
- h
200
264
  end
201
265
  end
202
266
  end