class RestConnection::Connection
Attributes
Settings is a hash of options for customizing the connection. settings.merge! { :common_headers => { “X_CUSTOM_HEADER” => “BLAH” }, :api_url => :user => :pass =>
Public Class Methods
RestConnection
api settings configuration file: Settings are loaded from a yaml configuration file in users home directory. Copy the example config from the gemhome/config/rest_api_config.yaml.sample to ~/.rest_connection/rest_api_config.yaml OR to /etc/rest_connection/rest_api_config.yaml Here’s an example of overriding the settings in the configuration file:
Server.connection.settings[:api_url] = "https://my.rightscale.com/api/acct/1234"
# File lib/rest_connection.rb, line 56 def initialize(config_yaml = File.join(File.expand_path("~"), ".rest_connection", "rest_api_config.yaml")) @@logger = nil @@user = nil @@pass = nil etc_config = File.join("#{File::SEPARATOR}etc", "rest_connection", "rest_api_config.yaml") app_bin_dir = File.expand_path(File.dirname(caller.last)) app_yaml = File.join(app_bin_dir,"..","config","rest_api_config.yaml") if config_yaml.is_a?(Hash) @settings = config_yaml elsif File.exists?(app_yaml) @settings = YAML::load(IO.read(app_yaml)) elsif File.exists?(config_yaml) @settings = YAML::load(IO.read(config_yaml)) elsif File.exists?(etc_config) @settings = YAML::load(IO.read(etc_config)) else logger("\nWARNING: you must setup config file rest_api_config.yaml in #{app_yaml} or #{config_yaml} or #{etc_config}") logger("WARNING: see GEM_HOME/rest_connection/config/rest_api_config.yaml for example config") @settings = {} end @settings.keys.each { |k| @settings[k.to_sym] = @settings[k] if String === k } @settings[:extension] = ".js" @settings[:api_href] = @settings[:api_url] unless @settings[:api_href] unless @settings[:user] @@user = ask("Username:") unless @@user @settings[:user] = @@user end unless @settings[:pass] @@pass = ask("Password:") { |q| q.echo = false } unless @@pass @settings[:pass] = @@pass end @settings[:azure_hack_on] = true if @settings[:azure_hack_on] == nil @settings[:azure_hack_retry_count] ||= 5 @settings[:azure_hack_sleep_seconds] ||= 60 @settings[:api_logging] ||= false @settings[:legacy_shard] = true if @settings[:legacy_shard] == nil end
Public Instance Methods
connection.delete(server_url)
href = “/api/base_new” if this begins with a slash then the url will be used as absolute path. href = “servers” this will be concat’d on to the api_url from the settings additional_parameters = Hash
or String of parameters to pass to HTTP::Delete
# File lib/rest_connection.rb, line 213 def delete(href, additional_parameters = {}) rest_connect do |base_uri, headers| new_href = (href =~ /^\// ? href : "#{base_uri}/#{href}") new_path = URI.escape(new_href) req = Net::HTTP::Delete.new(href, headers) req.set_content_type('application/json') req.body = additional_parameters.to_json req end end
# File lib/rest_connection.rb, line 134 def error_handler(e) case e when EOFError, Timeout::Error if @max_retries > 0 logger("Caught #{e}. Retrying...") @max_retries -= 1 return true end when RestConnection::Errors::Forbidden if @max_retries > 0 if e.response.body =~ /(session|cookie).*(invalid|expired)/i logger("Caught '#{e.response.body}'. Refreshing cookie...") refresh_cookie if respond_to?(:refresh_cookie) else return false end @max_retries -= 1 return true end end return false end
connection.get(“/root/login”, :test_header => “x”, :test_header2 => “y”) href = “/api/base_new” if this begins with a slash then the url will be used as absolute path. href = “servers” this will be concat’d on to the api_url from the settings additional_parameters = Hash
or String of parameters to pass to HTTP::Get
# File lib/rest_connection.rb, line 161 def get(href, additional_parameters = "") rest_connect do |base_uri,headers| new_href = (href =~ /^\// ? href : "#{base_uri}/#{href}") puts("DEBUG: new_href get : #{new_href.inspect}") if @settings[:api_logging] params = requestify(additional_parameters) || "" new_path = URI.escape(new_href + @settings[:extension] + "?") + params Net::HTTP::Get.new(new_path, headers) end end
handle_response
res = HTTP response
decoding and post processing goes here. This is where you may need some customization if you want to handle the response differently (or not at all!). Luckily it’s easy to modify based on this handler.
# File lib/rest_connection.rb, line 228 def handle_response(res) if res.code.to_i == 201 or res.code.to_i == 202 # # In most cases, it's safe to return the location # if res['Location'] return res['Location'] else # # Ec2ServerArrayInternal.run_script_on_instances returns XML. # We need to parse it to retrieve the href's to audit entries. # xml_response = Nokogiri::XML(res.body) return xml_response.xpath('audit-entries/audit-entry/href').map { |href| href.content } end elsif [200,203,204].detect { |d| d == res.code.to_i } if res.body begin return JSON.load(res.body) rescue => e return res end else return res end else raise RestConnection::Errors.status_error(res) end end
Logs a message at info level to stdout or log file FIXME: don’t lazy initialize
# File lib/rest_connection.rb, line 261 def logger(message) init_message = "Initializing Logging using " if @@logger.nil? if ENV['REST_CONNECTION_LOG'] @@logger = Logger.new(ENV['REST_CONNECTION_LOG']) init_message += ENV['REST_CONNECTION_LOG'] else @@logger = Logger.new(STDOUT) init_message += "STDOUT" end @@logger.info(init_message) end if @settings.nil? @@logger.info(message) else @@logger.info("[API v#{@settings[:common_headers]['X_API_VERSION']}] " + message) end end
used by requestify to build parameters strings
# File lib/rest_connection.rb, line 282 def name_with_prefix(prefix, name) prefix ? "#{prefix}[#{name}]" : name.to_s end
connection.post(server_url + “/start”)
href = “/api/base_new” if this begins with a slash then the url will be used as absolute path. href = “servers” this will be concat’d on to the api_url from the settings additional_parameters = Hash
or String of parameters to pass to HTTP::Post
# File lib/rest_connection.rb, line 176 def post(href, additional_parameters = {}) rest_connect do |base_uri, headers| new_href = (href =~ /^\// ? href : "#{base_uri}/#{href}") puts("DEBUG: new_href post : #{new_href.inspect}") if @settings[:api_logging] res = Net::HTTP::Post.new(new_href , headers) unless additional_parameters.empty? res.set_content_type('application/json') res.body = additional_parameters.to_json end #res.set_form_data(additional_parameters, '&') res end end
connection.put(server_url + “/start”)
href = “/api/base” if this begins with a slash then the url will be used as absolute path. href = “servers” this will be concat’d on to the api_url from the settings additional_parameters = Hash
or String of parameters to pass to HTTP::Put
# File lib/rest_connection.rb, line 195 def put(href, additional_parameters = {}) rest_connect do |base_uri, headers| new_href = (href =~ /^\// ? href : "#{base_uri}/#{href}") new_path = URI.escape(new_href) puts("DEBUG: new_href put : #{new_href.inspect}") if @settings[:api_logging] req = Net::HTTP::Put.new(new_path, headers) puts("DEBUG: req put : #{req.inspect}") if @settings[:api_logging] req.set_content_type('application/json') req.body = additional_parameters.to_json req end end
recursive method builds CGI escaped strings from Hashes, Arrays and strings of parameters.
# File lib/rest_connection.rb, line 287 def requestify(parameters, prefix=nil) if Hash === parameters return nil if parameters.empty? parameters.map { |k,v| requestify(v, name_with_prefix(prefix, k)) }.join("&") elsif Array === parameters parameters.map { |v| requestify(v, name_with_prefix(prefix, "")) }.join("&") elsif prefix.nil? parameters else "#{prefix}=#{CGI.escape(parameters.to_s)}" end end
Main HTTP connection loop. Common settings are set here, then we yield(BASE_URI, OPTIONAL_HEADERS) to other methods for each type of HTTP request: GET, PUT, POST, DELETE
The block must return a Net::HTTP Request. You have a chance to taylor the request inside the block that you pass by modifying the url and headers.
rest_connect
do |base_uri, headers|
headers.merge! {:my_header => "blah"} Net::HTTP::Get.new(base_uri, headers)
end
# File lib/rest_connection.rb, line 104 def rest_connect(&block) uri = URI.parse(@settings[:api_href]) http = Net::HTTP.new(uri.host, uri.port) http.read_timeout = 300 if uri.scheme == 'https' http.use_ssl = true http.verify_mode = OpenSSL::SSL::VERIFY_NONE end headers = @settings[:common_headers] http.start do |http| @max_retries = 3 ret = nil begin headers.delete("Cookie") headers.merge!("Cookie" => @cookie) if @cookie req = yield(uri, headers) logger("#{req.method}: #{req.path}") logger("\trequest body: #{req.body}") if req.body and req.body !~ /password/ req.basic_auth(@settings[:user], @settings[:pass]) if @settings[:user] unless @cookie response, body = http.request(req) ret = handle_response(response) rescue Exception => e raise unless error_handler(e) retry end ret end end