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
Public Class Methods
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 35 def initialize(site, format = ActiveResource::Formats::JsonFormat, logger: nil) raise ArgumentError, "Missing site URI" unless site @proxy = @user = @password = @bearer_token = nil self.site = site self.format = format self.logger = logger end
# File lib/active_resource/connection.rb, line 28 def requests @@requests ||= [] end
Public Instance Methods
Sets the auth type for remote service.
# File lib/active_resource/connection.rb, line 66 def auth_type=(auth_type) @auth_type = legitimize_auth_type(auth_type) end
Executes a DELETE request (see HTTP protocol documentation if unfamiliar). Used to delete resources.
# File lib/active_resource/connection.rb, line 90 def delete(path, headers = {}) with_auth { request(:delete, path, build_request_headers(headers, :delete, self.site.merge(path))) } end
Executes a GET request. Used to get (find) resources.
# File lib/active_resource/connection.rb, line 84 def get(path, headers = {}) with_auth { request(:get, path, build_request_headers(headers, :get, self.site.merge(path))) } end
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 114 def head(path, headers = {}) with_auth { request(:head, path, build_request_headers(headers, :head, self.site.merge(path))) } end
Executes a PATCH request (see HTTP protocol documentation if unfamiliar). Used to update resources.
# File lib/active_resource/connection.rb, line 96 def patch(path, body = "", headers = {}) with_auth { request(:patch, path, body.to_s, build_request_headers(headers, :patch, self.site.merge(path))) } end
Executes a POST request. Used to create new resources.
# File lib/active_resource/connection.rb, line 108 def post(path, body = "", headers = {}) with_auth { request(:post, path, body.to_s, build_request_headers(headers, :post, self.site.merge(path))) } end
Set the proxy for remote service.
# File lib/active_resource/connection.rb, line 52 def proxy=(proxy) @proxy = proxy.is_a?(URI) ? proxy : URI.parse(proxy) end
Executes a PUT request (see HTTP protocol documentation if unfamiliar). Used to update resources.
# File lib/active_resource/connection.rb, line 102 def put(path, body = "", headers = {}) with_auth { request(:put, path, body.to_s, build_request_headers(headers, :put, self.site.merge(path))) } end
Set URI for remote service.
# File lib/active_resource/connection.rb, line 44 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
Private Instance Methods
# File lib/active_resource/connection.rb, line 193 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
# File lib/active_resource/connection.rb, line 268 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']}"), 'nc="0"', %Q(cnonce="#{params['cnonce']}"), %Q(response="#{request_digest}")] auth_attrs << %Q(opaque="#{params['opaque']}") unless params["opaque"].blank? auth_attrs.join(", ") end
Builds headers for request to remote service.
# File lib/active_resource/connection.rb, line 210 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
# File lib/active_resource/connection.rb, line 256 def client_nonce Digest::MD5.hexdigest("%x" % (Time.now.to_i + rand(65535))) end
# File lib/active_resource/connection.rb, line 181 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
# File lib/active_resource/connection.rb, line 205 def default_header @default_header ||= {} end
# File lib/active_resource/connection.rb, line 242 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["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
# File lib/active_resource/connection.rb, line 260 def extract_params_from_response params = {} if response_auth_header =~ /^(\w+) (.*)/ $2.gsub(/(\w+)="(.*?)"/) { params[$1] = $2 } end params end
Handles response and error codes from the remote service.
# File lib/active_resource/connection.rb, line 134 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
Creates new Net::HTTP instance for communication with the remote service and resources.
# File lib/active_resource/connection.rb, line 167 def http configure_http(new_http) end
# File lib/active_resource/connection.rb, line 284 def http_format_header(http_method) { HTTP_FORMAT_HEADER_NAMES[http_method] => format.mime_type } end
# File lib/active_resource/http_mock.rb, line 360 def http_stub HttpMock.new(@site) end
# File lib/active_resource/connection.rb, line 288 def legitimize_auth_type(auth_type) return :basic if auth_type.nil? auth_type = auth_type.to_sym auth_type.in?([:basic, :digest, :bearer]) ? auth_type : :basic end
# File lib/active_resource/connection.rb, line 171 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
Makes a request to the remote service.
# File lib/active_resource/connection.rb, line 120 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
# File lib/active_resource/connection.rb, line 214 def response_auth_header @response_auth_header ||= "" end
# File lib/active_resource/http_mock.rb, line 368 def stub_http? HttpMock.net_connection_disabled? && defined?(@http) && @http.kind_of?(Net::HTTP) end
# File lib/active_resource/http_mock.rb, line 364 def unstub_http? HttpMock.net_connection_enabled? && defined?(@http) && @http.kind_of?(HttpMock) end
# File lib/active_resource/connection.rb, line 218 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