class Cubits::Connection

Constants

CONTENT_TYPE

Public Class Methods

new(params) click to toggle source

Creates a new Connection object

@param params [Hash] @param params [String] @param params [String]

# File lib/cubits/connection.rb, line 17
def initialize(params)
  fail ArgumentError, 'String is expected as :key' unless params[:key].is_a?(String)
  fail ArgumentError, 'String is expected as :secret' unless params[:secret].is_a?(String)
  @key = params[:key]
  @secret = params[:secret]
  @params = params.dup
end

Private Class Methods

nonce() click to toggle source

Returns timestamp based nonce

@return [Integer]

# File lib/cubits/connection.rb, line 106
def self.nonce
  (Time.now.to_f * 1000).to_i
end

Public Instance Methods

get(path, data = {}) click to toggle source

Executes a GET request

# File lib/cubits/connection.rb, line 27
def get(path, data = {})
  fail ArgumentError, 'Hash is expected as request data' unless data.is_a?(Hash)
  encoded_data = URI.encode_www_form(data)
  request(:get, path, encoded_data)
end
post(path, data = {}) click to toggle source

Executes a POST request

# File lib/cubits/connection.rb, line 35
def post(path, data = {})
  fail ArgumentError, 'Hash is expected as request data' unless data.is_a?(Hash)
  encoded_data = data.to_json
  request(:post, path, encoded_data)
end
sign_message(msg) click to toggle source

Signs the message with preconfigured secret

@param msg [String]

@return [String] Calculated signature

# File lib/cubits/connection.rb, line 47
def sign_message(msg)
  OpenSSL::HMAC.hexdigest('sha512', @secret, msg)
end

Private Instance Methods

cubits_headers(path, encoded_data) click to toggle source

Returns complete set of cubits headers

# File lib/cubits/connection.rb, line 112
def cubits_headers(path, encoded_data)
  nonce = self.class.nonce
  signature = sign_request(path, nonce, encoded_data)

  {
    'CUBITS_KEY' => @key,
    'CUBITS_NONCE' => nonce,
    'CUBITS_SIGNATURE' => signature,
    'Accept' => CONTENT_TYPE
  }
end
insecure?() click to toggle source

Returns true if an insecure connection is requested (do NOT use in production)

# File lib/cubits/connection.rb, line 152
def insecure?
  @insecure ||= @params[:insecure] || Cubits.base_url.scheme != 'https'
end
request(method, path, encoded_data) click to toggle source

Sends a request to the API

# File lib/cubits/connection.rb, line 55
def request(method, path, encoded_data)
  Cubits.logger.warn 'Connecting to Cubits using insecure connection!' if insecure?
  url = URI.join(Cubits.base_url, path)
  params = {}
  http = HTTP.with(cubits_headers(path, encoded_data))
  if method == :get
    url.query = encoded_data unless encoded_data.empty?
  else
    http = http.with('Content-Type' => CONTENT_TYPE)
    params[:body] = encoded_data
  end
  params[:ssl_context] = ssl_context unless insecure?
  Cubits.logger.debug "> #{method.to_s.upcase}: #{url}"
  response = http.send(method, url, params)
  Cubits.logger.debug "< #{response.code} #{response.reason}"
  begin
    body = JSON.parse(response.body)
  rescue JSON::ParserError => e
    raise ConnectionError, "Failed to parse response: #{e}"
  end
  return body if (200...300).include?(response.status)
  respond_with_error(response.code, body['message'])
end
respond_with_error(status, message) click to toggle source

Map unsuccessful HTTP response to appropriate exception and raise it

# File lib/cubits/connection.rb, line 81
def respond_with_error(status, message)
  case status
  when 400
    fail BadRequest, message
  when 403
    fail Forbidden, message
  when 404
    fail NotFound, message
  when 415
    fail UnsupportedMediaType, message
  when 500
    fail InternalServerError, message
  when 400...500
    fail ClientError, message
  when 500...600
    fail ServerError, message
  else
    fail ConnectionError, message
  end
end
sign_request(path, nonce, request_data) click to toggle source

Calculates request signature

@param path [String] @param nonce [Integer,String] @param request_data [String]

# File lib/cubits/connection.rb, line 130
def sign_request(path, nonce, request_data)
  msg = path + nonce.to_s + OpenSSL::Digest::SHA256.hexdigest(request_data)
  signature = sign_message(msg)
  Cubits.logger.debug 'sign_request: ' \
    "path=#{path} nonce=#{nonce} request_data=#{request_data} msg=#{msg} signature=#{signature}"
  signature
end
ssl_context() click to toggle source

Returns configured SSLContext

# File lib/cubits/connection.rb, line 140
def ssl_context
  return @ssl_context if @ssl_context
  @ssl_context = OpenSSL::SSL::SSLContext.new
  @ssl_context.verify_mode = OpenSSL::SSL::VERIFY_PEER
  cert_store = OpenSSL::X509::Store.new
  cert_store.set_default_paths
  @ssl_context.cert_store = cert_store
  @ssl_context
end