clientside_aws 0.0.17
Sign up to get free protection for your applications and to get access to all the features.
- 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
|