class RestConnection::Connection

Attributes

settings[RW]

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

new(config_yaml = File.join(File.expand_path("~"), ".rest_connection", "rest_api_config.yaml")) click to toggle source

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

delete(href, additional_parameters = {}) click to toggle source

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
error_handler(e) click to toggle source
# 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
get(href, additional_parameters = "") click to toggle source

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) click to toggle source

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
logger(message) click to toggle source

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
name_with_prefix(prefix, name) click to toggle source

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
post(href, additional_parameters = {}) click to toggle source

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
put(href, additional_parameters = {}) click to toggle source

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
requestify(parameters, prefix=nil) click to toggle source

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
rest_connect() { |uri, headers| ... } click to toggle source

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