Skip to content

Instantly share code, notes, and snippets.

@ssimeonov
Created April 4, 2011 01:33
Show Gist options
  • Save ssimeonov/901024 to your computer and use it in GitHub Desktop.
Save ssimeonov/901024 to your computer and use it in GitHub Desktop.
This is a simple way to post a message to Amazon SQS w/o any gems (the queue must already exist)
module Amazon
module Authentication
SIGNATURE_VERSION = "2"
@@digest = OpenSSL::Digest::Digest.new("sha256")
def sign(auth_string)
Base64.encode64(OpenSSL::HMAC.digest(digester, aws_secret_access_key, auth_string)).strip
end
def digester
@@digest
end
def aws_access_key_id
@config[:access_key]
end
def aws_secret_access_key
@config[:secret_key]
end
def amz_escape(param)
param.to_s.gsub(/([^a-zA-Z0-9._~-]+)/n) do
'%' + $1.unpack('H2' * $1.size).join('%').upcase
end
end
def signed_parameters(hash, verb, host, path)
data = hash.keys.sort.map do |key|
"#{amz_escape(key)}=#{amz_escape(hash[key])}"
end.join('&')
sig = amz_escape(sign("#{verb}\n#{host}\n#{path}\n#{data}"))
"#{data}&Signature=#{sig}"
end
def generate_request_hash(action, params={})
request_hash = {
"Action" => action,
"SignatureMethod" => 'HmacSHA256',
"AWSAccessKeyId" => aws_access_key_id,
"SignatureVersion" => SIGNATURE_VERSION,
}
request_hash.merge(default_parameters).merge(params)
end
end
end
# This version of VanillaSQS uses modified source from Qanat by Mike Perham
# @see https://github.com/mperham/qanat
module VanillaSQS
DEFAULT_HOST = URI.parse("http://queue.amazonaws.com/")
API_VERSION = "2009-02-01"
class << self
attr_accessor :log
end
# Default logging
@log = Logger.new(STDERR)
@log.level = Logger::DEBUG
class Queue
REQUEST_TTL = 600 # 10mins
include Amazon::Authentication
attr_reader :name
def initialize(name, config)
@name = name
@config = config
@config[:timeout] ||= 10
@uri = URI.parse(url_for(name))
end
def push(msg)
request_hash = generate_request_hash("SendMessage", 'MessageBody' => msg)
http = send_request(:get, @uri, request_hash)
code = http.code.to_i
if code == 200
true
else
VanillaSQS.log.error "SQS send_message returned an error response: #{code} #{http}"
false
end
end
private
def create(name)
request_hash = generate_request_hash("CreateQueue", 'QueueName' => name)
http = send_request(:post, DEFAULT_HOST, request_hash)
code = http.code.to_i
if code == 200
true
else
VanillaSQS.log.error "SQS send_message returned an error response: #{code} #{http}"
false
end
end
def url_for(name, recur=false)
raise ArgumentError, "No queue given" if !name || name.strip == ''
request_hash = generate_request_hash("ListQueues", 'QueueNamePrefix' => name)
http = send_request(:get, DEFAULT_HOST, request_hash)
code = http.code.to_i
if code == 200
doc = REXML::Document.new(http.body)
tag = REXML::XPath.first(doc, "//QueueUrl")
if !tag
if !recur
create(name)
return url_for(name, true)
else
raise ArgumentError, "Unable to create queue '#{name}'"
end
end
url = tag.text
VanillaSQS.log.info "Queue #{name} at #{url}"
return url
end
end
def send_request(method, uri, parameters)
data = signed_parameters(parameters, method.to_s.upcase, uri.host, uri.path)
case method
when :get
req = Net::HTTP::Get.new("#{uri.path || '/'}?#{data}")
Timeout.timeout(timeout) do
Net::HTTP.start(uri.host, uri.port) do |http|
http.request(req)
end
end
when :post
req = Net::HTTP::Post.new(uri.path || '/')
req.set_form_data(data)
Timeout.timeout(timeout) do
Net::HTTP.start(uri.host, uri.port) do |http|
http.request(req)
end
end
else
raise ArgumentError, "Unsupported HTTP method: #{method}"
end
end
def default_parameters
request_hash = { "Expires" => (Time.now + REQUEST_TTL).utc.strftime("%Y-%m-%dT%H:%M:%SZ"),
"Version" => API_VERSION }
end
def timeout
Integer(@config[:timeout])
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment