Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Xml/attachments options bugfix, MTOM support for attachments, better Nori options passthrough #944

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
.byebug_history
.DS_Store
.yardoc
.ruby-version
doc
rdox
coverage
Expand Down
41 changes: 29 additions & 12 deletions lib/savon/builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,23 @@ def pretty
end

def build_document
xml_result = build_xml
# check if xml was already provided
if @locals.include? :xml
xml_result = @locals[:xml]
else
xml_result = build_xml

# if we have a signature sign the document
if @signature
@signature.document = xml_result
# if we have a signature sign the document
if @signature
@signature.document = xml_result

2.times do
@header = nil
@signature.document = build_xml
end
2.times do
@header = nil
@signature.document = build_xml
end

xml_result = @signature.document
xml_result = @signature.document
end
end

# if there are attachments for the request, we should build a multipart message according to
Expand All @@ -70,7 +75,6 @@ def body_attributes
end

def to_s
return @locals[:xml] if @locals.include? :xml
build_document
end

Expand Down Expand Up @@ -254,15 +258,28 @@ def build_multipart_message(message_xml)

# the mail.body.encoded algorithm reorders the parts, default order is [ "text/plain", "text/enriched", "text/html" ]
# should redefine the sort order, because the soap request xml should be the first
multipart_message.body.set_sort_order [ "text/xml" ]
multipart_message.body.set_sort_order ['application/xop+xml', 'text/xml']

multipart_message.body.encoded(multipart_message.content_transfer_encoding)
end

def init_multipart_message(message_xml)
multipart_message = Mail.new

# MTOM differs from general SOAP attachments:
# 1. binary encoding
# 2. application/xop+xml mime type
if @locals[:mtom]
type = "application/xop+xml; charset=#{@globals[:encoding]}; type=\"text/xml\""

multipart_message.transport_encoding = 'binary'
message_xml.force_encoding('BINARY')
else
type = 'text/xml'
end

xml_part = Mail::Part.new do
content_type 'text/xml'
content_type type
body message_xml
# in Content-Type the start parameter is recommended (RFC 2387)
content_id '<soap-request-body@soap>'
Expand Down
5 changes: 3 additions & 2 deletions lib/savon/operation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,10 @@ def build_request(builder)
request.body = builder.to_s

if builder.multipart
request.gzip
type = @locals[:mtom] ? 'application/xop+xml"; start-info="text/xml' : SOAP_REQUEST_TYPE[@globals[:soap_version]]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: this MTOM mime type could also be extracted to a constant, so that both branches are harmonious, looking uniform.

request.gzip unless @locals[:mtom]
request.headers["Content-Type"] = ["multipart/related",
"type=\"#{SOAP_REQUEST_TYPE[@globals[:soap_version]]}\"",
"type=\"#{type}\"",
"start=\"#{builder.multipart[:start]}\"",
"boundary=\"#{builder.multipart[:multipart_boundary]}\""].join("; ")
request.headers["MIME-Version"] = "1.0"
Expand Down
13 changes: 12 additions & 1 deletion lib/savon/options.rb
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,8 @@ def initialize(options = {})
defaults = {
:advanced_typecasting => true,
:response_parser => :nokogiri,
:multipart => false
:multipart => false,
:mtom => false
}

super defaults.merge(options)
Expand Down Expand Up @@ -452,6 +453,11 @@ def attachments(attachments)
@options[:attachments] = attachments
end

# Instruct Savon to send attachments using MTOM https://www.w3.org/TR/soap12-mtom/
def mtom(mtom)
@options[:mtom] = mtom
end

# Value of the SOAPAction HTTP header.
def soap_action(soap_action)
@options[:soap_action] = soap_action
Expand All @@ -477,6 +483,11 @@ def response_parser(parser)
@options[:response_parser] = parser
end

# Pass already configured Nori instance.
def nori(nori)
@options[:nori] = nori
end

# Instruct Savon to create a multipart response if available.
def multipart(multipart)
@options[:multipart] = multipart
Expand Down
2 changes: 1 addition & 1 deletion lib/savon/request_logger.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def headers_to_log(headers)
end

def body_to_log(body)
LogMessage.new(body, @globals[:filters], @globals[:pretty_print_xml]).to_s
LogMessage.new(body, @globals[:filters], @globals[:pretty_print_xml]).to_s.force_encoding(@globals[:encoding])
end

end
Expand Down
22 changes: 9 additions & 13 deletions lib/savon/response.rb
Original file line number Diff line number Diff line change
Expand Up @@ -142,20 +142,16 @@ def xml_namespaces
end

def nori
return @nori if @nori
return @locals[:nori] if @locals[:nori]

nori_options = {
: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]
}

non_nil_nori_options = nori_options.reject { |_, value| value.nil? }
@nori = Nori.new(non_nil_nori_options)
@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