lib/rets4r/client.rb in rets4r-0.8.2 vs lib/rets4r/client.rb in rets4r-0.8.3
- old
+ new
@@ -37,11 +37,34 @@
DEFAULT_USER_AGENT = 'RETS4R/0.8.2'
DEFAULT_RETS_VERSION = '1.7'
SUPPORTED_RETS_VERSIONS = ['1.5', '1.7']
CAPABILITY_LIST = ['Action', 'ChangePassword', 'GetObject', 'Login', 'LoginComplete', 'Logout', 'Search', 'GetMetadata', 'Update']
SUPPORTED_PARSERS = [] # This will be populated by parsers as they load
-
+
+ # These are the response messages as defined in the RETS 1.5e2 and 1.7d6 specifications.
+ # Provided for convenience and are used by the HTTPError class to provide more useful
+ # messages.
+ RETS_HTTP_MESSAGES = {
+ '200' => 'Operation successful.',
+ '400' => 'The request could not be understood by the server due to malformed syntax.',
+ '401' => 'Either the header did not contain an acceptable Authorization or the username/password was invalid. The server response MUST include a WWW-Authenticate header field.',
+ '402' => 'The requested transaction requires a payment which could not be authorized.',
+ '403' => 'The server understood the request, but is refusing to fulfill it.',
+ '404' => 'The server has not found anything matching the Request-URI.',
+ '405' => 'The method specified in the Request-Line is not allowed for the resource identified by the Request-URI.',
+ '406' => 'The resource identified by the request is only capable of generating response entities which have content characteristics not acceptable according to the accept headers sent in the request.',
+ '408' => 'The client did not produce a request within the time that the server was prepared to wait.',
+ '411' => 'The server refuses to accept the request without a defined Content-Length.',
+ '412' => 'Transaction not permitted at this point in the session.',
+ '413' => 'The server is refusing to process a request because the request entity is larger than the server is willing or able to process.',
+ '414' => 'The server is refusing to service the request because the Request-URI is longer than the server is willing to interpret. This error usually only occurs for a GET method.',
+ '500' => 'The server encountered an unexpected condition which prevented it from fulfilling the request.',
+ '501' => 'The server does not support the functionality required to fulfill the request.',
+ '503' => 'The server is currently unable to handle the request due to a temporary overloading or maintenance of the server.',
+ '505' => 'The server does not support, or refuses to support, the HTTP protocol version that was used in the request message.',
+ }
+
attr_accessor :mimemap, :logger
# We load our parsers here so that they can modify the client class appropriately. Because
# the default parser will be the first parser to list itself in the DEFAULT_PARSER array,
# we need to require them in the order of preference. Hence, XMLParser is loaded first because
@@ -80,10 +103,39 @@
if block_given?
yield self
end
end
+ # Assigns a block that will be called just before the request is sent.
+ # This block must accept three parameters:
+ # * self
+ # * Net::HTTP instance
+ # * Hash of headers
+ #
+ # The block's return value will be ignored. If you want to prevent the request
+ # to go through, raise an exception.
+ #
+ # == Example
+ #
+ # client = RETS4R::Client.new(...)
+ # # Make a new pre_request_block that calculates the RETS-UA-Authorization header.
+ # client.set_pre_request_block do |rets, http, headers|
+ # a1 = Digest::MD5.hexdigest([headers["User-Agent"], @password].join(":"))
+ # if headers.has_key?("Cookie") then
+ # cookie = headers["Cookie"].split(";").map(&:strip).select {|c| c =~ /rets-session-id/i}
+ # cookie = cookie ? cookie.split("=").last : ""
+ # else
+ # cookie = ""
+ # end
+ #
+ # parts = [a1, "", cookie, headers["RETS-Version"]]
+ # headers["RETS-UA-Authorization"] = "Digest " + Digest::MD5.hexdigest(parts.join(":"))
+ # end
+ def set_pre_request_block(&block)
+ @pre_request_block = block
+ end
+
# We only allow external read access to URLs because they are internally set based on the
# results of various queries.
def urls
@urls
end
@@ -341,13 +393,12 @@
'Content-Type' => response['content-type'],
'Object-ID' => response['Object-ID'],
'Content-ID' => response['Content-ID']
}
- if response['Content-Length'].to_i > 100
+ if response['Transfer-Encoding'].to_s.downcase == "chunked" || response['Content-Length'].to_i > 100 then
data_object = DataObject.new(info, response.body)
-
if block_given?
yield data_object
results += 1
else
results << data_object
@@ -464,10 +515,12 @@
end
headers = @headers
headers.merge(header) unless header.empty?
+ @pre_request_block.call(self, http, headers) if @pre_request_block
+
logger.debug(headers.inspect) if logger
@semaphore.unlock
response = http.get(uri, headers)
@@ -475,10 +528,14 @@
@semaphore.lock
if response.code == '401'
# Authentication is required
raise AuthRequired
+ elsif response.code.to_i >= 300
+ # We have a non-successful response that we cannot handle
+ @semaphore.unlock if @semaphore.locked?
+ raise HTTPError.new(response)
else
cookies = []
if set_cookies = response.get_fields('set-cookie') then
set_cookies.each do |cookie|
cookies << cookie.split(";").first
@@ -492,10 +549,13 @@
if retry_auth > 0
retry_auth -= 1
set_header('Authorization', Auth.authenticate(response, @username, @password, url.path, method, @headers['RETS-Request-ID'], get_user_agent, @nc))
retry
+ else
+ @semaphore.unlock if @semaphore.locked?
+ raise LoginError.new(response.message)
end
end
logger.debug(response.body) if logger
end
@@ -541,9 +601,39 @@
attr_accessor :file
end
# The client does not currently support a specified action.
class Unsupported < ClientException
+ end
+
+ # The HTTP response returned by the server indicates that there was an error processing
+ # the request and the client cannot continue on its own without intervention.
+ class HTTPError < ClientException
+ attr_accessor :http_response
+
+ # Takes a HTTPResponse object
+ def initialize(http_response)
+ self.http_response = http_response
+ end
+
+ # Shorthand for calling HTTPResponse#code
+ def code
+ http_response.code
+ end
+
+ # Shorthand for calling HTTPResponse#message
+ def message
+ http_response.message
+ end
+
+ # Returns the RETS specification message for the HTTP response code
+ def rets_message
+ Client::RETS_HTTP_MESSAGES[code]
+ end
+
+ def to_s
+ "#{code} #{message}: #{rets_message}"
+ end
end
# A general RETS level exception was encountered. This would include HTTP and RETS
# specification level errors as well as informative mishaps such as authentication being
# required for access.