module Twilio
module REST
class BaseClient
include Twilio::Util
include Twilio::REST::Utils
HTTP_HEADERS = {
'Accept' => 'application/json',
'Accept-Charset' => 'utf-8',
'User-Agent' => "twilio-ruby/#{Twilio::VERSION}" \
" (#{RUBY_ENGINE}/#{RUBY_PLATFORM}" \
" #{RUBY_VERSION}-p#{RUBY_PATCHLEVEL})"
}
DEFAULTS = {
host: 'api.twilio.com',
port: 443,
use_ssl: true,
ssl_verify_peer: true,
ssl_ca_file: File.dirname(__FILE__) + '/../../../conf/cacert.pem',
timeout: 30,
proxy_addr: nil,
proxy_port: nil,
proxy_user: nil,
proxy_pass: nil,
retry_limit: 1
}
attr_reader :account_sid, :last_request, :last_response
def initialize(*args)
options = args.last.is_a?(Hash) ? args.pop : {}
@config = get_defaults.merge! options
@account_sid = args[0] || Twilio.account_sid
@auth_token = args[1] || Twilio.auth_token
if @account_sid.nil? || @auth_token.nil?
raise ArgumentError, 'Account SID and auth token are required'
end
set_up_connection
set_up_subresources
end
##
# Define #get, #put, #post and #delete helper methods for sending HTTP
# requests to Twilio. You shouldn't need to use these methods directly,
# but they can be useful for debugging. Each method returns a hash
# obtained from parsing the JSON object in the response body.
[:get, :put, :post, :delete].each do |method|
method_class = Net::HTTP.const_get method.to_s.capitalize
define_method method do |path, *args|
params = twilify(args[0])
params = {} if params.empty?
# build the full path unless already given
path = build_full_path(path, params, method) unless args[1]
request = method_class.new(path, HTTP_HEADERS)
request.basic_auth(@account_sid, @auth_token)
request.form_data = params if [:post, :put].include?(method)
connect_and_send(request)
end
end
protected
##
# Get the default config values.
def get_defaults
# To be overridden
DEFAULTS
end
##
# Builds up full request path
# Needs implementation in child classes
def build_full_path(path, params, method)
raise NotImplementedError
end
##
# Set up and cache a Net::HTTP object to use when making requests. This is
# a private method documented for completeness.
def set_up_connection # :doc:
connection_class = Net::HTTP::Proxy @config[:proxy_addr],
@config[:proxy_port], @config[:proxy_user], @config[:proxy_pass]
@connection = connection_class.new @config[:host], @config[:port]
set_up_ssl
@connection.open_timeout = @config[:timeout]
@connection.read_timeout = @config[:timeout]
end
##
# Set up the ssl properties of the @connection Net::HTTP object.
# This is a private method documented for completeness.
def set_up_ssl # :doc:
@connection.use_ssl = @config[:use_ssl]
if @config[:ssl_verify_peer]
@connection.verify_mode = OpenSSL::SSL::VERIFY_PEER
@connection.ca_file = @config[:ssl_ca_file]
else
@connection.verify_mode = OpenSSL::SSL::VERIFY_NONE
end
end
##
# Set up sub resources attributes.
def set_up_subresources # :doc:
# To be overridden
end
##
# Send an HTTP request using the cached @connection object and
# return the JSON response body parsed into a hash. Also save the raw
# Net::HTTP::Request and Net::HTTP::Response objects as
# @last_request and @last_response to allow for
# inspection later.
def connect_and_send(request) # :doc:
@last_request = request
retries_left = @config[:retry_limit]
begin
response = @connection.request request
@last_response = response
if response.kind_of? Net::HTTPServerError
raise Twilio::REST::ServerError
end
rescue Exception
raise if request.class == Net::HTTP::Post
if retries_left > 0 then retries_left -= 1; retry else raise end
end
if response.body and !response.body.empty?
object = MultiJson.load response.body
end
if response.kind_of? Net::HTTPClientError
raise Twilio::REST::RequestError.new object['message'], object['code']
end
object
end
end
end
end