aws-sdk-resources 2.11.416 → 3.67.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/aws-v3.rb +201 -0
- data/lib/aws-sdk-resources.rb +221 -87
- metadata +3042 -69
- data/lib/aws-sdk-resources/batch.rb +0 -143
- data/lib/aws-sdk-resources/builder.rb +0 -85
- data/lib/aws-sdk-resources/builder_sources.rb +0 -105
- data/lib/aws-sdk-resources/collection.rb +0 -107
- data/lib/aws-sdk-resources/definition.rb +0 -331
- data/lib/aws-sdk-resources/documenter.rb +0 -70
- data/lib/aws-sdk-resources/documenter/base_operation_documenter.rb +0 -279
- data/lib/aws-sdk-resources/documenter/data_operation_documenter.rb +0 -25
- data/lib/aws-sdk-resources/documenter/has_many_operation_documenter.rb +0 -69
- data/lib/aws-sdk-resources/documenter/has_operation_documenter.rb +0 -66
- data/lib/aws-sdk-resources/documenter/operation_documenter.rb +0 -20
- data/lib/aws-sdk-resources/documenter/resource_operation_documenter.rb +0 -53
- data/lib/aws-sdk-resources/documenter/waiter_operation_documenter.rb +0 -77
- data/lib/aws-sdk-resources/errors.rb +0 -15
- data/lib/aws-sdk-resources/operation_methods.rb +0 -83
- data/lib/aws-sdk-resources/operations.rb +0 -280
- data/lib/aws-sdk-resources/options.rb +0 -17
- data/lib/aws-sdk-resources/request.rb +0 -39
- data/lib/aws-sdk-resources/request_params.rb +0 -140
- data/lib/aws-sdk-resources/resource.rb +0 -243
- data/lib/aws-sdk-resources/services/ec2.rb +0 -21
- data/lib/aws-sdk-resources/services/ec2/instance.rb +0 -29
- data/lib/aws-sdk-resources/services/iam.rb +0 -19
- data/lib/aws-sdk-resources/services/s3.rb +0 -20
- data/lib/aws-sdk-resources/services/s3/bucket.rb +0 -127
- data/lib/aws-sdk-resources/services/s3/encryption.rb +0 -21
- data/lib/aws-sdk-resources/services/s3/encryption/client.rb +0 -369
- data/lib/aws-sdk-resources/services/s3/encryption/decrypt_handler.rb +0 -174
- data/lib/aws-sdk-resources/services/s3/encryption/default_cipher_provider.rb +0 -63
- data/lib/aws-sdk-resources/services/s3/encryption/default_key_provider.rb +0 -38
- data/lib/aws-sdk-resources/services/s3/encryption/encrypt_handler.rb +0 -50
- data/lib/aws-sdk-resources/services/s3/encryption/errors.rb +0 -13
- data/lib/aws-sdk-resources/services/s3/encryption/io_auth_decrypter.rb +0 -56
- data/lib/aws-sdk-resources/services/s3/encryption/io_decrypter.rb +0 -29
- data/lib/aws-sdk-resources/services/s3/encryption/io_encrypter.rb +0 -69
- data/lib/aws-sdk-resources/services/s3/encryption/key_provider.rb +0 -29
- data/lib/aws-sdk-resources/services/s3/encryption/kms_cipher_provider.rb +0 -71
- data/lib/aws-sdk-resources/services/s3/encryption/materials.rb +0 -58
- data/lib/aws-sdk-resources/services/s3/encryption/utils.rb +0 -79
- data/lib/aws-sdk-resources/services/s3/file_downloader.rb +0 -169
- data/lib/aws-sdk-resources/services/s3/file_part.rb +0 -75
- data/lib/aws-sdk-resources/services/s3/file_uploader.rb +0 -58
- data/lib/aws-sdk-resources/services/s3/multipart_file_uploader.rb +0 -187
- data/lib/aws-sdk-resources/services/s3/multipart_upload.rb +0 -42
- data/lib/aws-sdk-resources/services/s3/multipart_upload_error.rb +0 -16
- data/lib/aws-sdk-resources/services/s3/object.rb +0 -290
- data/lib/aws-sdk-resources/services/s3/object_copier.rb +0 -99
- data/lib/aws-sdk-resources/services/s3/object_multipart_copier.rb +0 -180
- data/lib/aws-sdk-resources/services/s3/object_summary.rb +0 -73
- data/lib/aws-sdk-resources/services/s3/presigned_post.rb +0 -647
- data/lib/aws-sdk-resources/services/sns.rb +0 -7
- data/lib/aws-sdk-resources/services/sns/message_verifier.rb +0 -171
- data/lib/aws-sdk-resources/services/sqs.rb +0 -7
- data/lib/aws-sdk-resources/services/sqs/queue_poller.rb +0 -521
- data/lib/aws-sdk-resources/source.rb +0 -39
@@ -1,171 +0,0 @@
|
|
1
|
-
require 'net/http'
|
2
|
-
require 'openssl'
|
3
|
-
require 'base64'
|
4
|
-
|
5
|
-
module Aws
|
6
|
-
module SNS
|
7
|
-
|
8
|
-
# A utility class that can be used to verify the authenticity of messages
|
9
|
-
# sent by Amazon SNS.
|
10
|
-
#
|
11
|
-
# verifier = Aws::SNS::MessageVerifier.new
|
12
|
-
#
|
13
|
-
# # returns true/false
|
14
|
-
# verifier.authentic?(message_body)
|
15
|
-
#
|
16
|
-
# # raises a Aws::SNS::MessageVerifier::VerificationError on failure
|
17
|
-
# verifier.authenticate!(message_body)
|
18
|
-
#
|
19
|
-
# You can re-use a single {MessageVerifier} instance to authenticate
|
20
|
-
# multiple SNS messages.
|
21
|
-
class MessageVerifier
|
22
|
-
|
23
|
-
class VerificationError < StandardError; end
|
24
|
-
|
25
|
-
# @api private
|
26
|
-
SIGNABLE_KEYS = [
|
27
|
-
'Message',
|
28
|
-
'MessageId',
|
29
|
-
'Subject',
|
30
|
-
'SubscribeURL',
|
31
|
-
'Timestamp',
|
32
|
-
'Token',
|
33
|
-
'TopicArn',
|
34
|
-
'Type',
|
35
|
-
].freeze
|
36
|
-
|
37
|
-
# @api private
|
38
|
-
AWS_HOSTNAMES = [
|
39
|
-
/^sns\.[a-zA-Z0-9\-]{3,}\.amazonaws\.com(\.cn)?$/
|
40
|
-
]
|
41
|
-
|
42
|
-
def initialize
|
43
|
-
@cached_pems = {}
|
44
|
-
end
|
45
|
-
|
46
|
-
# @param [String<JSON>] message_body
|
47
|
-
# @return [Boolean] Returns `true` if the given message has been
|
48
|
-
# successfully verified. Returns `false` otherwise.
|
49
|
-
def authentic?(message_body)
|
50
|
-
authenticate!(message_body)
|
51
|
-
rescue VerificationError
|
52
|
-
false
|
53
|
-
end
|
54
|
-
|
55
|
-
# @param [String<JSON>] message_body
|
56
|
-
# @return [Boolean] Returns `true` when the given message has been
|
57
|
-
# successfully verified.
|
58
|
-
# @raise [VerificationError] Raised when the given message has failed
|
59
|
-
# verification.
|
60
|
-
def authenticate!(message_body)
|
61
|
-
msg = Json.load(message_body)
|
62
|
-
msg = convert_lambda_msg(msg) if is_from_lambda(msg)
|
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 is_from_lambda(message)
|
74
|
-
message.key? 'SigningCertUrl'
|
75
|
-
end
|
76
|
-
|
77
|
-
def convert_lambda_msg(message)
|
78
|
-
cert_url = message.delete('SigningCertUrl')
|
79
|
-
unsubscribe_url = message.delete('UnsubscribeUrl')
|
80
|
-
|
81
|
-
message['SigningCertURL'] = cert_url
|
82
|
-
message['UnsubscribeURL'] = unsubscribe_url
|
83
|
-
message
|
84
|
-
end
|
85
|
-
|
86
|
-
def sha1
|
87
|
-
OpenSSL::Digest::SHA1.new
|
88
|
-
end
|
89
|
-
|
90
|
-
def signature(message)
|
91
|
-
Base64.decode64(message['Signature'])
|
92
|
-
end
|
93
|
-
|
94
|
-
def canonical_string(message)
|
95
|
-
parts = []
|
96
|
-
SIGNABLE_KEYS.each do |key|
|
97
|
-
value = message[key]
|
98
|
-
unless value.nil? or value.empty?
|
99
|
-
parts << "#{key}\n#{value}\n"
|
100
|
-
end
|
101
|
-
end
|
102
|
-
parts.join
|
103
|
-
end
|
104
|
-
|
105
|
-
def public_key(message)
|
106
|
-
x509_url = URI.parse(message['SigningCertURL'])
|
107
|
-
x509 = OpenSSL::X509::Certificate.new(pem(x509_url))
|
108
|
-
OpenSSL::PKey::RSA.new(x509.public_key)
|
109
|
-
end
|
110
|
-
|
111
|
-
def pem(uri)
|
112
|
-
if @cached_pems[uri.to_s]
|
113
|
-
@cached_pems[uri.to_s]
|
114
|
-
else
|
115
|
-
@cached_pems[uri.to_s] = download_pem(uri)
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
def download_pem(uri)
|
120
|
-
verify_uri!(uri)
|
121
|
-
https_get(uri)
|
122
|
-
end
|
123
|
-
|
124
|
-
def verify_uri!(uri)
|
125
|
-
verify_https!(uri)
|
126
|
-
verify_hosted_by_aws!(uri)
|
127
|
-
verify_pem!(uri)
|
128
|
-
end
|
129
|
-
|
130
|
-
def verify_https!(uri)
|
131
|
-
unless uri.scheme == 'https'
|
132
|
-
msg = "the SigningCertURL must be https, got: #{uri}"
|
133
|
-
raise VerificationError, msg
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
def verify_hosted_by_aws!(uri)
|
138
|
-
unless AWS_HOSTNAMES.any? { |pattern| pattern.match(uri.host) }
|
139
|
-
msg = "signing cert is not hosted by AWS: #{uri}"
|
140
|
-
raise VerificationError, msg
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
def verify_pem!(uri)
|
145
|
-
unless File.extname(uri.path) == '.pem'
|
146
|
-
msg = "the SigningCertURL must link to a .pem file"
|
147
|
-
raise VerificationError, msg
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
def https_get(uri, failed_attempts = 0)
|
152
|
-
http = Net::HTTP.new(uri.host, uri.port)
|
153
|
-
http.use_ssl = true
|
154
|
-
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
155
|
-
http.start
|
156
|
-
resp = http.request(Net::HTTP::Get.new(uri.request_uri))
|
157
|
-
http.finish
|
158
|
-
if resp.code == '200'
|
159
|
-
resp.body
|
160
|
-
else
|
161
|
-
raise VerificationError, resp.body
|
162
|
-
end
|
163
|
-
rescue => error
|
164
|
-
failed_attempts += 1
|
165
|
-
retry if failed_attempts < 3
|
166
|
-
raise VerificationError, error.message
|
167
|
-
end
|
168
|
-
|
169
|
-
end
|
170
|
-
end
|
171
|
-
end
|
@@ -1,521 +0,0 @@
|
|
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.received_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_request 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_request 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_request 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: block) if block_given?
|
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 options [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({
|
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
|