- require 'cgi'
- require 'openssl'
- require 'net/https'
- require 'rubygems'
- require 'activesupport'
- module AWS
- class API
- def initialize(access_key, secret_key, uri='/')
- prefix = self.class.name.split('::').last.downcase!
- @host = "#{prefix}.amazonaws.com"
- @access_key = access_key
- @secret_key = secret_key
- @uri = uri
- @signature_version = '2'
- @signature_method = 'HmacSHA256'
- end
- def method_missing(symbol, args={})
- send_request(format_method(symbol.to_s), args)
- end
- private
- def format_method(string)
- string.gsub!(/^[a-z]|_+[a-z]/) { |a| a.upcase }
- string.gsub!(/_/, '')
- end
- def format_params(params)
- param_array = Array.new
- params.sort_by { |k, v| k}.each do |param|
- param_array << "#{CGI.escape(param[0]).gsub('+', '%20')}=#{CGI.escape(param[1]).gsub('+', '%20')}"
- end
- flat_params = "#{param_array.join('&')}"
- # make the proper string to sign
- string_to_sign = "#{@request_method}\n#{@host}\n#{@uri}\n#{flat_params}"
- digest = OpenSSL::Digest::Digest.new('sha256')
- b64_hmac = [OpenSSL::HMAC.digest(digest, @secret_key, string_to_sign)].pack("m").strip
- signature = CGI.escape(b64_hmac)
- "#{flat_params}&Signature=#{signature}"
- end
- def add_default_params
- {
- 'Timestamp' => Time.now.utc.strftime("%Y-%m-%dT%H:%M:%SZ"),
- 'AWSAccessKeyId' => @access_key,
- 'Version' => @api_version,
- 'SignatureVersion' => @signature_version,
- 'SignatureMethod' => @signature_method
- }
- end
- def send_request(method, params={}, request_method=:get)
- @request_method = 'GET' if request_method == :get # TODO: add POST support
- params.merge! add_default_params
- params['Action'] = method
- canonical_string = format_params(params)
- http = Net::HTTP.new(@host, 443)
- http.use_ssl = true
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
- response, xml_data = http.get("#{@uri}?#{canonical_string}")
- data = Hash.from_xml(xml_data)
- case response
- when Net::HTTPOK
- data
- else
- raise Error.new(data)
- end
- end
- end
- class Error < Exception
- attr_reader :message
- def initialize(data)
- @message = "#{data['Response']['Errors']['Error']['Code']}: #{data['Response']['Errors']['Error']['Message']}"
- end
- end
- class EC2 < AWS::API
- def initialize(access_key, secret_key)
- @api_version = '2010-11-15'
- super
- end
- end
- class IAM < AWS::API
- def initalize(access_key, secret_key)
- @api_version = '2010-05-08'
- super
- end
- end
- end