aws-sdk-resources 2.0.30 → 2.0.31
Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6599b6f95603aeafd4a36e645ae61ad4cf409fd9
|
4
|
+
data.tar.gz: ad18e46cf3dca6036b88cf1fa4b8a90e5326efda
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5fdf4f2d4dbe6644ce48a06369f3868ce8af3781aa6ce44f45890d387290d8f04d9f3ed16dad3ff44f450f71af3525a2fce0629c77b2ff03dc94a4c1f3101ffa
|
7
|
+
data.tar.gz: b7e83a5a26d3839ddf6b065b115bd67e0aa844f66705fb636f5e356fcc360e335b15ad3a28d9714666d762f4ad125dfa68bcde149bbc9b87c65f1aee9ec3f199
|
@@ -0,0 +1,158 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'openssl'
|
3
|
+
require 'base64'
|
4
|
+
require 'multi_json'
|
5
|
+
|
6
|
+
module Aws
|
7
|
+
module SNS
|
8
|
+
|
9
|
+
# A utility class that can be used to verify the authenticity of messages
|
10
|
+
# sent by Amazon SNS.
|
11
|
+
#
|
12
|
+
# verifier = Aws::SNS::MessageVerifier.new
|
13
|
+
#
|
14
|
+
# # returns true/false
|
15
|
+
# verifier.authentic?(message_body)
|
16
|
+
#
|
17
|
+
# # raises a Aws::SNS::MessageVerifier::VerificationError on failure
|
18
|
+
# verifier.authenticate!(message_body)
|
19
|
+
#
|
20
|
+
# You can re-use a single {MessageVerifier} instance to authenticate
|
21
|
+
# multiple SNS messages.
|
22
|
+
class MessageVerifier
|
23
|
+
|
24
|
+
class VerificationError < StandardError; end
|
25
|
+
|
26
|
+
# @api private
|
27
|
+
SIGNABLE_KEYS = [
|
28
|
+
'Message',
|
29
|
+
'MessageId',
|
30
|
+
'Subject',
|
31
|
+
'SubscribeURL',
|
32
|
+
'Timestamp',
|
33
|
+
'Token',
|
34
|
+
'TopicArn',
|
35
|
+
'Type',
|
36
|
+
].freeze
|
37
|
+
|
38
|
+
# @api private
|
39
|
+
AWS_HOSTNAMES = [
|
40
|
+
/^sns\.[a-zA-Z0-9\-]{3,}\.amazonaws\.com(\.cn)?$/
|
41
|
+
]
|
42
|
+
|
43
|
+
def initialize
|
44
|
+
@cached_pems = {}
|
45
|
+
end
|
46
|
+
|
47
|
+
# @param [String<JSON>] message_body
|
48
|
+
# @return [Boolean] Returns `true` if the given message has been
|
49
|
+
# successfully verified. Returns `false` otherwise.
|
50
|
+
def authentic?(message_body)
|
51
|
+
authenticate!(message_body)
|
52
|
+
rescue VerificationError
|
53
|
+
false
|
54
|
+
end
|
55
|
+
|
56
|
+
# @param [String<JSON>] message_body
|
57
|
+
# @return [Boolean] Returns `true` when the given message has been
|
58
|
+
# successfully verified.
|
59
|
+
# @raise [VerificationError] Raised when the given message has failed
|
60
|
+
# verification.
|
61
|
+
def authenticate!(message_body)
|
62
|
+
msg = MultiJson.load(message_body)
|
63
|
+
if public_key(msg).verify(sha1, signature(msg), canonical_string(msg))
|
64
|
+
true
|
65
|
+
else
|
66
|
+
msg = 'the authenticity of the message cannot be verified'
|
67
|
+
raise VerificationError, msg
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def sha1
|
74
|
+
OpenSSL::Digest::SHA1.new
|
75
|
+
end
|
76
|
+
|
77
|
+
def signature(message)
|
78
|
+
Base64.decode64(message['Signature'])
|
79
|
+
end
|
80
|
+
|
81
|
+
def canonical_string(message)
|
82
|
+
parts = []
|
83
|
+
SIGNABLE_KEYS.each do |key|
|
84
|
+
value = message[key]
|
85
|
+
unless value.nil? or value.empty?
|
86
|
+
parts << "#{key}\n#{value}\n"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
parts.join
|
90
|
+
end
|
91
|
+
|
92
|
+
def public_key(message)
|
93
|
+
x509_url = URI.parse(message['SigningCertURL'])
|
94
|
+
x509 = OpenSSL::X509::Certificate.new(pem(x509_url))
|
95
|
+
OpenSSL::PKey::RSA.new(x509.public_key)
|
96
|
+
end
|
97
|
+
|
98
|
+
def pem(uri)
|
99
|
+
if @cached_pems[uri.to_s]
|
100
|
+
@cached_pems[uri.to_s]
|
101
|
+
else
|
102
|
+
@cached_pems[uri.to_s] = download_pem(uri)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def download_pem(uri)
|
107
|
+
verify_uri!(uri)
|
108
|
+
https_get(uri)
|
109
|
+
end
|
110
|
+
|
111
|
+
def verify_uri!(uri)
|
112
|
+
verify_https!(uri)
|
113
|
+
verify_hosted_by_aws!(uri)
|
114
|
+
verify_pem!(uri)
|
115
|
+
end
|
116
|
+
|
117
|
+
def verify_https!(uri)
|
118
|
+
unless uri.scheme == 'https'
|
119
|
+
msg = "the SigningCertURL must be https, got: #{uri}"
|
120
|
+
raise VerificationError, msg
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def verify_hosted_by_aws!(uri)
|
125
|
+
unless AWS_HOSTNAMES.any? { |pattern| pattern.match(uri.host) }
|
126
|
+
msg = "signing cert is not hosted by AWS: #{uri}"
|
127
|
+
raise VerificationError, msg
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def verify_pem!(uri)
|
132
|
+
unless File.extname(uri.path) == '.pem'
|
133
|
+
msg = "the SigningCertURL must link to a .pem file"
|
134
|
+
raise VerificationError, msg
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def https_get(uri, failed_attempts = 0)
|
139
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
140
|
+
http.use_ssl = true
|
141
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
142
|
+
http.start
|
143
|
+
resp = http.request(Net::HTTP::Get.new(uri.request_uri))
|
144
|
+
http.finish
|
145
|
+
if resp.code == '200'
|
146
|
+
resp.body
|
147
|
+
else
|
148
|
+
raise VerificationError, resp.body
|
149
|
+
end
|
150
|
+
rescue => error
|
151
|
+
failed_attempts += 1
|
152
|
+
retry if failed_attempts < 3
|
153
|
+
raise VerificationError, error.message
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
@@ -0,0 +1,521 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module Aws
|
4
|
+
module SQS
|
5
|
+
|
6
|
+
# A utility class for long polling messages in a loop. **Messages are
|
7
|
+
# automatically deleted from the queue at the end of the given block.**
|
8
|
+
#
|
9
|
+
# poller = Aws::SQS::QueuePoller.new(queue_url)
|
10
|
+
#
|
11
|
+
# poller.poll do |msg|
|
12
|
+
# puts msg.body
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# ## Long Polling
|
16
|
+
#
|
17
|
+
# By default, messages are received using long polling. This
|
18
|
+
# method will force a default `:wait_time_seconds` of 20 seconds.
|
19
|
+
# If you prefer to use the queue default wait time, then pass
|
20
|
+
# a `nil` value for `:wait_time_seconds`.
|
21
|
+
#
|
22
|
+
# # disables 20 second default, use queue ReceiveMessageWaitTimeSeconds
|
23
|
+
# poller.poll(wait_time_seconds:nil) do |msg|
|
24
|
+
# # ...
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# When disabling `:wait_time_seconds` by passing `nil`, you must
|
28
|
+
# ensure the queue `ReceiveMessageWaitTimeSeconds` attribute is
|
29
|
+
# set to a non-zero value, or you will be short-polling.
|
30
|
+
# This will trigger significantly more API calls.
|
31
|
+
#
|
32
|
+
# ## Batch Receiving Messages
|
33
|
+
#
|
34
|
+
# You can specify a maximum number of messages to receive with
|
35
|
+
# each polling attempt via `:max_number_of_messages`. When this is
|
36
|
+
# set to a positive value, greater than 1, the block will receive
|
37
|
+
# an array of messages, instead of a single message.
|
38
|
+
#
|
39
|
+
# # receives and yields 1 message at a time
|
40
|
+
# poller.poll do |msg|
|
41
|
+
# # ...
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# # receives and yields up to 10 messages at a time
|
45
|
+
# poller.poll(max_number_of_messages:10) do |messages|
|
46
|
+
# messages.each do |msg|
|
47
|
+
# # ...
|
48
|
+
# end
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# The maximum value for `:max_number_of_messages` is enforced by
|
52
|
+
# Amazon SQS.
|
53
|
+
#
|
54
|
+
# ## Visibility Timeouts
|
55
|
+
#
|
56
|
+
# When receiving messages, you have a fixed amount of time to process
|
57
|
+
# and delete the message before it is added back into the queue. This
|
58
|
+
# is the visibility timeout. By default, the queue's `VisibilityTimeout`
|
59
|
+
# attribute is used. You can provide an alternative visibility timeout
|
60
|
+
# when polling.
|
61
|
+
#
|
62
|
+
# # queue default VisibilityTimeout
|
63
|
+
# poller.poll do |msg|
|
64
|
+
# end
|
65
|
+
#
|
66
|
+
# # custom visibility timeout
|
67
|
+
# poller.poll(visibility_timeout:10) do |msg|
|
68
|
+
# end
|
69
|
+
#
|
70
|
+
#
|
71
|
+
# You can reset the visibility timeout of a single message by calling
|
72
|
+
# {#change_message_visibility_timeout}. This is useful when you need
|
73
|
+
# more time to finish processing the message.
|
74
|
+
#
|
75
|
+
# poller.poll do |msg|
|
76
|
+
#
|
77
|
+
# # do work ...
|
78
|
+
#
|
79
|
+
# # need more time for processing
|
80
|
+
# poller.change_message_visibility_timeout(msg, 60)
|
81
|
+
#
|
82
|
+
# # finish work ...
|
83
|
+
#
|
84
|
+
# end
|
85
|
+
#
|
86
|
+
# If you change the visibility timeout of a message to zero, it will
|
87
|
+
# return to the queue immediately.
|
88
|
+
#
|
89
|
+
# ## Deleting Messages
|
90
|
+
#
|
91
|
+
# Messages are deleted from the queue when the block returns normally.
|
92
|
+
#
|
93
|
+
# poller.poll do |msg|
|
94
|
+
# # do work
|
95
|
+
# end # messages deleted here
|
96
|
+
#
|
97
|
+
# You can skip message deletion by passing `skip_delete: true`.
|
98
|
+
# This allows you to manually delete the messages using
|
99
|
+
# {#delete_message}, or {#delete_messages}.
|
100
|
+
#
|
101
|
+
# # single message
|
102
|
+
# poller.poll(skip_delete: true) do |msg|
|
103
|
+
# poller.delete_message(msg) # if successful
|
104
|
+
# end
|
105
|
+
#
|
106
|
+
# # batch delete messages
|
107
|
+
# poller.poll(skip_delete: true, max_number_of_messages:10) do |messages|
|
108
|
+
# poller.delete_messages(messages)
|
109
|
+
# end
|
110
|
+
#
|
111
|
+
# Another way to manage message deletion is to throw `:skip_delete`
|
112
|
+
# from the poll block. You can use this to choose when a message, or
|
113
|
+
# message batch is deleted on an individual basis. This can be very
|
114
|
+
# useful when you are capturing temporal errors and wish for the
|
115
|
+
# message to timeout.
|
116
|
+
#
|
117
|
+
# poller.poll do |msg|
|
118
|
+
# begin
|
119
|
+
# # do work
|
120
|
+
# rescue
|
121
|
+
# # unexpected error occurred while processing messages,
|
122
|
+
# # log it, and skip delete so it can be re-processed later
|
123
|
+
# throw :skip_delete
|
124
|
+
# end
|
125
|
+
# end
|
126
|
+
#
|
127
|
+
# ## Terminating the Polling Loop
|
128
|
+
#
|
129
|
+
# By default, polling will continue indefinitely. You can stop
|
130
|
+
# the poller by providing an idle timeout or by throwing `:stop_polling`
|
131
|
+
# from the {#before_request} callback.
|
132
|
+
#
|
133
|
+
# ### `:idle_timeout` Option
|
134
|
+
#
|
135
|
+
# This is a configurable, maximum number of seconds to wait for a
|
136
|
+
# new message before the polling loop exists. By default, there is
|
137
|
+
# no idle timeout.
|
138
|
+
#
|
139
|
+
# # stops polling after a minute of no received messages
|
140
|
+
# poller.poll(idle_timeout: 60) do |msg|
|
141
|
+
# # ...
|
142
|
+
# end
|
143
|
+
#
|
144
|
+
# ### Throw `:stop_polling`
|
145
|
+
#
|
146
|
+
# If you want more fine grained control, you can configure a
|
147
|
+
# before request callback to trigger before each long poll. Throwing
|
148
|
+
# `:stop_polling` from this callback will cause the poller to exit
|
149
|
+
# normally without making the next request.
|
150
|
+
#
|
151
|
+
# # stop after processing 100 messages
|
152
|
+
# poller.before_request do |stats|
|
153
|
+
# throw :stop_polling if stats.receive_message_count >= 100
|
154
|
+
# end
|
155
|
+
#
|
156
|
+
# poller.poll do |msg|
|
157
|
+
# # do work ...
|
158
|
+
# end
|
159
|
+
#
|
160
|
+
# ## Tracking Progress
|
161
|
+
#
|
162
|
+
# The poller will automatically track a few statistics client-side in
|
163
|
+
# a {PollerStats} object. You can access the poller stats
|
164
|
+
# three ways:
|
165
|
+
#
|
166
|
+
# * The first block argument of {#before_request}
|
167
|
+
# * The second block argument of {#poll}.
|
168
|
+
# * The return value from {#poll}.
|
169
|
+
#
|
170
|
+
# Here are examples of accessing the statistics.
|
171
|
+
#
|
172
|
+
# * Configure a {#before_request} callback.
|
173
|
+
#
|
174
|
+
# ```
|
175
|
+
# poller.before_reqeust do |stats|
|
176
|
+
# logger.info("requests: #{stats.request_count}")
|
177
|
+
# logger.info("messages: #{stats.received_message_count}")
|
178
|
+
# logger.info("last-timestamp: #{stats.last_message_received_at}")
|
179
|
+
# end
|
180
|
+
# ```
|
181
|
+
#
|
182
|
+
# * Accept a 2nd argument in the poll block, for example:
|
183
|
+
#
|
184
|
+
# ```
|
185
|
+
# poller.poll do |msg, stats|
|
186
|
+
# logger.info("requests: #{stats.request_count}")
|
187
|
+
# logger.info("messages: #{stats.received_message_count}")
|
188
|
+
# logger.info("last-timestamp: #{stats.last_message_received_at}")
|
189
|
+
# end
|
190
|
+
# ```
|
191
|
+
#
|
192
|
+
# * Return value:
|
193
|
+
#
|
194
|
+
# ```
|
195
|
+
# stats = poller.poll(idle_timeout:10) do |msg|
|
196
|
+
# # do work ...
|
197
|
+
# end
|
198
|
+
# logger.info("requests: #{stats.request_count}")
|
199
|
+
# logger.info("messages: #{stats.received_message_count}")
|
200
|
+
# logger.info("last-timestamp: #{stats.last_message_received_at}")
|
201
|
+
# ```
|
202
|
+
#
|
203
|
+
class QueuePoller
|
204
|
+
|
205
|
+
# @param [String] queue_url
|
206
|
+
# @option options [Client] :client
|
207
|
+
# @option (see #poll)
|
208
|
+
def initialize(queue_url, options = {})
|
209
|
+
@queue_url = queue_url
|
210
|
+
@client = options.delete(:client) || Client.new
|
211
|
+
@default_config = PollerConfig.new(options)
|
212
|
+
end
|
213
|
+
|
214
|
+
# @return [String]
|
215
|
+
attr_reader :queue_url
|
216
|
+
|
217
|
+
# @return [Client]
|
218
|
+
attr_reader :client
|
219
|
+
|
220
|
+
# @return [PollerConfig]
|
221
|
+
attr_reader :default_config
|
222
|
+
|
223
|
+
# Registers a callback that is invoked once before every polling
|
224
|
+
# attempt.
|
225
|
+
#
|
226
|
+
# poller.before_reqeust do |stats|
|
227
|
+
# logger.info("requests: #{stats.request_count}")
|
228
|
+
# logger.info("messages: #{stats.received_message_count}")
|
229
|
+
# logger.info("last-timestamp: #{stats.last_message_received_at}")
|
230
|
+
# end
|
231
|
+
#
|
232
|
+
# poller.poll do |msg|
|
233
|
+
# # do work ...
|
234
|
+
# end
|
235
|
+
#
|
236
|
+
# ## `:stop_polling`
|
237
|
+
#
|
238
|
+
# If you throw `:stop_polling` from the {#before_request} callback,
|
239
|
+
# then the poller will exit normally before making the next long
|
240
|
+
# poll request.
|
241
|
+
#
|
242
|
+
# poller.before_reqeust do |stats|
|
243
|
+
# throw :stop_polling if stats.received_messages >= 100
|
244
|
+
# end
|
245
|
+
#
|
246
|
+
# # at most 100 messages will be yielded
|
247
|
+
# poller.poll do |msg|
|
248
|
+
# # do work ...
|
249
|
+
# end
|
250
|
+
#
|
251
|
+
# @yieldparam [PollerStats] stats An object that tracks a few
|
252
|
+
# client-side statistics about the queue polling.
|
253
|
+
#
|
254
|
+
# @return [void]
|
255
|
+
def before_request(&block)
|
256
|
+
@default_config = @default_config.with(before_request: Proc.new)
|
257
|
+
end
|
258
|
+
|
259
|
+
# Polls the queue, yielded a message, or an array of messages.
|
260
|
+
# Messages are automatically deleted from the queue at the
|
261
|
+
# end of the given block. See the class documentation on
|
262
|
+
# {QueuePoller} for more examples.
|
263
|
+
#
|
264
|
+
# @example Basic example, loops indefinitely
|
265
|
+
#
|
266
|
+
# poller.poll do |msg|
|
267
|
+
# # ...
|
268
|
+
# end
|
269
|
+
#
|
270
|
+
# @example Receives and deletes messages as a batch
|
271
|
+
#
|
272
|
+
# poller.poll(max_number_of_messages:10) do |messages|
|
273
|
+
# messages.each do |msg|
|
274
|
+
# # ...
|
275
|
+
# end
|
276
|
+
# end
|
277
|
+
#
|
278
|
+
# @option options [Integer] :wait_time_seconds (20) The
|
279
|
+
# long polling interval. Messages are yielded as soon as they are
|
280
|
+
# received. The `:wait_time_seconds` option specifies the max
|
281
|
+
# duration for each polling attempt before a new request is
|
282
|
+
# sent to receive messages.
|
283
|
+
#
|
284
|
+
# @option options [Integer] :max_number_of_messages (1) The maximum
|
285
|
+
# number of messages to yield from each polling attempt.
|
286
|
+
# Values can be from 1 to 10.
|
287
|
+
#
|
288
|
+
# @option options [Integer] :visibility_timeout (nil)
|
289
|
+
# The number of seconds you have to process a message before
|
290
|
+
# it is put back into the queue and can be received again.
|
291
|
+
# By default, the queue's
|
292
|
+
#
|
293
|
+
# @option opitons [Array<String>] :attribute_names ([])
|
294
|
+
# The list of attributes that need to be returned along with each
|
295
|
+
# message. Valid attribute names include:
|
296
|
+
#
|
297
|
+
# * `All` - All attributes.
|
298
|
+
# * `ApproximateFirstReceiveTimestamp` - The time when the message
|
299
|
+
# was first received from the queue (epoch time in milliseconds).
|
300
|
+
# * `ApproximateReceiveCount` - The number of times a message has
|
301
|
+
# been received from the queue but not deleted.
|
302
|
+
# * `SenderId` - The AWS account number (or the IP address, if
|
303
|
+
# anonymous access is allowed) of the sender.
|
304
|
+
# * `SentTimestamp` - The time when the message was sent to the
|
305
|
+
# queue (epoch time in milliseconds).
|
306
|
+
#
|
307
|
+
# @option options [Array<String>] :message_attribute_names ([])
|
308
|
+
# A list of message attributes to receive. You can receive
|
309
|
+
# all messages by using `All` or `.*`. You can also use
|
310
|
+
# `foo.*` to return all message attributes starting with the
|
311
|
+
# `foo` prefix.
|
312
|
+
#
|
313
|
+
# @option options [Integer] :idle_timeout (nil) Polling terminates
|
314
|
+
# gracefully when `:idle_timeout` seconds have passed without
|
315
|
+
# receiving any messages.
|
316
|
+
#
|
317
|
+
# @option options [Boolean] :skip_delete (false) When `true`, messages
|
318
|
+
# are not deleted after polling block. If you wish to delete
|
319
|
+
# received messages, you will need to call `#delete_message` or
|
320
|
+
# `#delete_messages` manually.
|
321
|
+
#
|
322
|
+
# @option options [Proc] :before_request (nil) Called before each
|
323
|
+
# polling attempt. This proc receives a single argument, an
|
324
|
+
# instance of {PollerStats}.
|
325
|
+
#
|
326
|
+
# @return [PollerStats]
|
327
|
+
def poll(options = {}, &block)
|
328
|
+
config = @default_config.with(options)
|
329
|
+
stats = PollerStats.new
|
330
|
+
catch(:stop_polling) do
|
331
|
+
loop do
|
332
|
+
messages = get_messages(config, stats)
|
333
|
+
if messages.empty?
|
334
|
+
check_idle_timeout(config, stats, messages)
|
335
|
+
else
|
336
|
+
process_messages(config, stats, messages, &block)
|
337
|
+
end
|
338
|
+
end
|
339
|
+
end
|
340
|
+
stats.polling_stopped_at = Time.now
|
341
|
+
stats
|
342
|
+
end
|
343
|
+
|
344
|
+
# @note This method should be called from inside a {#poll} block.
|
345
|
+
# @param [#receipt_handle] message An object that responds to
|
346
|
+
# `#receipt_handle`.
|
347
|
+
# @param [Integer] seconds
|
348
|
+
def change_message_visibility_timeout(message, seconds)
|
349
|
+
@client.change_message_visibility_timeout({
|
350
|
+
queue_url: @queue_url,
|
351
|
+
receipt_handle: message.receipt_handle,
|
352
|
+
visibility_timeout: seconds,
|
353
|
+
})
|
354
|
+
end
|
355
|
+
|
356
|
+
# @note This method should be called from inside a {#poll} block.
|
357
|
+
# @param [#receipt_handle] message An object that responds to
|
358
|
+
# `#receipt_handle`.
|
359
|
+
def delete_message(message)
|
360
|
+
@client.delete_message({
|
361
|
+
queue_url: @queue_url,
|
362
|
+
receipt_handle: message.receipt_handle,
|
363
|
+
})
|
364
|
+
end
|
365
|
+
|
366
|
+
# @note This method should be called from inside a {#poll} block.
|
367
|
+
# @param [Array<#message_id, #receipt_handle>] messages An array of received
|
368
|
+
# messages. Each object must respond to `#message_id` and
|
369
|
+
# `#receipt_handle`.
|
370
|
+
def delete_messages(messages)
|
371
|
+
@client.delete_message_batch(
|
372
|
+
queue_url: @queue_url,
|
373
|
+
entries: messages.map { |msg|
|
374
|
+
{ id: msg.message_id, receipt_handle: msg.receipt_handle }
|
375
|
+
}
|
376
|
+
)
|
377
|
+
end
|
378
|
+
|
379
|
+
private
|
380
|
+
|
381
|
+
def get_messages(config, stats)
|
382
|
+
config.before_request.call(stats) if config.before_request
|
383
|
+
messages = send_request(config).messages
|
384
|
+
stats.request_count += 1
|
385
|
+
messages
|
386
|
+
end
|
387
|
+
|
388
|
+
def send_request(config)
|
389
|
+
params = config.request_params.merge(queue_url: @queue_url)
|
390
|
+
@client.receive_message(params)
|
391
|
+
end
|
392
|
+
|
393
|
+
def check_idle_timeout(config, stats, messages)
|
394
|
+
if config.idle_timeout
|
395
|
+
since = stats.last_message_received_at || stats.polling_started_at
|
396
|
+
idle_time = Time.now - since
|
397
|
+
throw :stop_polling if idle_time > config.idle_timeout
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
def process_messages(config, stats, messages, &block)
|
402
|
+
stats.received_message_count += messages.count
|
403
|
+
stats.last_message_received_at = Time.now
|
404
|
+
catch(:skip_delete) do
|
405
|
+
yield_messages(config, messages, stats, &block)
|
406
|
+
delete_messages(messages) unless config.skip_delete
|
407
|
+
end
|
408
|
+
end
|
409
|
+
|
410
|
+
def yield_messages(config, messages, stats, &block)
|
411
|
+
if config.request_params[:max_number_of_messages] == 1
|
412
|
+
messages.each do |msg|
|
413
|
+
yield(msg, stats)
|
414
|
+
end
|
415
|
+
else
|
416
|
+
yield(messages, stats)
|
417
|
+
end
|
418
|
+
end
|
419
|
+
|
420
|
+
# Statistics tracked client-side by the {QueuePoller}.
|
421
|
+
class PollerStats
|
422
|
+
|
423
|
+
def initialize
|
424
|
+
@request_count = 0
|
425
|
+
@received_message_count = 0
|
426
|
+
@last_message_received_at = nil
|
427
|
+
@polling_started_at = Time.now
|
428
|
+
@polling_stopped_at = nil
|
429
|
+
end
|
430
|
+
|
431
|
+
# @return [Integer]
|
432
|
+
attr_accessor :request_count
|
433
|
+
|
434
|
+
# @return [Integer]
|
435
|
+
attr_accessor :received_message_count
|
436
|
+
|
437
|
+
# @return [Time,nil]
|
438
|
+
attr_accessor :last_message_received_at
|
439
|
+
|
440
|
+
# @return [Time]
|
441
|
+
attr_accessor :polling_started_at
|
442
|
+
|
443
|
+
# @return [Time,nil]
|
444
|
+
attr_accessor :polling_stopped_at
|
445
|
+
|
446
|
+
end
|
447
|
+
|
448
|
+
# A read-only set of configuration used by the QueuePoller.
|
449
|
+
class PollerConfig
|
450
|
+
|
451
|
+
# @api private
|
452
|
+
CONFIG_OPTIONS = Set.new([
|
453
|
+
:idle_timeout,
|
454
|
+
:skip_delete,
|
455
|
+
:before_request,
|
456
|
+
])
|
457
|
+
|
458
|
+
# @api private
|
459
|
+
PARAM_OPTIONS = Set.new([
|
460
|
+
:wait_time_seconds,
|
461
|
+
:max_number_of_messages,
|
462
|
+
:visibility_timeout,
|
463
|
+
:attribute_names,
|
464
|
+
:message_attribute_names,
|
465
|
+
])
|
466
|
+
|
467
|
+
# @return [Integer,nil]
|
468
|
+
attr_reader :idle_timeout
|
469
|
+
|
470
|
+
# @return [Boolean]
|
471
|
+
attr_reader :skip_delete
|
472
|
+
|
473
|
+
# @return [Proc,nil]
|
474
|
+
attr_reader :before_request
|
475
|
+
|
476
|
+
# @return [Hash]
|
477
|
+
attr_reader :request_params
|
478
|
+
|
479
|
+
def initialize(options)
|
480
|
+
@idle_timeout = nil
|
481
|
+
@skip_delete = false
|
482
|
+
@before_request = nil
|
483
|
+
@request_params = {
|
484
|
+
wait_time_seconds: 20,
|
485
|
+
max_number_of_messages: 1,
|
486
|
+
visibility_timeout: nil,
|
487
|
+
attribute_names: ['All'],
|
488
|
+
message_attribute_names: ['All'],
|
489
|
+
}
|
490
|
+
options.each do |opt_name, value|
|
491
|
+
if CONFIG_OPTIONS.include?(opt_name)
|
492
|
+
instance_variable_set("@#{opt_name}", value)
|
493
|
+
elsif PARAM_OPTIONS.include?(opt_name)
|
494
|
+
@request_params[opt_name] = value
|
495
|
+
else
|
496
|
+
raise ArgumentError, "invalid option #{opt_name.inspect}"
|
497
|
+
end
|
498
|
+
end
|
499
|
+
@request_params.freeze
|
500
|
+
freeze
|
501
|
+
end
|
502
|
+
|
503
|
+
# @return [PollerConfig] Returns a new {PollerConfig} instance
|
504
|
+
# with the given options applied.
|
505
|
+
def with(options)
|
506
|
+
self.class.new(to_h.merge(options))
|
507
|
+
end
|
508
|
+
|
509
|
+
private
|
510
|
+
|
511
|
+
def to_h
|
512
|
+
hash = {}
|
513
|
+
CONFIG_OPTIONS.each { |key| hash[key] = send(key) }
|
514
|
+
PARAM_OPTIONS.each { |key| hash[key] = @request_params[key] }
|
515
|
+
hash
|
516
|
+
end
|
517
|
+
|
518
|
+
end
|
519
|
+
end
|
520
|
+
end
|
521
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: aws-sdk-resources
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.31
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Amazon Web Services
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-03-
|
11
|
+
date: 2015-03-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: aws-sdk-core
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - '='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 2.0.
|
19
|
+
version: 2.0.31
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - '='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 2.0.
|
26
|
+
version: 2.0.31
|
27
27
|
description: Provides resource oriented interfaces and other higher-level abstractions
|
28
28
|
for many AWS services. This gem is part of the official AWS SDK for Ruby.
|
29
29
|
email:
|
@@ -78,6 +78,10 @@ files:
|
|
78
78
|
- lib/aws-sdk-resources/services/s3/object.rb
|
79
79
|
- lib/aws-sdk-resources/services/s3/object_summary.rb
|
80
80
|
- lib/aws-sdk-resources/services/s3/public_url.rb
|
81
|
+
- lib/aws-sdk-resources/services/sns.rb
|
82
|
+
- lib/aws-sdk-resources/services/sns/message_verifier.rb
|
83
|
+
- lib/aws-sdk-resources/services/sqs.rb
|
84
|
+
- lib/aws-sdk-resources/services/sqs/queue_poller.rb
|
81
85
|
- lib/aws-sdk-resources/source.rb
|
82
86
|
homepage: http://github.com/aws/aws-sdk-ruby
|
83
87
|
licenses:
|