class ActiveResource::Connection

Class to handle connections to remote web services. This class is used by ActiveResource::Base to interface with REST services.

Constants

HTTP_FORMAT_HEADER_NAMES

Attributes

auth_type[R]
format[RW]
password[R]
proxy[R]
site[R]
ssl_options[R]
timeout[R]
user[R]

Public Class Methods

new(site, format = ActiveResource::Formats::JsonFormat) click to toggle source

The site parameter is required and will set the site attribute to the URI for the remote resource service.

# File lib/active_resource/connection.rb, line 34
def initialize(site, format = ActiveResource::Formats::JsonFormat)
  raise ArgumentError, 'Missing site URI' unless site
  @user = @password = nil
  self.site = site
  self.format = format
end
requests() click to toggle source
# File lib/active_resource/connection.rb, line 27
def requests
  @@requests ||= []
end

Public Instance Methods

auth_type=(auth_type) click to toggle source

Sets the auth type for remote service.

# File lib/active_resource/connection.rb, line 65
def auth_type=(auth_type)
  @auth_type = legitimize_auth_type(auth_type)
end
delete(path, headers = {}) click to toggle source

Executes a DELETE request (see HTTP protocol documentation if unfamiliar). Used to delete resources.

# File lib/active_resource/connection.rb, line 87
def delete(path, headers = {})
  with_auth { request(:delete, path, build_request_headers(headers, :delete, self.site.merge(path))) }
end
get(path, headers = {}) click to toggle source

Executes a GET request. Used to get (find) resources.

# File lib/active_resource/connection.rb, line 81
def get(path, headers = {})
  with_auth { request(:get, path, build_request_headers(headers, :get, self.site.merge(path))) }
end
head(path, headers = {}) click to toggle source

Executes a HEAD request. Used to obtain meta-information about resources, such as whether they exist and their size (via response headers).

# File lib/active_resource/connection.rb, line 111
def head(path, headers = {})
  with_auth { request(:head, path, build_request_headers(headers, :head, self.site.merge(path))) }
end
password=(password) click to toggle source

Sets the password for remote service.

# File lib/active_resource/connection.rb, line 60
def password=(password)
  @password = password
end
patch(path, body = '', headers = {}) click to toggle source

Executes a PATCH request (see HTTP protocol documentation if unfamiliar). Used to update resources.

# File lib/active_resource/connection.rb, line 93
def patch(path, body = '', headers = {})
  with_auth { request(:patch, path, body.to_s, build_request_headers(headers, :patch, self.site.merge(path))) }
end
post(path, body = '', headers = {}) click to toggle source

Executes a POST request. Used to create new resources.

# File lib/active_resource/connection.rb, line 105
def post(path, body = '', headers = {})
  with_auth { request(:post, path, body.to_s, build_request_headers(headers, :post, self.site.merge(path))) }
end
proxy=(proxy) click to toggle source

Set the proxy for remote service.

# File lib/active_resource/connection.rb, line 50
def proxy=(proxy)
  @proxy = proxy.is_a?(URI) ? proxy : URI.parse(proxy)
end
put(path, body = '', headers = {}) click to toggle source

Executes a PUT request (see HTTP protocol documentation if unfamiliar). Used to update resources.

# File lib/active_resource/connection.rb, line 99
def put(path, body = '', headers = {})
  with_auth { request(:put, path, body.to_s, build_request_headers(headers, :put, self.site.merge(path))) }
end
site=(site) click to toggle source

Set URI for remote service.

# File lib/active_resource/connection.rb, line 42
def site=(site)
  @site = site.is_a?(URI) ? site : URI.parse(site)
  @ssl_options ||= {} if @site.is_a?(URI::HTTPS)
  @user = URI.parser.unescape(@site.user) if @site.user
  @password = URI.parser.unescape(@site.password) if @site.password
end
ssl_options=(options) click to toggle source

Hash of options applied to Net::HTTP instance when site protocol is 'https'.

# File lib/active_resource/connection.rb, line 75
def ssl_options=(options)
  @ssl_options = options
end
timeout=(timeout) click to toggle source

Sets the number of seconds after which HTTP requests to the remote service should time out.

# File lib/active_resource/connection.rb, line 70
def timeout=(timeout)
  @timeout = timeout
end
user=(user) click to toggle source

Sets the user for remote service.

# File lib/active_resource/connection.rb, line 55
def user=(user)
  @user = user
end

Private Instance Methods

apply_ssl_options(http) click to toggle source
# File lib/active_resource/connection.rb, line 186
def apply_ssl_options(http)
  http.tap do |https|
    # Skip config if site is already a https:// URI.
    if defined? @ssl_options
      http.use_ssl = true

      # Default to no cert verification (WTF? FIXME)
      http.verify_mode = OpenSSL::SSL::VERIFY_NONE

      # All the SSL options have corresponding http settings.
      @ssl_options.each { |key, value| http.send "#{key}=", value }
    end
  end
end
auth_attributes_for(uri, request_digest, params) click to toggle source
# File lib/active_resource/connection.rb, line 262
def auth_attributes_for(uri, request_digest, params)
  [
    %Q(username="#{@user}"),
    %Q(realm="#{params['realm']}"),
    %Q(qop="#{params['qop']}"),
    %Q(uri="#{uri.path}"),
    %Q(nonce="#{params['nonce']}"),
    %Q(nc="0"),
    %Q(cnonce="#{params['cnonce']}"),
    %Q(opaque="#{params['opaque']}"),
    %Q(response="#{request_digest}")].join(", ")
end
authorization_header(http_method, uri) click to toggle source
# File lib/active_resource/connection.rb, line 224
def authorization_header(http_method, uri)
  if @user || @password
    if auth_type == :digest
      { 'Authorization' => digest_auth_header(http_method, uri) }
    else
      { 'Authorization' => 'Basic ' + ["#{@user}:#{@password}"].pack('m').delete("\r\n") }
    end
  else
    {}
  end
end
build_request_headers(headers, http_method, uri) click to toggle source

Builds headers for request to remote service.

# File lib/active_resource/connection.rb, line 206
def build_request_headers(headers, http_method, uri)
  authorization_header(http_method, uri).update(default_header).update(http_format_header(http_method)).update(headers)
end
client_nonce() click to toggle source
# File lib/active_resource/connection.rb, line 250
def client_nonce
  Digest::MD5.hexdigest("%x" % (Time.now.to_i + rand(65535)))
end
configure_http(http) click to toggle source
# File lib/active_resource/connection.rb, line 176
def configure_http(http)
  apply_ssl_options(http).tap do |https|
    # Net::HTTP timeouts default to 60 seconds.
    if defined? @timeout
      https.open_timeout = @timeout
      https.read_timeout = @timeout
    end
  end
end
default_header() click to toggle source
# File lib/active_resource/connection.rb, line 201
def default_header
  @default_header ||= {}
end
digest_auth_header(http_method, uri) click to toggle source
# File lib/active_resource/connection.rb, line 236
def digest_auth_header(http_method, uri)
  params = extract_params_from_response

  request_uri = uri.path
  request_uri << "?#{uri.query}" if uri.query

  ha1 = Digest::MD5.hexdigest("#{@user}:#{params['realm']}:#{@password}")
  ha2 = Digest::MD5.hexdigest("#{http_method.to_s.upcase}:#{request_uri}")

  params.merge!('cnonce' => client_nonce)
  request_digest = Digest::MD5.hexdigest([ha1, params['nonce'], "0", params['cnonce'], params['qop'], ha2].join(":"))
  "Digest #{auth_attributes_for(uri, request_digest, params)}"
end
extract_params_from_response() click to toggle source
# File lib/active_resource/connection.rb, line 254
def extract_params_from_response
  params = {}
  if response_auth_header =~ /^(\w+) (.*)/
    $2.gsub(/(\w+)="(.*?)"/) { params[$1] = $2 }
  end
  params
end
handle_response(response) click to toggle source

Handles response and error codes from the remote service.

# File lib/active_resource/connection.rb, line 131
def handle_response(response)
  case response.code.to_i
    when 301, 302, 303, 307
      raise(Redirection.new(response))
    when 200...400
      response
    when 400
      raise(BadRequest.new(response))
    when 401
      raise(UnauthorizedAccess.new(response))
    when 403
      raise(ForbiddenAccess.new(response))
    when 404
      raise(ResourceNotFound.new(response))
    when 405
      raise(MethodNotAllowed.new(response))
    when 409
      raise(ResourceConflict.new(response))
    when 410
      raise(ResourceGone.new(response))
    when 422
      raise(ResourceInvalid.new(response))
    when 401...500
      raise(ClientError.new(response))
    when 500...600
      raise(ServerError.new(response))
    else
      raise(ConnectionError.new(response, "Unknown response code: #{response.code}"))
  end
end
http() click to toggle source

Creates new Net::HTTP instance for communication with the remote service and resources.

# File lib/active_resource/connection.rb, line 164
def http
  configure_http(new_http)
end
http_format_header(http_method) click to toggle source
# File lib/active_resource/connection.rb, line 275
def http_format_header(http_method)
  {HTTP_FORMAT_HEADER_NAMES[http_method] => format.mime_type}
end
legitimize_auth_type(auth_type) click to toggle source
# File lib/active_resource/connection.rb, line 279
def legitimize_auth_type(auth_type)
  return :basic if auth_type.nil?
  auth_type = auth_type.to_sym
  auth_type.in?([:basic, :digest]) ? auth_type : :basic
end
new_http() click to toggle source
# File lib/active_resource/connection.rb, line 168
def new_http
  if @proxy
    Net::HTTP.new(@site.host, @site.port, @proxy.host, @proxy.port, @proxy.user, @proxy.password)
  else
    Net::HTTP.new(@site.host, @site.port)
  end
end
request(method, path, *arguments) click to toggle source

Makes a request to the remote service.

# File lib/active_resource/connection.rb, line 117
def request(method, path, *arguments)
  result = ActiveSupport::Notifications.instrument("request.active_resource") do |payload|
    payload[:method]      = method
    payload[:request_uri] = "#{site.scheme}://#{site.host}:#{site.port}#{path}"
    payload[:result]      = http.send(method, path, *arguments)
  end
  handle_response(result)
rescue Timeout::Error => e
  raise TimeoutError.new(e.message)
rescue OpenSSL::SSL::SSLError => e
  raise SSLError.new(e.message)
end
response_auth_header() click to toggle source
# File lib/active_resource/connection.rb, line 210
def response_auth_header
  @response_auth_header ||= ""
end
with_auth() { || ... } click to toggle source
# File lib/active_resource/connection.rb, line 214
def with_auth
  retried ||= false
  yield
rescue UnauthorizedAccess => e
  raise if retried || auth_type != :digest
  @response_auth_header = e.response['WWW-Authenticate']
  retried = true
  retry
end