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 34 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 27 def requests @@requests ||= [] end
Public Instance Methods
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
Executes a DELETE request (see HTTP protocol documentation if unfamiliar). Used to delete resources.
# File lib/active_resource/connection.rb, line 89 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 83 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 113 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 95 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 107 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 51 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 101 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 43 def site=(site) @site = site.is_a?(URI) ? site : URI.parse(site) @ssl_options ||= {} if @site.is_a?(URI::HTTPS) @user = URI::DEFAULT_PARSER.unescape(@site.user) if @site.user @password = URI::DEFAULT_PARSER.unescape(@site.password) if @site.password end
Private Instance Methods
# File lib/active_resource/connection.rb, line 196 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 271 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 213 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 259 def client_nonce Digest::MD5.hexdigest("%x" % (Time.now.to_i + rand(65535))) end
# File lib/active_resource/connection.rb, line 184 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 208 def default_header @default_header ||= {} end
# File lib/active_resource/connection.rb, line 245 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 263 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 133 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 412 raise(PreconditionFailed.new(response)) when 422 raise(ResourceInvalid.new(response)) when 429 raise(TooManyRequests.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 170 def http configure_http(new_http) end
# File lib/active_resource/connection.rb, line 287 def http_format_header(http_method) { HTTP_FORMAT_HEADER_NAMES[http_method] => format.mime_type } end
# File lib/active_resource/http_mock.rb, line 358 def http_stub HttpMock.new(@site) end
# File lib/active_resource/connection.rb, line 291 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 174 def new_http if @proxy user = URI::DEFAULT_PARSER.unescape(@proxy.user) if @proxy.user password = URI::DEFAULT_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 119 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 217 def response_auth_header @response_auth_header ||= "" end
# File lib/active_resource/http_mock.rb, line 366 def stub_http? HttpMock.net_connection_disabled? && defined?(@http) && @http.kind_of?(Net::HTTP) end
# File lib/active_resource/http_mock.rb, line 362 def unstub_http? HttpMock.net_connection_enabled? && defined?(@http) && @http.kind_of?(HttpMock) end
# File lib/active_resource/connection.rb, line 221 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