clientside_aws 0.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/Dockerfile +46 -0
- data/Gemfile +23 -0
- data/Gemfile.lock +99 -0
- data/README.md +105 -0
- data/bin/clientside_aws_build +6 -0
- data/bin/clientside_aws_run +5 -0
- data/bin/clientside_aws_test +4 -0
- data/clientside_aws.gemspec +31 -0
- data/clientside_aws/dynamodb.rb +722 -0
- data/clientside_aws/ec2.rb +103 -0
- data/clientside_aws/elastic_transcoder.rb +179 -0
- data/clientside_aws/firehose.rb +13 -0
- data/clientside_aws/kinesis.rb +13 -0
- data/clientside_aws/mock/core.rb +7 -0
- data/clientside_aws/mock/firehose.rb +14 -0
- data/clientside_aws/mock/kinesis.rb +18 -0
- data/clientside_aws/mock/s3.rb +59 -0
- data/clientside_aws/mock/ses.rb +74 -0
- data/clientside_aws/mock/sns.rb +17 -0
- data/clientside_aws/s3.rb +223 -0
- data/clientside_aws/ses.rb +9 -0
- data/clientside_aws/sns.rb +41 -0
- data/clientside_aws/sqs.rb +233 -0
- data/docker/clientside-aws-run +3 -0
- data/docker/redis-server-run +2 -0
- data/index.rb +57 -0
- data/lib/clientside_aws.rb +27 -0
- data/lib/clientside_aws/configuration.rb +14 -0
- data/lib/clientside_aws/mock.rb +224 -0
- data/lib/clientside_aws/version.rb +3 -0
- data/public/images/jscruff.jpg +0 -0
- data/public/images/spacer.gif +0 -0
- data/public/images/stock_video.mp4 +0 -0
- data/spec/dynamodb_spec.rb +1069 -0
- data/spec/ec2_spec.rb +138 -0
- data/spec/firehose_spec.rb +16 -0
- data/spec/kinesis_spec.rb +22 -0
- data/spec/s3_spec.rb +219 -0
- data/spec/sns_spec.rb +72 -0
- data/spec/spec_helper.rb +71 -0
- data/spec/sqs_spec.rb +87 -0
- data/spec/test_client/test.rb +45 -0
- data/spec/transcoder_spec.rb +138 -0
- metadata +241 -0
@@ -0,0 +1,17 @@
|
|
1
|
+
module AWS
|
2
|
+
class SNS
|
3
|
+
class Client < Core::QueryClient
|
4
|
+
# Monkeypatch to save the last sent message
|
5
|
+
class V20100331
|
6
|
+
attr_reader :last_msg
|
7
|
+
|
8
|
+
def publish(target_arn:,
|
9
|
+
message_structure:,
|
10
|
+
message:,
|
11
|
+
message_attributes: nil)
|
12
|
+
@last_msg = message
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,223 @@
|
|
1
|
+
require 'builder'
|
2
|
+
|
3
|
+
helpers do
|
4
|
+
def get_file(bucket:, file_name:)
|
5
|
+
halt 404, objectNotFound if AWS_REDIS.hget("s3:bucket:#{bucket}:#{file_name}", 'body').nil?
|
6
|
+
|
7
|
+
body = download_file(bucket, file_name)
|
8
|
+
content_type = AWS_REDIS.hget("s3:bucket:#{bucket}:#{file_name}", 'content-type')
|
9
|
+
response.headers['content-type'] = content_type.nil? ? 'html' : content_type
|
10
|
+
# response.headers["Content-Length"] = body.length.to_s
|
11
|
+
response.headers['etag'] = Digest::MD5.hexdigest(body)
|
12
|
+
response.body = body
|
13
|
+
|
14
|
+
status 200
|
15
|
+
end
|
16
|
+
|
17
|
+
def list_buckets
|
18
|
+
buckets = AWS_REDIS.keys 's3:bucket:*'
|
19
|
+
|
20
|
+
xml = Builder::XmlMarkup.new
|
21
|
+
xml.instruct!
|
22
|
+
xml.ListAllMyBucketsResult(xmlns: 'http://s3.amazonaws.com/doc/2006-03-01') do
|
23
|
+
xml.Owner do
|
24
|
+
xml.tag!(:ID, SecureRandom.hex(10))
|
25
|
+
xml.tag!(:DisplayName, 'Fake Owner')
|
26
|
+
end
|
27
|
+
xml.Buckets do
|
28
|
+
buckets.each do |bucket|
|
29
|
+
xml.Bucket do
|
30
|
+
xml.tag!(:Name, bucket.split(':').last)
|
31
|
+
xml.tag!(:CreationDate, Time.at(AWS_REDIS.hget(bucket, 'created_at').to_i).xmlschema)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
content_type :xml
|
38
|
+
xml.target!
|
39
|
+
end
|
40
|
+
|
41
|
+
def list_objects(bucket)
|
42
|
+
xml = Builder::XmlMarkup.new
|
43
|
+
xml.instruct!
|
44
|
+
xml.ListBucketResult(xmlns: 'http://doc.s3.amazonaws.com/2006-03-01') do
|
45
|
+
objects = AWS_REDIS.keys "s3:bucket:#{bucket}:*"
|
46
|
+
xml.tag!(:Name, bucket)
|
47
|
+
xml.tag!(:KeyCount, objects.length)
|
48
|
+
xml.tag!(:Prefix, nil)
|
49
|
+
xml.tag!(:Marker, nil)
|
50
|
+
xml.tag!(:MaxKeys, 1000)
|
51
|
+
xml.tag!(:IsTruncated, false)
|
52
|
+
|
53
|
+
objects.each do |object|
|
54
|
+
prefix = params.key?('prefix') ? params['prefix'] : nil
|
55
|
+
next unless prefix.nil? || \
|
56
|
+
object.start_with?("s3:bucket:#{bucket}:#{prefix}")
|
57
|
+
|
58
|
+
xml.Contents do
|
59
|
+
key = AWS_REDIS.hget object, 'key'
|
60
|
+
last_modified = AWS_REDIS.hget object, 'last_modified'
|
61
|
+
etag = AWS_REDIS.hget object, 'etag'
|
62
|
+
size = AWS_REDIS.hget object, 'size'
|
63
|
+
|
64
|
+
xml.tag!(:Key, key)
|
65
|
+
xml.tag!(:LastModified, Time.at(last_modified.to_i).xmlschema)
|
66
|
+
xml.tag!(:ETag, etag)
|
67
|
+
xml.tag!(:Size, size)
|
68
|
+
xml.tag!(:Storage, 'STANDARD')
|
69
|
+
xml.Owner do
|
70
|
+
xml.tag!(:ID, SecureRandom.hex(10))
|
71
|
+
xml.tag!(:DisplayName, 'fake@example.com')
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
content_type :xml
|
78
|
+
xml.target!
|
79
|
+
end
|
80
|
+
|
81
|
+
def objectNotFound
|
82
|
+
xml = Builder::XmlMarkup.new
|
83
|
+
xml.instruct!
|
84
|
+
xml.Error do
|
85
|
+
xml.tag!(:Code, 'NoSuchKey')
|
86
|
+
xml.tag!(:Message, 'The specified key does not exist.')
|
87
|
+
end
|
88
|
+
content_type :xml
|
89
|
+
xml.target!
|
90
|
+
end
|
91
|
+
|
92
|
+
def download_file(bucket, obj_name)
|
93
|
+
obj_key = "s3:bucket:#{bucket}:#{obj_name}"
|
94
|
+
(AWS_REDIS.hget obj_key, 'body').force_encoding('UTF-8')
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# Get all of the buckets
|
99
|
+
# Host: s3[.-][us-mockregion-1].amazonaws.com
|
100
|
+
get %r{/s3.*?\.amazonaws\.com/?} do
|
101
|
+
list_buckets
|
102
|
+
|
103
|
+
status 200
|
104
|
+
end
|
105
|
+
|
106
|
+
# Get bucket
|
107
|
+
# Host: bucket-name.s3[.-][us-mockregion-1].amazonaws.com
|
108
|
+
get %r{/(.*?)\.(s3.*?\.amazonaws\.com)/?$} do
|
109
|
+
bucket = params[:captures][0]
|
110
|
+
halt 404 unless AWS_REDIS.exists "s3:bucket:#{bucket}"
|
111
|
+
list_objects(bucket)
|
112
|
+
end
|
113
|
+
|
114
|
+
# Get a file a bucket
|
115
|
+
# Host: bucket-name.s3[.-][us-mockregion-1].amazonaws.com/file-name
|
116
|
+
get %r{/(.*?)\.(s3.*?\.amazonaws\.com)?/(.+)} do
|
117
|
+
bucket = params[:captures][0]
|
118
|
+
file_name = params[:captures][2]
|
119
|
+
|
120
|
+
get_file(bucket: bucket, file_name: file_name)
|
121
|
+
end
|
122
|
+
|
123
|
+
# Old-style way of referencing S3
|
124
|
+
get %r{/s3/([^/]+)?/(.+)} do
|
125
|
+
bucket = params[:captures][0]
|
126
|
+
file_name = params[:captures][1]
|
127
|
+
|
128
|
+
get_file(bucket: bucket, file_name: file_name)
|
129
|
+
end
|
130
|
+
|
131
|
+
# Bucket creation
|
132
|
+
# Host: bucket-name.s3[.-][us-mockregion-1].amazonaws.com
|
133
|
+
put %r{/(.*?)\.(s3\.?.*?\.amazonaws\.com)/?$} do
|
134
|
+
bucket = params[:captures][0]
|
135
|
+
AWS_REDIS.hset "s3:bucket:#{bucket}", 'created_at', Time.now.to_i
|
136
|
+
|
137
|
+
status 200
|
138
|
+
end
|
139
|
+
|
140
|
+
# Upload file into a bucket
|
141
|
+
# Host: bucket-name.s3[.-][us-mockregion-1].amazonaws.com/file-name
|
142
|
+
put %r{/(.*?)\.s3\.(.*?\.amazonaws\.com)?/(.+)} do
|
143
|
+
bucket = params[:captures][0]
|
144
|
+
file_name = params[:captures][2]
|
145
|
+
|
146
|
+
# upload the file (chunking not implemented) to fake S3
|
147
|
+
if file_name && bucket
|
148
|
+
file_name = file_name[1..-1] if '/' == file_name[0]
|
149
|
+
clientside_aws_testing = \
|
150
|
+
defined?(Sinatra::Base.settings.clientside_aws_testing) && \
|
151
|
+
Sinatra::Base.settings.clientside_aws_testing
|
152
|
+
body_send = clientside_aws_testing ? params[:body] : request.body.read
|
153
|
+
|
154
|
+
# Handle the copy_XXX case
|
155
|
+
if body_send.nil? || body_send.empty?
|
156
|
+
%w(HTTP_X_AMZ_COPY_SOURCE x-amz-copy-source X-Amz-Copy-Source).each do |copy_source_key|
|
157
|
+
next unless env.key?(copy_source_key)
|
158
|
+
copy_source = env[copy_source_key]
|
159
|
+
if copy_source.start_with?('/')
|
160
|
+
(_extra, srcbucket, srcfile) = copy_source.split('/')
|
161
|
+
else
|
162
|
+
(srcbucket, srcfile) = copy_source.split('/')
|
163
|
+
end
|
164
|
+
body_send = download_file(srcbucket, srcfile)
|
165
|
+
break
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
if AWS_REDIS.hget("s3:bucket:#{bucket}:#{file_name}", 'size').to_i > 0 &&
|
170
|
+
(body_send.nil? || body_send.empty?) &&
|
171
|
+
env.key?('HTTP_X_AMZ_ACL')
|
172
|
+
# you are setting the ACL only on a file that already seems to exist
|
173
|
+
# Do not update the body
|
174
|
+
else
|
175
|
+
AWS_REDIS.hset "s3:bucket:#{bucket}:#{file_name}", 'body', body_send
|
176
|
+
AWS_REDIS.hset "s3:bucket:#{bucket}:#{file_name}", 'size', body_send.length
|
177
|
+
end
|
178
|
+
|
179
|
+
AWS_REDIS.hset "s3:bucket:#{bucket}:#{file_name}", 'key', file_name
|
180
|
+
AWS_REDIS.hset "s3:bucket:#{bucket}:#{file_name}", 'last_modified', Time.now.to_i
|
181
|
+
AWS_REDIS.hset "s3:bucket:#{bucket}:#{file_name}", 'etag', Digest::MD5.hexdigest(body_send)
|
182
|
+
AWS_REDIS.hset "s3:bucket:#{bucket}:#{file_name}", 'acl', env['HTTP_X_AMZ_ACL']
|
183
|
+
|
184
|
+
%w(content-type Content-Type CONTENT_TYPE).each do |content_type_key|
|
185
|
+
next unless env.key?(content_type_key)
|
186
|
+
AWS_REDIS.hset "s3:bucket:#{bucket}:#{file_name}",
|
187
|
+
'content-type',
|
188
|
+
env[content_type_key]
|
189
|
+
break
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
status 200
|
194
|
+
end
|
195
|
+
|
196
|
+
# Delete file from a bucket
|
197
|
+
# Host: bucket-name.s3[.-][us-mockregion-1].amazonaws.com/file-name
|
198
|
+
delete %r{/(.*?)\.s3\.(.*?\.amazonaws\.com)?/(.+)} do
|
199
|
+
bucket = params[:captures][0]
|
200
|
+
file_name = params[:captures][2]
|
201
|
+
AWS_REDIS.del "s3:bucket:#{bucket}:#{file_name}"
|
202
|
+
|
203
|
+
status 200
|
204
|
+
end
|
205
|
+
|
206
|
+
# post %r{/s3(.*?\.amazonaws\.com)?/([^/]+)/?} do
|
207
|
+
# # upload the file (chunking not implemented) to fake S3
|
208
|
+
# bucket = params[:captures][1]
|
209
|
+
# file_name = params[:key]
|
210
|
+
# if file_name
|
211
|
+
# file_name = file_name[1..-1] if file_name.start_with? '/'
|
212
|
+
# body_send = params[:file]
|
213
|
+
# body_send = body_send[:tempfile].read unless AWS::Core.testing
|
214
|
+
#
|
215
|
+
# AWS_REDIS.hset "s3:bucket:#{bucket}:#{file_name}", 'body', body_send
|
216
|
+
# if env.key?('content-type')
|
217
|
+
# AWS_REDIS.hset "s3:bucket:#{bucket}:#{file_name}", 'content-type', env['content-type']
|
218
|
+
# elsif env.key?('CONTENT_TYPE')
|
219
|
+
# AWS_REDIS.hset "s3:bucket:#{bucket}:#{file_name}", 'content-type', env['CONTENT_TYPE']
|
220
|
+
# end
|
221
|
+
# end
|
222
|
+
# status 200
|
223
|
+
# end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
helpers do
|
2
|
+
def create_platform_endpoint(platform_application_arn:)
|
3
|
+
xml = Builder::XmlMarkup.new
|
4
|
+
xml.instruct!
|
5
|
+
|
6
|
+
xml.CreatePlatformEndpointResponse do
|
7
|
+
xml.CreatePlatformEndpointResult do
|
8
|
+
platform = if platform_application_arn =~ %r{app/APNS}
|
9
|
+
'endpoint/APNS/'
|
10
|
+
elsif platform_application_arn =~ %r{app/WNS}
|
11
|
+
'endpoint/WNS/'
|
12
|
+
elsif platform_application_arn =~ %r{app/GCM}
|
13
|
+
'endpoint/GCM/'
|
14
|
+
else
|
15
|
+
'endpoint/UNKNOWN/'
|
16
|
+
end
|
17
|
+
|
18
|
+
xml.tag!(:EndpointArn,
|
19
|
+
'arn:aws:sns:us-east-1:999999999999:' \
|
20
|
+
"#{platform}MYAPP/#{SecureRandom.hex(10)}")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
content_type :xml
|
25
|
+
xml.target!
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
get %r{/sns\.(\w+?)\.amazonaws\.com/?(.*)} do
|
30
|
+
200
|
31
|
+
end
|
32
|
+
|
33
|
+
post %r{/sns(\.(\w+?)\.amazonaws\.com)?/?(.*)} do
|
34
|
+
case params[:Action]
|
35
|
+
when 'CreatePlatformEndpoint'
|
36
|
+
create_platform_endpoint(platform_application_arn:
|
37
|
+
params['PlatformApplicationArn'])
|
38
|
+
else
|
39
|
+
200
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,233 @@
|
|
1
|
+
SQS_DEFAULT_VISIBILITY_TIMEOUT = \
|
2
|
+
(ENV['SQS_VISIBILITY_TIMEOUT'] || 60 * 60 * 6).to_i
|
3
|
+
|
4
|
+
helpers do
|
5
|
+
def get_queue_url
|
6
|
+
queue_name = params[:QueueName]
|
7
|
+
|
8
|
+
xml = Builder::XmlMarkup.new
|
9
|
+
xml.instruct!
|
10
|
+
|
11
|
+
xml.GetQueueUrlResponse do
|
12
|
+
xml.GetQueueUrlResult do
|
13
|
+
xml.tag!(:QueueUrl, queue_name)
|
14
|
+
end
|
15
|
+
xml.ResponseMetadata do
|
16
|
+
xml.tag!(:RequestId, SecureRandom.hex(10))
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
content_type :xml
|
21
|
+
xml.target!
|
22
|
+
end
|
23
|
+
|
24
|
+
def get_queue_attributes
|
25
|
+
queue = params[:QueueUrl]
|
26
|
+
|
27
|
+
xml = Builder::XmlMarkup.new
|
28
|
+
xml.instruct!
|
29
|
+
xml.GetQueueAttributesResponse do
|
30
|
+
xml.GetQueueAttributesResult do
|
31
|
+
xml.Attribute do
|
32
|
+
xml.tag!(:Name, 'ApproximateNumberOfMessages')
|
33
|
+
xml.tag!(:Value, (AWS_REDIS.llen queue))
|
34
|
+
end
|
35
|
+
xml.Attribute do
|
36
|
+
xml.tag!(:Name, 'ApproximateNumberOfMessagesNotVisible')
|
37
|
+
xml.tag!(:Value, (AWS_REDIS.keys 'sqs:pending:*').length)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
xml.ResponseMetadata do
|
41
|
+
xml.tag!(:RequestId, SecureRandom.hex(10))
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
content_type :xml
|
46
|
+
xml.target!
|
47
|
+
end
|
48
|
+
|
49
|
+
def delete_message
|
50
|
+
AWS_REDIS.del("sqs:pending:#{params['ReceiptHandle']}")
|
51
|
+
200
|
52
|
+
end
|
53
|
+
|
54
|
+
def delete_message_batch
|
55
|
+
deleted_messages = []
|
56
|
+
keys = params.keys.select { |k| k =~ /DeleteMessageBatchRequestEntry/ }
|
57
|
+
keys.each do |k|
|
58
|
+
AWS_REDIS.del("sqs:pending:#{params[k]}") if k =~ /ReceiptHandle$/
|
59
|
+
deleted_messages << params[k] if k =~ /Id$/
|
60
|
+
end
|
61
|
+
|
62
|
+
xml = Builder::XmlMarkup.new
|
63
|
+
xml.instruct!
|
64
|
+
xml.DeleteMessageBatchResponse do
|
65
|
+
xml.DeleteMessageBatchResult do
|
66
|
+
deleted_messages.each do |msg_id|
|
67
|
+
xml.DeleteMessageBatchResultEntry do
|
68
|
+
xml.tag!(:Id, msg_id)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
xml.ResponseMetadata do
|
73
|
+
xml.tag!(:RequestId, SecureRandom.hex(10))
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
content_type :xml
|
78
|
+
xml.target!
|
79
|
+
end
|
80
|
+
|
81
|
+
def receive_message
|
82
|
+
queue = params[:QueueUrl]
|
83
|
+
max_messages = params[:MaxNumberOfMessages].to_i
|
84
|
+
max_messages = 1 if max_messages.zero?
|
85
|
+
|
86
|
+
xml = Builder::XmlMarkup.new
|
87
|
+
xml.instruct!
|
88
|
+
|
89
|
+
if AWS_REDIS.llen(queue).zero?
|
90
|
+
xml.ReceiveMessageResponse do
|
91
|
+
xml.ReceiveMessageResult do
|
92
|
+
end
|
93
|
+
xml.ResponseMetadata do
|
94
|
+
xml.tag!(:RequestId, SecureRandom.hex(10))
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
return xml.target!
|
99
|
+
end
|
100
|
+
|
101
|
+
results_json = []
|
102
|
+
max_messages.times do
|
103
|
+
raw_message = AWS_REDIS.rpop queue
|
104
|
+
results_json << JSON.parse(raw_message.force_encoding('UTF-8'))
|
105
|
+
break if (AWS_REDIS.llen queue).zero?
|
106
|
+
end
|
107
|
+
|
108
|
+
xml.ReceiveMessageResponse do
|
109
|
+
xml.ReceiveMessageResult do
|
110
|
+
results_json.each do |result|
|
111
|
+
xml.Message do
|
112
|
+
xml.tag!(:MessageId, result['MessageId'])
|
113
|
+
xml.tag!(:ReceiptHandle, result['ReceiptHandle'])
|
114
|
+
xml.tag!(:MD5OfBody, Digest::MD5.hexdigest(result['MessageBody']))
|
115
|
+
xml.tag!(:Body, result['MessageBody'])
|
116
|
+
xml.Attribute do
|
117
|
+
xml.tag!(:Name, 'SenderId')
|
118
|
+
xml.tag!(:Value, '1')
|
119
|
+
end
|
120
|
+
xml.Attribute do
|
121
|
+
xml.tag!(:Name, 'SentTimestamp')
|
122
|
+
xml.tag!(:Value, result['Timestamp'])
|
123
|
+
end
|
124
|
+
xml.Attribute do
|
125
|
+
xml.tag!(:Name, 'ReceiptHandle')
|
126
|
+
xml.tag!(:Value, result['Timestamp'])
|
127
|
+
end
|
128
|
+
xml.Attribute do
|
129
|
+
xml.tag!(:Name, 'ApproximateReceiveCount')
|
130
|
+
xml.tag!(:Value, '1')
|
131
|
+
end
|
132
|
+
xml.Attribute do
|
133
|
+
xml.tag!(:Name, 'ApproximateFirstReceiveTimestamp')
|
134
|
+
xml.tag!(:Value, result['Timestamp'] * 1000)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
redis_key = "sqs:pending:#{result['ReceiptHandle']}"
|
138
|
+
payload = { queue: queue, message: result, received: Time.now }
|
139
|
+
AWS_REDIS.set(redis_key, payload.to_json)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
xml.ResponseMetadata do
|
143
|
+
xml.tag!(:RequestId, results_json.first['RequestId'])
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
content_type :xml
|
148
|
+
xml.target!
|
149
|
+
end
|
150
|
+
|
151
|
+
def send_message_batch
|
152
|
+
queue = params[:QueueUrl]
|
153
|
+
idx = 1
|
154
|
+
loop do
|
155
|
+
break unless params["SendMessageBatchRequestEntry.#{idx}.MessageBody"]
|
156
|
+
send_message(queue, params["SendMessageBatchRequestEntry.#{idx}.MessageBody"])
|
157
|
+
idx += 1
|
158
|
+
end
|
159
|
+
|
160
|
+
halt 200
|
161
|
+
end
|
162
|
+
|
163
|
+
def send_message(queue, message_body)
|
164
|
+
message_id = SecureRandom.hex(10)
|
165
|
+
request_id = SecureRandom.hex(10)
|
166
|
+
msg = {
|
167
|
+
MessageBody: message_body,
|
168
|
+
MessageId: message_id,
|
169
|
+
RequestId: request_id,
|
170
|
+
Timestamp: Time.now.to_i,
|
171
|
+
ReceiptHandle: Base64.encode64(message_id).strip
|
172
|
+
}
|
173
|
+
AWS_REDIS.lpush(queue, msg.to_json)
|
174
|
+
|
175
|
+
xml = Builder::XmlMarkup.new
|
176
|
+
xml.instruct!
|
177
|
+
xml.SendMessageResponse do
|
178
|
+
xml.SendMessageResult do
|
179
|
+
xml.tag!(:MD5OfMessageBody, Digest::MD5.hexdigest(message_body))
|
180
|
+
xml.tag!(:MessageId, message_id)
|
181
|
+
end
|
182
|
+
xml.ResponseMetadata do
|
183
|
+
xml.tag!(:RequestId, request_id)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
content_type :xml
|
188
|
+
xml.target!
|
189
|
+
end
|
190
|
+
|
191
|
+
def __check_pending
|
192
|
+
AWS_REDIS.keys('sqs:pending:*').each do |key|
|
193
|
+
json = AWS_REDIS.get(key).to_s.force_encoding("UTF-8")
|
194
|
+
begin
|
195
|
+
payload = JSON.parse(json)
|
196
|
+
time_received = Time.parse(payload['received'])
|
197
|
+
since_received = Time.now - time_received
|
198
|
+
|
199
|
+
if since_received > SQS_DEFAULT_VISIBILITY_TIMEOUT
|
200
|
+
AWS_REDIS.lpush(payload['queue'], payload['message'].to_json)
|
201
|
+
AWS_REDIS.del(key)
|
202
|
+
end
|
203
|
+
rescue => e
|
204
|
+
puts "INVALID PENDING PAYLOAD: #{e.message} #{json} #{e.backtrace}"
|
205
|
+
STDOUT.flush
|
206
|
+
AWS_REDIS.del(key)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
post %r{/sqs(\.(\w+?)\.amazonaws\.com)?/?(.*)} do
|
213
|
+
case params[:Action]
|
214
|
+
when 'SendMessage'
|
215
|
+
send_message(params[:QueueUrl], params[:MessageBody])
|
216
|
+
when 'SendMessageBatch'
|
217
|
+
send_message_batch
|
218
|
+
when 'ReceiveMessage'
|
219
|
+
__check_pending
|
220
|
+
receive_message
|
221
|
+
when 'DeleteMessage'
|
222
|
+
delete_message
|
223
|
+
when 'DeleteMessageBatch'
|
224
|
+
delete_message_batch
|
225
|
+
when 'GetQueueAttributes'
|
226
|
+
__check_pending
|
227
|
+
get_queue_attributes
|
228
|
+
when 'GetQueueUrl'
|
229
|
+
get_queue_url
|
230
|
+
else
|
231
|
+
halt 500, "Unknown action #{params.inspect}"
|
232
|
+
end
|
233
|
+
end
|