+++ /dev/null
-require 'net/https'
-require 'date'
-require 'time'
-require 'uri'
-require 'benchmark'
-
-module ActiveResource
- class ConnectionError < StandardError # :nodoc:
- attr_reader :response
-
- def initialize(response, message = nil)
- @response = response
- @message = message
- end
-
- def to_s
- "Failed with #{response.code} #{response.message if response.respond_to?(:message)}"
- end
- end
-
- # Raised when a Timeout::Error occurs.
- class TimeoutError < ConnectionError
- def initialize(message)
- @message = message
- end
- def to_s; @message ;end
- end
-
- # 3xx Redirection
- class Redirection < ConnectionError # :nodoc:
- def to_s; response['Location'] ? "#{super} => #{response['Location']}" : super; end
- end
-
- # 4xx Client Error
- class ClientError < ConnectionError; end # :nodoc:
-
- # 400 Bad Request
- class BadRequest < ClientError; end # :nodoc
-
- # 401 Unauthorized
- class UnauthorizedAccess < ClientError; end # :nodoc
-
- # 403 Forbidden
- class ForbiddenAccess < ClientError; end # :nodoc
-
- # 404 Not Found
- class ResourceNotFound < ClientError; end # :nodoc:
-
- # 409 Conflict
- class ResourceConflict < ClientError; end # :nodoc:
-
- # 5xx Server Error
- class ServerError < ConnectionError; end # :nodoc:
-
- # 405 Method Not Allowed
- class MethodNotAllowed < ClientError # :nodoc:
- def allowed_methods
- @response['Allow'].split(',').map { |verb| verb.strip.downcase.to_sym }
- end
- end
-
- # 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',
- :delete => 'Accept'
- }
-
- attr_reader :site, :user, :password, :timeout
- attr_accessor :format
-
- 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[:xml])
- raise ArgumentError, 'Missing site URI' unless site
- @user = @password = nil
- self.site = site
- self.format = format
- end
-
- # Set URI for remote service.
- def site=(site)
- @site = site.is_a?(URI) ? site : URI.parse(site)
- @user = URI.decode(@site.user) if @site.user
- @password = URI.decode(@site.password) if @site.password
- end
-
- # Set user for remote service.
- def user=(user)
- @user = user
- end
-
- # Set password for remote service.
- def password=(password)
- @password = password
- end
-
- # Set the number of seconds after which HTTP requests to the remote service should time out.
- def timeout=(timeout)
- @timeout = timeout
- end
-
- # Execute a GET request.
- # Used to get (find) resources.
- def get(path, headers = {})
- format.decode(request(:get, path, build_request_headers(headers, :get)).body)
- end
-
- # Execute a DELETE request (see HTTP protocol documentation if unfamiliar).
- # Used to delete resources.
- def delete(path, headers = {})
- request(:delete, path, build_request_headers(headers, :delete))
- end
-
- # Execute a PUT request (see HTTP protocol documentation if unfamiliar).
- # Used to update resources.
- def put(path, body = '', headers = {})
- request(:put, path, body.to_s, build_request_headers(headers, :put))
- end
-
- # Execute a POST request.
- # Used to create new resources.
- def post(path, body = '', headers = {})
- request(:post, path, body.to_s, build_request_headers(headers, :post))
- end
-
- # Execute a HEAD request.
- # Used to obtain meta-information about resources, such as whether they exist and their size (via response headers).
- def head(path, headers = {})
- request(:head, path, build_request_headers(headers))
- end
-
-
- private
- # Makes request to remote service.
- def request(method, path, *arguments)
- logger.info "#{method.to_s.upcase} #{site.scheme}://#{site.host}:#{site.port}#{path}" if logger
- result = nil
- ms = Benchmark.ms { result = http.send(method, path, *arguments) }
- logger.info "--> %d %s (%d %.0fms)" % [result.code, result.message, result.body ? result.body.length : 0, ms] if logger
- handle_response(result)
- rescue Timeout::Error => e
- raise TimeoutError.new(e.message)
- end
-
- # Handles response and error codes from remote service.
- def handle_response(response)
- case response.code.to_i
- when 301,302
- 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 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
- # remote service and resources.
- def http
- http = Net::HTTP.new(@site.host, @site.port)
- http.use_ssl = @site.is_a?(URI::HTTPS)
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE if http.use_ssl
- http.read_timeout = @timeout if @timeout # If timeout is not set, the default Net::HTTP timeout (60s) is used.
- http
- end
-
- def default_header
- @default_header ||= {}
- end
-
- # Builds headers for request to remote service.
- def build_request_headers(headers, http_method=nil)
- authorization_header.update(default_header).update(http_format_header(http_method)).update(headers)
- end
-
- # Sets authorization header
- def authorization_header
- (@user || @password ? { 'Authorization' => 'Basic ' + ["#{@user}:#{ @password}"].pack('m').delete("\r\n") } : {})
- end
-
- def http_format_header(http_method)
- {HTTP_FORMAT_HEADER_NAMES[http_method] => format.mime_type}
- end
-
- def logger #:nodoc:
- Base.logger
- end
- end
-end