lib/aliyun/common.rb

Summary

Maintainability
A
0 mins
Test Coverage
require 'net/http'
require 'time'
require 'securerandom'
require 'uri'
require 'base64'
require 'hmac-sha1'
require 'json'

module Aliyun
  class Service
    attr_accessor :access_key_id, :access_key_secret
    attr_accessor :options
    attr_accessor :endpoint_url
    attr_accessor :service

    def initialize(options={})
      options[:service] ||= :ecs
      self.service = SERVICES[options[:service].to_sym]
      self.access_key_id = options[:access_key_id]
      self.access_key_secret = options[:access_key_secret]
      self.endpoint_url = options[:endpoint_url] || self.service.endpoint
      self.options = {:AccessKeyId => self.access_key_id}
    end

    def method_missing(method, *args)
      if $DEBUG
        puts "Not Found Method: #{method}"
      end

      if args[0].nil?
        raise AliyunAPIException.new "Method missing: #{method}!"
      end

      call_aliyun_with_parameter(method, args[0])
    end

    #Dispatch the request with parameter
    private
    def call_aliyun_with_parameter(method, params)
      params = gen_request_parameters method, params
      uri = URI(endpoint_url)

      uri.query = URI.encode_www_form(params)

      http = Net::HTTP.new(uri.host, uri.port)
      http.use_ssl = (uri.scheme == "https")

      http.verify_mode = OpenSSL::SSL::VERIFY_NONE
      if $DEBUG
        puts "Request URI: #{uri.request_uri}"
      end

      request = Net::HTTP::Get.new(uri.request_uri)
      response = http.request(request)

      case response
      when Net::HTTPSuccess
        return JSON.parse(response.body)
      else
        raise AliyunAPIException.new "Response code: #{response.code}, message: #{response.body}"
      end
    end

    #generate the parameters
    def gen_request_parameters method, params
      #add common parameters
      params.merge! self.service.default_parameters

      params.merge! self.options

      params[:Action] = method.to_s
      params[:Timestamp] = Time.now.utc.iso8601
      params[:SignatureNonce] = SecureRandom.uuid
      params[:Signature] = compute_signature params

      params
    end

    #compute the signature of the parameters String
    def compute_signature params
      if $DEBUG
        puts "keys before sorted: #{params.keys}"
      end

      sorted_keys = params.keys.sort

      if $DEBUG
        puts "keys after sorted: #{sorted_keys}"
      end

      canonicalized_query_string = ""

      canonicalized_query_string = sorted_keys.map {|key|
        "%s=%s" % [safe_encode(key.to_s), safe_encode(params[key])]
      }.join(self.service.separator)

      length = canonicalized_query_string.length

      string_to_sign = self.service.http_method + self.service.separator + safe_encode('/') + self.service.separator + safe_encode(canonicalized_query_string)

      if $DEBUG
        puts "string_to_sign is #{string_to_sign}"
      end

      signature = calculate_signature access_key_secret+"&", string_to_sign
    end

    #calculate the signature
    def calculate_signature key, string_to_sign
      hmac = HMAC::SHA1.new(key)
      hmac.update(string_to_sign)
      signature = Base64.encode64(hmac.digest).gsub("\n", '')
      if $DEBUG
        puts "Signature #{signature}"
      end
      signature
    end

    #encode the value to aliyun's requirement
    def safe_encode value
      value = URI.encode_www_form_component(value).gsub(/\+/,'%20').gsub(/\*/,'%2A').gsub(/%7E/,'~')
    end
  end
end