lib/active_resource/connection.rb in embark-journey-0.1.8 vs lib/active_resource/connection.rb in embark-journey-0.2.1

- old
+ new

@@ -1,43 +1,45 @@ -require 'active_support/core_ext/benchmark' -require 'active_support/core_ext/uri' -require 'active_support/core_ext/object/inclusion' -require 'net/https' -require 'date' -require 'time' -require 'uri' +# frozen_string_literal: true +require "active_support/core_ext/benchmark" +require "active_support/core_ext/uri" +require "active_support/core_ext/object/inclusion" +require "net/https" +require "date" +require "time" +require "uri" + module ActiveResource # Class to handle connections to remote web services. # This class is used by ActiveResource::Base to interface with REST # services. class Connection - - HTTP_FORMAT_HEADER_NAMES = { :get => 'Accept', - :put => 'Content-Type', - :post => 'Content-Type', - :patch => 'Content-Type', - :delete => 'Accept', - :head => 'Accept' + HTTP_FORMAT_HEADER_NAMES = { get: "Accept", + put: "Content-Type", + post: "Content-Type", + patch: "Content-Type", + delete: "Accept", + head: "Accept" } - attr_reader :site, :user, :password, :auth_type, :timeout, :proxy, :ssl_options - attr_accessor :format + attr_reader :site, :user, :password, :bearer_token, :auth_type, :timeout, :open_timeout, :read_timeout, :proxy, :ssl_options + attr_accessor :format, :logger class << self def requests @@requests ||= [] end end # The +site+ parameter is required and will set the +site+ # attribute to the URI for the remote resource service. - def initialize(site, format = ActiveResource::Formats::JsonFormat) - raise ArgumentError, 'Missing site URI' unless site - @proxy = @user = @password = nil + 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 # Set URI for remote service. def site=(site) @site = site.is_a?(URI) ? site : URI.parse(site) @@ -50,33 +52,34 @@ def proxy=(proxy) @proxy = proxy.is_a?(URI) ? proxy : URI.parse(proxy) end # Sets the user for remote service. - def user=(user) - @user = user - end + attr_writer :user # Sets the password for remote service. - def password=(password) - @password = password - end + attr_writer :password + # Sets the bearer token for remote service. + attr_writer :bearer_token + # Sets the auth type for remote service. def auth_type=(auth_type) @auth_type = legitimize_auth_type(auth_type) end # Sets the number of seconds after which HTTP requests to the remote service should time out. - def timeout=(timeout) - @timeout = timeout - end + attr_writer :timeout + # Sets the number of seconds after which HTTP connects to the remote service should time out. + attr_writer :open_timeout + + # Sets the number of seconds after which HTTP read requests to the remote service should time out. + attr_writer :read_timeout + # Hash of options applied to Net::HTTP instance when +site+ protocol is 'https'. - def ssl_options=(options) - @ssl_options = options - end + attr_writer :ssl_options # Executes a GET request. # Used to get (find) resources. def get(path, headers = {}) with_auth { request(:get, path, build_request_headers(headers, :get, self.site.merge(path))) } @@ -88,23 +91,23 @@ with_auth { request(:delete, path, build_request_headers(headers, :delete, self.site.merge(path))) } end # Executes a PATCH request (see HTTP protocol documentation if unfamiliar). # Used to update resources. - def patch(path, body = '', headers = {}) + 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 PUT request (see HTTP protocol documentation if unfamiliar). # Used to update resources. - def put(path, body = '', headers = {}) + def put(path, body = "", headers = {}) with_auth { request(:put, path, body.to_s, build_request_headers(headers, :put, self.site.merge(path))) } end # Executes a POST request. # Used to create new resources. - def post(path, body = '', headers = {}) + def post(path, body = "", headers = {}) with_auth { request(:post, path, body.to_s, build_request_headers(headers, :post, 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). @@ -127,46 +130,46 @@ raise SSLError.new(e.message) end # Handles response and error codes from the remote service. def handle_response(response) + if response.respond_to?(:header) && (response.header["content-encoding"] == 'gzip') begin response.instance_variable_set('@body', ActiveSupport::Gzip.decompress(response.body)) rescue Exception => e raise(BadRequest.new(response)) end end - 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}")) + 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. @@ -174,11 +177,13 @@ configure_http(new_http) end def new_http if @proxy - Net::HTTP.new(@site.host, @site.port, @proxy.host, @proxy.port, @proxy.user, @proxy.password) + 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 @@ -187,22 +192,21 @@ # 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 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 - # Default to no cert verification (WTF? FIXME) - http.verify_mode = OpenSSL::SSL::VERIFY_NONE - # All the SSL options have corresponding http settings. @ssl_options.each { |key, value| http.send "#{key}=", value } end end end @@ -223,22 +227,24 @@ def with_auth retried ||= false yield rescue UnauthorizedAccess => e raise if retried || auth_type != :digest - @response_auth_header = e.response['WWW-Authenticate'] + @response_auth_header = e.response["WWW-Authenticate"] retried = true retry end def authorization_header(http_method, uri) if @user || @password if auth_type == :digest - { 'Authorization' => digest_auth_header(http_method, uri) } + { "Authorization" => digest_auth_header(http_method, uri) } else - { 'Authorization' => 'Basic ' + ["#{@user}:#{@password}"].pack('m').delete("\r\n") } + { "Authorization" => "Basic " + ["#{@user}:#{@password}"].pack("m").delete("\r\n") } end + elsif @bearer_token + { "Authorization" => "Bearer #{@bearer_token}" } else {} end end @@ -249,12 +255,12 @@ 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(":")) + 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 def client_nonce Digest::MD5.hexdigest("%x" % (Time.now.to_i + rand(65535))) @@ -267,28 +273,31 @@ end params end 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(", ") + 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 def http_format_header(http_method) - {HTTP_FORMAT_HEADER_NAMES[http_method] => format.mime_type} + { HTTP_FORMAT_HEADER_NAMES[http_method] => format.mime_type } end 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 + auth_type.in?([:basic, :digest, :bearer]) ? auth_type : :basic end end end