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 33
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 26
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 63
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 85
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 79
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 103
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 58
def password=(password)
  @password = password
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 97
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 48
def proxy=(proxy)
  @proxy = proxy.is_a?(URI) ? proxy : URI.parser.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 91
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 41
def site=(site)
  @site = site.is_a?(URI) ? site : URI.parser.parse(site)
  @user = URI.parser.unescape(@site.user) if @site.user
  @password = URI.parser.unescape(@site.password) if @site.password
end
ssl_options=(opts={}) click to toggle source

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

# File lib/active_resource/connection.rb, line 73
def ssl_options=(opts={})
  @ssl_options = opts
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 68
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 53
def user=(user)
  @user = user
end

Private Instance Methods

apply_ssl_options(http) click to toggle source
# File lib/active_resource/connection.rb, line 180
def apply_ssl_options(http)
  return http unless @site.is_a?(URI::HTTPS)

  http.use_ssl     = true
  http.verify_mode = OpenSSL::SSL::VERIFY_NONE
  return http unless defined?(@ssl_options)

  http.ca_path     = @ssl_options[:ca_path] if @ssl_options[:ca_path]
  http.ca_file     = @ssl_options[:ca_file] if @ssl_options[:ca_file]

  http.cert        = @ssl_options[:cert] if @ssl_options[:cert]
  http.key         = @ssl_options[:key]  if @ssl_options[:key]

  http.cert_store  = @ssl_options[:cert_store]  if @ssl_options[:cert_store]
  http.ssl_timeout = @ssl_options[:ssl_timeout] if @ssl_options[:ssl_timeout]

  http.verify_mode     = @ssl_options[:verify_mode]     if @ssl_options[:verify_mode]
  http.verify_callback = @ssl_options[:verify_callback] if @ssl_options[:verify_callback]
  http.verify_depth    = @ssl_options[:verify_depth]    if @ssl_options[:verify_depth]

  http
end
auth_attributes_for(uri, request_digest, params) click to toggle source
# File lib/active_resource/connection.rb, line 264
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 226
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 208
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 252
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 168
def configure_http(http)
  http = apply_ssl_options(http)

  # Net::HTTP timeouts default to 60 seconds.
  if @timeout
    http.open_timeout = @timeout
    http.read_timeout = @timeout
  end

  http
end
default_header() click to toggle source
# File lib/active_resource/connection.rb, line 203
def default_header
  @default_header ||= {}
end
digest_auth_header(http_method, uri) click to toggle source
# File lib/active_resource/connection.rb, line 238
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 256
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 123
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 156
def http
  configure_http(new_http)
end
http_format_header(http_method) click to toggle source
# File lib/active_resource/connection.rb, line 277
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 281
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 160
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 109
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 212
def response_auth_header
  @response_auth_header ||= ""
end
with_auth() { || ... } click to toggle source
# File lib/active_resource/connection.rb, line 216
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