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) raise ArgumentError, 'Missing site URI' unless site @proxy = @user = @password = nil self.site = site self.format = format 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 97 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 91 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 121 def head(path, headers = {}) with_auth { request(:head, path, build_request_headers(headers, :head, self.site.merge(path))) } end
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
Sets the password for remote service.
# File lib/active_resource/connection.rb, line 60 def password=(password) @password = password end
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
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
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
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
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
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
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
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
Sets the user for remote service.
# File lib/active_resource/connection.rb, line 55 def user=(user) @user = user end
Private Instance Methods
# 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
# 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
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
# File lib/active_resource/connection.rb, line 261 def client_nonce Digest::MD5.hexdigest("%x" % (Time.now.to_i + rand(65535))) end
# 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
# File lib/active_resource/connection.rb, line 212 def default_header @default_header ||= {} end
# 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
# 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
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
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
# File lib/active_resource/connection.rb, line 289 def http_format_header(http_method) {HTTP_FORMAT_HEADER_NAMES[http_method] => format.mime_type} end
# File lib/active_resource/http_mock.rb, line 361 def http_stub HttpMock.new(@site) end
# 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
# 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
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
# File lib/active_resource/connection.rb, line 221 def response_auth_header @response_auth_header ||= "" end
# File lib/active_resource/http_mock.rb, line 369 def stub_http? HttpMock.net_connection_disabled? && defined?(@http) && @http.kind_of?(Net::HTTP) end
# File lib/active_resource/http_mock.rb, line 365 def unstub_http? HttpMock.net_connection_enabled? && defined?(@http) && @http.kind_of?(HttpMock) end
# 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