require "nori"
require "savon/soap_fault"
require "savon/http_error"
module Savon
class Response
CRLF = /\r\n/
WSP = /[#{%Q|\x9\x20|}]/
def initialize(http, globals, locals)
@http = http
@globals = globals
@locals = locals
@attachments = []
@xml = ''
@has_parsed_body = false
build_soap_and_http_errors!
raise_soap_and_http_errors! if @globals[:raise_errors]
end
attr_reader :http, :globals, :locals, :soap_fault, :http_error
def success?
!soap_fault? && !http_error?
end
alias_method :successful?, :success?
def soap_fault?
SOAPFault.present?(@http, xml)
end
def http_error?
HTTPError.present? @http
end
def
find('Header')
end
def body
find('Body')
end
alias_method :to_hash, :body
def to_array(*path)
result = path.inject body do |memo, key|
return [] if memo[key].nil?
memo[key]
end
result.kind_of?(Array) ? result.compact : [result].compact
end
def full_hash
@full_hash ||= nori.parse(xml)
end
def xml
if multipart?
parse_body unless @has_parsed_body
@xml
else
@http.body
end
end
alias_method :to_xml, :xml
alias_method :to_s, :xml
def doc
@doc ||= Nokogiri.XML(xml)
end
def xpath(path, namespaces = nil)
doc.xpath(path, namespaces || xml_namespaces)
end
def find(*path)
envelope = nori.find(full_hash, 'Envelope')
raise_invalid_response_error! unless envelope.is_a?(Hash)
nori.find(envelope, *path)
end
def attachments
if multipart?
parse_body unless @has_parsed_body
@attachments
else
[]
end
end
def multipart?
!(http.['content-type'] =~ /^multipart/im).nil?
end
private
def boundary
return unless multipart?
Mail::Field.new('content-type', http.['content-type']).parameters['boundary']
end
def parse_body
http.body.force_encoding Encoding::ASCII_8BIT
parts = http.body.split(/(?:\A|\r\n)--#{Regexp.escape(boundary)}(?=(?:--)?\s*$)/)
parts[1..-1].to_a.each_with_index do |part, index|
, body_part = part.lstrip.split(/#{CRLF}#{CRLF}|#{CRLF}#{WSP}*#{CRLF}(?!#{WSP})/m, 2)
section = Mail::Part.new(
body: body_part
)
section. =
if index == 0
@xml = section.body.to_s
else
@attachments << section
end
end
@has_parsed_body = true
end
def build_soap_and_http_errors!
@soap_fault = SOAPFault.new(@http, nori, xml) if soap_fault?
@http_error = HTTPError.new(@http) if http_error?
end
def raise_soap_and_http_errors!
raise soap_fault if soap_fault?
raise http_error if http_error?
end
def raise_invalid_response_error!
raise InvalidResponseError, "Unable to parse response body:\n" + xml.inspect
end
def xml_namespaces
@xml_namespaces ||= doc.collect_namespaces
end
def nori
return @locals[:nori] if @locals[:nori]
@nori ||= Nori.new({
:delete_namespace_attributes => @globals[:delete_namespace_attributes],
:strip_namespaces => @globals[:strip_namespaces],
:convert_tags_to => @globals[:convert_response_tags_to],
:convert_attributes_to => @globals[:convert_attributes_to],
:advanced_typecasting => @locals[:advanced_typecasting],
:parser => @locals[:response_parser]
}.reject { |_, value| value.nil? })
end
end
end