lib/cfoundry/baseclient.rb
require "cfoundry/trace_helpers"
require "net/https"
require "net/http/post/multipart"
require "multi_json"
require "fileutils"
require "forwardable"
module CFoundry
class BaseClient # :nodoc:
include CFoundry::ProxyOptions
extend Forwardable
attr_reader :rest_client
def_delegators :rest_client, :target, :target=, :token,
:trace, :backtrace, :backtrace=, :log, :log=,
:http_proxy, :http_proxy=, :https_proxy, :https_proxy=
def initialize(target, token = nil)
@rest_client = CFoundry::RestClient.new(target, token)
self.trace = false
self.backtrace = false
self.log = false
end
def uaa
@uaa ||= begin
endpoint = info[:authorization_endpoint]
if endpoint
uaa = CFoundry::UAAClient.new(endpoint, "cf", http_proxy: http_proxy, https_proxy: https_proxy)
uaa.trace = trace
uaa.token = token
uaa
else
nil
end
end
end
def password_score(password)
uaa ? uaa.password_score(password) : :unknown
end
def token=(token)
if token.is_a?(String)
token = CFoundry::AuthToken.new(token)
end
@rest_client.token = token
@uaa.token = token if @uaa
end
def trace=(trace)
@rest_client.trace = trace
@uaa.trace = trace if @uaa
end
# Cloud metadata
def info
get("info", :accept => :json)
end
def get(*args)
request("GET", *args)
end
def delete(*args)
request("DELETE", *args)
end
def post(*args)
request("POST", *args)
end
def put(*args)
request("PUT", *args)
end
def request(method, *args)
if needs_token_refresh?
token.auth_header = nil
refresh_token!
end
path, options = normalize_arguments(args)
request, response = request_raw(method, path, options)
handle_response(response, options, request)
end
def request_raw(method, path, options)
@rest_client.request(method, path, options)
end
def refresh_token!
self.token = uaa.try_to_refresh_token!
end
def stream_url(url, &blk)
uri = URI.parse(url)
opts = {}
if uri.scheme == "https"
opts[:use_ssl] = true
opts[:verify_mode] = OpenSSL::SSL::VERIFY_NONE
end
Net::HTTP.start(uri.host, uri.port, *proxy_options_for(uri), opts) do |http|
http.read_timeout = 5
req = Net::HTTP::Get.new(uri.request_uri)
req["Authorization"] = token.auth_header if token
http.request(req) do |response|
case response
when Net::HTTPOK
response.read_body(&blk)
when Net::HTTPNotFound
raise CFoundry::NotFound.new(response.body, 404)
when Net::HTTPForbidden
raise CFoundry::Denied.new(response.body, 403)
when Net::HTTPUnauthorized
raise CFoundry::Unauthorized.new(response.body, 401)
else
raise CFoundry::BadResponse.new(response.body, response.code)
end
end
end
end
private
def needs_token_refresh?
token && token.auth_header && token.refresh_token && \
token.expires_soon?
end
def status_is_successful?(code)
(code >= 200) && (code < 400)
end
def handle_response(response, options, request)
if status_is_successful?(response[:status].to_i)
handle_successful_response(response, options)
else
handle_error_response(response, request)
end
end
def handle_successful_response(response, options)
if options[:return_response]
response
elsif options[:accept] == :json
parse_json(response[:body])
else
response[:body]
end
end
def handle_error_response(response, request)
body_json = parse_json(response[:body])
body_code = body_json && body_json[:code]
code = body_code || response[:status].to_i
if body_code
error_class = CFoundry::APIError.error_classes[body_code] || CFoundry::APIError
raise error_class.new(body_json[:description], body_code, request, response)
end
case code
when 404
raise CFoundry::NotFound.new(nil, code, request, response)
when 403
raise CFoundry::Denied.new(nil, code, request, response)
when 401
raise CFoundry::Unauthorized.new(nil, code, request, response)
else
raise CFoundry::BadResponse.new(nil, code, request, response)
end
end
def normalize_arguments(args)
if args.last.is_a?(Hash)
options = args.pop
else
options = {}
end
[normalize_path(args), options]
end
URI_ENCODING_PATTERN = Regexp.new("[^#{URI::PATTERN::UNRESERVED}]")
def normalize_path(segments)
if segments.size == 1 && segments.first =~ /^\//
segments.first
else
segments.flatten.collect { |x|
URI.encode(x.to_s, URI_ENCODING_PATTERN)
}.join("/")
end
end
def parse_json(x)
if x.empty?
raise MultiJson::DecodeError.new("Empty JSON string", [], "")
else
MultiJson.load(x, :symbolize_keys => true)
end
rescue MultiJson::DecodeError
nil
end
end
end