require "zlib" require "stringio" require "rack/utils" require "httpi/dime" require "httpi/cookie" module HTTPI # = HTTPI::Response # # Represents an HTTP response and contains various response details. class Response # Range of HTTP response codes considered to be successful. SuccessfulResponseCodes = 200..299 # HTTP response codes considered to be a redirect. RedirectResponseCodes = [301, 302, 303, 307, 308] # Initializer expects an HTTP response +code+, +headers+ and +body+. def initialize(code, headers, body) self.code = code.to_i self.headers = Rack::Utils::HeaderHash.new(headers) self.raw_body = body end attr_accessor :code, :headers, :raw_body attr_writer :attachments # Returns whether the HTTP response is considered successful. def error? !SuccessfulResponseCodes.include? code.to_i end # Returns whether the HTTP response is a multipart response. def multipart? !!(headers["Content-Type"] =~ /^multipart/i) end # Returns a list of cookies from the response. def cookies @cookies ||= Cookie.list_from_headers(headers) end # Returns any DIME attachments. def attachments @body ||= nil decode_body unless @body @attachments ||= [] end # Returns the HTTP response body. def body @body ||= nil decode_body unless @body @body end attr_writer :body private def decode_body return @body = "" if !raw_body || raw_body.empty? body = gzipped_response? ? decoded_gzip_body : raw_body @body = dime_response? ? decoded_dime_body(body) : body end # Returns whether the response is gzipped. def gzipped_response? headers["Content-Encoding"] == "gzip" end # Returns whether this is a DIME response. def dime_response? headers["Content-Type"] == "application/dime" end # Returns the gzip decoded response body. def decoded_gzip_body unless gzip = Zlib::GzipReader.new(StringIO.new(raw_body)) raise ArgumentError, "Unable to create Zlib::GzipReader" end gzip.read ensure gzip.close if gzip end # Returns the DIME decoded response body. def decoded_dime_body(body = nil) dime = Dime.new(body || raw_body) self.attachments = dime.binary_records dime.xml_records.first.data end end end