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]
open_timeout[R]
password[R]
proxy[R]
read_timeout[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
  @proxy = @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 97
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 91
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 121
def head(path, headers = {})
  with_auth { request(:head, path, build_request_headers(headers, :head, self.site.merge(path))) }
end
open_timeout=(timeout) click to toggle source

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

# File lib/active_resource/connection.rb, line 75
def open_timeout=(timeout)
  @open_timeout = timeout
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 103
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 115
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 109
def put(path, body = '', headers = {})
  with_auth { request(:put, path, body.to_s, build_request_headers(headers, :put, self.site.merge(path))) }
end
read_timeout=(timeout) click to toggle source

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

# File lib/active_resource/connection.rb, line 80
def read_timeout=(timeout)
  @read_timeout = timeout
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 85
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 200
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

      # 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 273
def auth_attributes_for(uri, request_digest, params)
  auth_attrs =
    [
      %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(response="#{request_digest}")]

  auth_attrs << %Q(opaque="#{params['opaque']}") unless params['opaque'].blank?
  auth_attrs.join(", ")
end
authorization_header(http_method, uri) click to toggle source
# File lib/active_resource/connection.rb, line 235
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 217
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 261
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 188
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
    https.open_timeout = @open_timeout if defined?(@open_timeout)
    https.read_timeout = @read_timeout if defined?(@read_timeout)
  end
end
default_header() click to toggle source
# File lib/active_resource/connection.rb, line 212
def default_header
  @default_header ||= {}
end
digest_auth_header(http_method, uri) click to toggle source
# File lib/active_resource/connection.rb, line 247
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 265
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 141
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 174
def http
  configure_http(new_http)
end
http_format_header(http_method) click to toggle source
# File lib/active_resource/connection.rb, line 289
def http_format_header(http_method)
  {HTTP_FORMAT_HEADER_NAMES[http_method] => format.mime_type}
end
http_stub() click to toggle source
# File lib/active_resource/http_mock.rb, line 361
def http_stub
  HttpMock.new(@site)
end
legitimize_auth_type(auth_type) click to toggle source
# File lib/active_resource/connection.rb, line 293
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 178
def new_http
  if @proxy
    user = URI.parser.unescape(@proxy.user) if @proxy.user
    password = URI.parser.unescape(@proxy.password) if @proxy.password
    Net::HTTP.new(@site.host, @site.port, @proxy.host, @proxy.port, user, 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 127
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 221
def response_auth_header
  @response_auth_header ||= ""
end
stub_http?() click to toggle source
# File lib/active_resource/http_mock.rb, line 369
def stub_http?
  HttpMock.net_connection_disabled? && defined?(@http) && @http.kind_of?(Net::HTTP)
end
unstub_http?() click to toggle source
# File lib/active_resource/http_mock.rb, line 365
def unstub_http?
  HttpMock.net_connection_enabled? && defined?(@http) && @http.kind_of?(HttpMock)
end
with_auth() { || ... } click to toggle source
# File lib/active_resource/connection.rb, line 225
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