require 'json' require 'openssl' require 'net/http' require 'net/https' require 'time' require 'stringio' require 'zlib' require 'uri' require 'ddbcli/ddb-error' require 'ddbcli/ddb-endpoint' module DynamoDB class Client SERVICE_NAME = 'dynamodb' API_VERSION = '2012-08-10' USER_AGENT = "ddbcli/#{DynamoDB::VERSION}" DEFAULT_TIMEOUT = 60 attr_reader :region attr_accessor :timeout attr_accessor :retry_num attr_accessor :retry_intvl attr_accessor :debug def initialize(accessKeyId, secretAccessKey, endpoint_or_region) @accessKeyId = accessKeyId @secretAccessKey = secretAccessKey set_endpoint_and_region(endpoint_or_region) @timeout = DEFAULT_TIMEOUT @debug = false @retry_num = 3 @retry_intvl = 10 end def set_endpoint_and_region(endpoint_or_region) if endpoint_or_region.kind_of?(URI) @endpoint = endpoint_or_region aws_endpoint, region = DynamoDB::Endpoint::ENDPOINTS.find do |k, v| @endpoint.host[k] end if region @region = region else @region = [@endpoint.host, @endpoint.port].join(':') end else host, @region = DynamoDB::Endpoint.endpoint_and_region(endpoint_or_region) @endpoint = URI.parse("https://#{host}") end end def query(action, hash) retry_query do query0(action, hash) end end def query0(action, hash) if @debug $stderr.puts(< 'application/x-amz-json-1.0', 'X-Amz-Target' => "DynamoDB_#{API_VERSION.gsub('-', '')}.#{action}", 'Content-Length' => req_body.length.to_s, 'User-Agent' => USER_AGENT, 'Host' => @endpoint.host, 'X-Amz-Date' => iso8601(date), 'X-Amz-Content-Sha256' => hexhash(req_body), 'Accept' => '*/*', 'Accept-Encoding' => 'gzip', } headers['Authorization'] = authorization(date, headers, req_body) Net::HTTP.version_1_2 http = Net::HTTP.new(@endpoint.host, @endpoint.port) if @endpoint.scheme == 'https' http.use_ssl = true http.verify_mode = OpenSSL::SSL::VERIFY_NONE end http.open_timeout = @timeout http.read_timeout = @timeout res_code = nil res_msg = nil res_body = http.start do |w| req = Net::HTTP::Post.new('/', headers) req.body = req_body res = w.request(req) res_code = res.code.to_i res_msg = res.message if res['Content-Encoding'] == 'gzip' StringIO.open(res.body, 'rb') do |f| Zlib::GzipReader.wrap(f).read end else res.body end end res_data = JSON.parse(res_body) if @debug $stderr.puts(< e raise e if i >= @retry_num rescue DynamoDB::Error => e if [/\bServiceUnavailable\b/i, /\bexceeded\b/i].any? {|i| i =~ e.message } raise e if i >= @retry_num else raise e end rescue Timeout::Error => e raise e if i >= @retry_num end wait_sec = @retry_intvl * (i + 1) if @debug $stderr.puts("Retry... (wait %d seconds)" % wait_sec) end sleep wait_sec end return retval end end # Client end # DynamoDB