savon 2.12.1 → 2.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/CHANGELOG.md +87 -73
- data/CONTRIBUTING.md +15 -19
- data/Gemfile +2 -7
- data/README.md +14 -16
- data/coverage/assets/0.12.3/DataTables-1.10.20/images/sort_asc.png +0 -0
- data/coverage/assets/0.12.3/DataTables-1.10.20/images/sort_asc_disabled.png +0 -0
- data/coverage/assets/0.12.3/DataTables-1.10.20/images/sort_both.png +0 -0
- data/coverage/assets/0.12.3/DataTables-1.10.20/images/sort_desc.png +0 -0
- data/coverage/assets/0.12.3/DataTables-1.10.20/images/sort_desc_disabled.png +0 -0
- data/coverage/assets/0.12.3/application.css +1 -0
- data/coverage/assets/0.12.3/application.js +7 -0
- data/coverage/assets/0.12.3/colorbox/border.png +0 -0
- data/coverage/assets/0.12.3/colorbox/controls.png +0 -0
- data/coverage/assets/0.12.3/colorbox/loading.gif +0 -0
- data/coverage/assets/0.12.3/colorbox/loading_background.png +0 -0
- data/coverage/assets/0.12.3/favicon_green.png +0 -0
- data/coverage/assets/0.12.3/favicon_red.png +0 -0
- data/coverage/assets/0.12.3/favicon_yellow.png +0 -0
- data/coverage/assets/0.12.3/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/coverage/assets/0.12.3/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/coverage/assets/0.12.3/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/coverage/assets/0.12.3/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/coverage/assets/0.12.3/images/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/coverage/assets/0.12.3/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/coverage/assets/0.12.3/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- data/coverage/assets/0.12.3/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/coverage/assets/0.12.3/images/ui-icons_222222_256x240.png +0 -0
- data/coverage/assets/0.12.3/images/ui-icons_2e83ff_256x240.png +0 -0
- data/coverage/assets/0.12.3/images/ui-icons_454545_256x240.png +0 -0
- data/coverage/assets/0.12.3/images/ui-icons_888888_256x240.png +0 -0
- data/coverage/assets/0.12.3/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/coverage/assets/0.12.3/loading.gif +0 -0
- data/coverage/assets/0.12.3/magnify.png +0 -0
- data/coverage/index.html +21518 -0
- data/lib/savon/block_interface.rb +1 -0
- data/lib/savon/builder.rb +95 -29
- data/lib/savon/client.rb +1 -0
- data/lib/savon/core_ext/string.rb +1 -0
- data/lib/savon/header.rb +2 -6
- data/lib/savon/http_error.rb +4 -4
- data/lib/savon/log_message.rb +1 -0
- data/lib/savon/message.rb +1 -0
- data/lib/savon/mock/expectation.rb +1 -0
- data/lib/savon/mock/spec_helper.rb +1 -0
- data/lib/savon/mock.rb +1 -0
- data/lib/savon/model.rb +1 -0
- data/lib/savon/operation.rb +20 -18
- data/lib/savon/options.rb +56 -0
- data/lib/savon/qualified_message.rb +3 -2
- data/lib/savon/request.rb +5 -0
- data/lib/savon/request_logger.rb +8 -2
- data/lib/savon/response.rb +48 -1
- data/lib/savon/soap_fault.rb +1 -0
- data/lib/savon/version.rb +2 -1
- data/lib/savon.rb +1 -0
- data/savon.gemspec +9 -8
- data/spec/integration/support/application.rb +33 -1
- data/spec/integration/support/server.rb +1 -0
- data/spec/integration/zipcode_example_spec.rb +5 -8
- data/spec/savon/builder_spec.rb +2 -1
- data/spec/savon/client_spec.rb +5 -4
- data/spec/savon/core_ext/string_spec.rb +2 -1
- data/spec/savon/features/message_tag_spec.rb +2 -1
- data/spec/savon/http_error_spec.rb +9 -1
- data/spec/savon/log_message_spec.rb +2 -1
- data/spec/savon/message_spec.rb +2 -11
- data/spec/savon/mock_spec.rb +2 -1
- data/spec/savon/model_spec.rb +2 -1
- data/spec/savon/multipart_request_spec.rb +46 -0
- data/spec/savon/observers_spec.rb +2 -1
- data/spec/savon/operation_spec.rb +20 -43
- data/spec/savon/options_spec.rb +40 -1
- data/spec/savon/qualified_message_spec.rb +2 -1
- data/spec/savon/request_logger_spec.rb +2 -1
- data/spec/savon/request_spec.rb +47 -6
- data/spec/savon/response_spec.rb +2 -1
- data/spec/savon/soap_fault_spec.rb +2 -1
- data/spec/savon/softlayer_spec.rb +3 -2
- data/spec/spec_helper.rb +5 -4
- data/spec/support/adapters.rb +1 -0
- data/spec/support/endpoint.rb +1 -0
- data/spec/support/fixture.rb +1 -0
- data/spec/support/integration.rb +1 -0
- data/spec/support/stdout.rb +1 -0
- metadata +81 -30
- data/.travis.yml +0 -26
- data/donate.png +0 -0
- data/spec/integration/centra_spec.rb +0 -67
- data/spec/integration/email_example_spec.rb +0 -32
- data/spec/integration/random_quote_spec.rb +0 -23
- data/spec/integration/ratp_example_spec.rb +0 -28
- data/spec/integration/stockquote_example_spec.rb +0 -34
- data/spec/integration/temperature_example_spec.rb +0 -46
    
        data/lib/savon/builder.rb
    CHANGED
    
    | @@ -1,3 +1,4 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 1 2 | 
             
            require "savon/header"
         | 
| 2 3 | 
             
            require "savon/message"
         | 
| 3 4 | 
             
            require "nokogiri"
         | 
| @@ -6,6 +7,7 @@ require "gyoku" | |
| 6 7 |  | 
| 7 8 | 
             
            module Savon
         | 
| 8 9 | 
             
              class Builder
         | 
| 10 | 
            +
                attr_reader :multipart
         | 
| 9 11 |  | 
| 10 12 | 
             
                SCHEMA_TYPES = {
         | 
| 11 13 | 
             
                  "xmlns:xsd" => "http://www.w3.org/2001/XMLSchema",
         | 
| @@ -36,14 +38,7 @@ module Savon | |
| 36 38 | 
             
                end
         | 
| 37 39 |  | 
| 38 40 | 
             
                def build_document
         | 
| 39 | 
            -
                  xml_result =  | 
| 40 | 
            -
                    tag(xml, :Header, header_attributes) { xml << header.to_s } unless header.empty?
         | 
| 41 | 
            -
                    if @globals[:no_message_tag]
         | 
| 42 | 
            -
                      tag(xml, :Body, body_attributes) { xml << message.to_s }
         | 
| 43 | 
            -
                    else
         | 
| 44 | 
            -
                      tag(xml, :Body, body_attributes) { xml.tag!(*namespaced_message_tag) { xml << body_message } }
         | 
| 45 | 
            -
                    end
         | 
| 46 | 
            -
                  end
         | 
| 41 | 
            +
                  xml_result = build_xml
         | 
| 47 42 |  | 
| 48 43 | 
             
                  # if we have a signature sign the document
         | 
| 49 44 | 
             
                  if @signature
         | 
| @@ -51,20 +46,19 @@ module Savon | |
| 51 46 |  | 
| 52 47 | 
             
                    2.times do
         | 
| 53 48 | 
             
                      @header = nil
         | 
| 54 | 
            -
                      @signature.document =  | 
| 55 | 
            -
                        tag(xml, :Header, header_attributes) { xml << header.to_s } unless header.empty?
         | 
| 56 | 
            -
                        if @globals[:no_message_tag]
         | 
| 57 | 
            -
                          tag(xml, :Body, body_attributes) { xml << message.to_s }
         | 
| 58 | 
            -
                        else
         | 
| 59 | 
            -
                          tag(xml, :Body, body_attributes) { xml.tag!(*namespaced_message_tag) { xml << message.to_s } }
         | 
| 60 | 
            -
                        end
         | 
| 61 | 
            -
                      end
         | 
| 49 | 
            +
                      @signature.document = build_xml
         | 
| 62 50 | 
             
                    end
         | 
| 63 51 |  | 
| 64 52 | 
             
                    xml_result = @signature.document
         | 
| 65 53 | 
             
                  end
         | 
| 66 54 |  | 
| 67 | 
            -
                   | 
| 55 | 
            +
                  # if there are attachments for the request, we should build a multipart message according to
         | 
| 56 | 
            +
                  # https://www.w3.org/TR/SOAP-attachments
         | 
| 57 | 
            +
                  if @locals[:attachments]
         | 
| 58 | 
            +
                    build_multipart_message(xml_result)
         | 
| 59 | 
            +
                  else
         | 
| 60 | 
            +
                    xml_result
         | 
| 61 | 
            +
                  end
         | 
| 68 62 | 
             
                end
         | 
| 69 63 |  | 
| 70 64 | 
             
                def header_attributes
         | 
| @@ -117,15 +111,21 @@ module Savon | |
| 117 111 | 
             
                  @namespaces ||= begin
         | 
| 118 112 | 
             
                    namespaces = SCHEMA_TYPES.dup
         | 
| 119 113 |  | 
| 120 | 
            -
                     | 
| 121 | 
            -
             | 
| 122 | 
            -
             | 
| 123 | 
            -
                      namespaces["xmlns:#{namespace_identifier}"] = @globals[:namespace] || @wsdl.namespace
         | 
| 124 | 
            -
                    end
         | 
| 114 | 
            +
                    # check namespace_identifier
         | 
| 115 | 
            +
                    namespaces["xmlns#{namespace_identifier.nil? ? '' : ":#{namespace_identifier}"}"] =
         | 
| 116 | 
            +
                      @globals[:namespace] || @wsdl.namespace
         | 
| 125 117 |  | 
| 126 | 
            -
                     | 
| 127 | 
            -
                     | 
| 128 | 
            -
             | 
| 118 | 
            +
                    # check env_namespace
         | 
| 119 | 
            +
                    namespaces["xmlns#{env_namespace && env_namespace != "" ? ":#{env_namespace}" : ''}"] =
         | 
| 120 | 
            +
                      SOAP_NAMESPACE[@globals[:soap_version]]
         | 
| 121 | 
            +
             | 
| 122 | 
            +
                    if @wsdl&.document
         | 
| 123 | 
            +
                      @wsdl.parser.namespaces.each do |identifier, path|
         | 
| 124 | 
            +
                        next if namespaces.key?("xmlns:#{identifier}")
         | 
| 125 | 
            +
             | 
| 126 | 
            +
                        namespaces["xmlns:#{identifier}"] = path
         | 
| 127 | 
            +
                      end
         | 
| 128 | 
            +
                    end
         | 
| 129 129 |  | 
| 130 130 | 
             
                    namespaces
         | 
| 131 131 | 
             
                  end
         | 
| @@ -162,18 +162,20 @@ module Savon | |
| 162 162 | 
             
                    break if @locals[:message].nil?
         | 
| 163 163 | 
             
                    message_locals = @locals[:message][message.snakecase.to_sym]
         | 
| 164 164 | 
             
                    message_content = Message.new(message_tag, namespace_identifier, @types, @used_namespaces, message_locals, :unqualified, @globals[:convert_request_keys_to], @globals[:unwrap]).to_s
         | 
| 165 | 
            -
                    messages  | 
| 165 | 
            +
                    messages += "<#{message} xsi:type=\"#{type.join(':')}\">#{message_content}</#{message}>"
         | 
| 166 166 | 
             
                  end
         | 
| 167 167 | 
             
                  messages
         | 
| 168 168 | 
             
                end
         | 
| 169 169 |  | 
| 170 170 | 
             
                def message_tag
         | 
| 171 | 
            -
                   | 
| 171 | 
            +
                  wsdl_tag_name = @wsdl.document? && @wsdl.soap_input(@operation_name.to_sym)
         | 
| 172 | 
            +
             | 
| 173 | 
            +
                  message_tag = wsdl_tag_name.keys.first if wsdl_tag_name.is_a?(Hash)
         | 
| 172 174 | 
             
                  message_tag ||= @locals[:message_tag]
         | 
| 173 | 
            -
                  message_tag ||=  | 
| 175 | 
            +
                  message_tag ||= wsdl_tag_name
         | 
| 174 176 | 
             
                  message_tag ||= Gyoku.xml_tag(@operation_name, :key_converter => @globals[:convert_request_keys_to])
         | 
| 175 177 |  | 
| 176 | 
            -
                   | 
| 178 | 
            +
                  message_tag.to_sym
         | 
| 177 179 | 
             
                end
         | 
| 178 180 |  | 
| 179 181 | 
             
                def message_attributes
         | 
| @@ -227,5 +229,69 @@ module Savon | |
| 227 229 | 
             
                  end
         | 
| 228 230 | 
             
                end
         | 
| 229 231 |  | 
| 232 | 
            +
                def build_xml
         | 
| 233 | 
            +
                  tag(builder, :Envelope, namespaces_with_globals) do |xml|
         | 
| 234 | 
            +
                    tag(xml, :Header, header_attributes) { xml << header.to_s } unless header.empty?
         | 
| 235 | 
            +
                    tag(xml, :Body, body_attributes) do
         | 
| 236 | 
            +
                      if @globals[:no_message_tag]
         | 
| 237 | 
            +
                        xml << message.to_s
         | 
| 238 | 
            +
                      else
         | 
| 239 | 
            +
                        xml.tag!(*namespaced_message_tag) { xml << body_message }
         | 
| 240 | 
            +
                      end
         | 
| 241 | 
            +
                    end
         | 
| 242 | 
            +
                  end
         | 
| 243 | 
            +
                end
         | 
| 244 | 
            +
             | 
| 245 | 
            +
                def build_multipart_message(message_xml)
         | 
| 246 | 
            +
                  multipart_message = init_multipart_message(message_xml)
         | 
| 247 | 
            +
                  add_attachments_to_multipart_message(multipart_message)
         | 
| 248 | 
            +
             | 
| 249 | 
            +
                  multipart_message.ready_to_send!
         | 
| 250 | 
            +
             | 
| 251 | 
            +
                  # the mail.body.encoded algorithm reorders the parts, default order is [ "text/plain", "text/enriched", "text/html" ]
         | 
| 252 | 
            +
                  # should redefine the sort order, because the soap request xml should be the first
         | 
| 253 | 
            +
                  multipart_message.body.set_sort_order [ "text/xml" ]
         | 
| 254 | 
            +
             | 
| 255 | 
            +
                  multipart_message.body.encoded(multipart_message.content_transfer_encoding)
         | 
| 256 | 
            +
                end
         | 
| 257 | 
            +
             | 
| 258 | 
            +
                def init_multipart_message(message_xml)
         | 
| 259 | 
            +
                  multipart_message = Mail.new
         | 
| 260 | 
            +
                  xml_part = Mail::Part.new do
         | 
| 261 | 
            +
                    content_type 'text/xml'
         | 
| 262 | 
            +
                    body message_xml
         | 
| 263 | 
            +
                    # in Content-Type the start parameter is recommended (RFC 2387)
         | 
| 264 | 
            +
                    content_id '<soap-request-body@soap>'
         | 
| 265 | 
            +
                  end
         | 
| 266 | 
            +
                  multipart_message.add_part xml_part
         | 
| 267 | 
            +
             | 
| 268 | 
            +
                  #request.headers["Content-Type"] = "multipart/related; boundary=\"#{multipart_message.body.boundary}\"; type=\"text/xml\"; start=\"#{xml_part.content_id}\""
         | 
| 269 | 
            +
                  @multipart = {
         | 
| 270 | 
            +
                    multipart_boundary: multipart_message.body.boundary,
         | 
| 271 | 
            +
                    start: xml_part.content_id,
         | 
| 272 | 
            +
                  }
         | 
| 273 | 
            +
             | 
| 274 | 
            +
                  multipart_message
         | 
| 275 | 
            +
                end
         | 
| 276 | 
            +
             | 
| 277 | 
            +
                def add_attachments_to_multipart_message(multipart_message)
         | 
| 278 | 
            +
                  if @locals[:attachments].is_a? Hash
         | 
| 279 | 
            +
                    # hash example: { 'att1' => '/path/to/att1', 'att2' => '/path/to/att2' }
         | 
| 280 | 
            +
                    @locals[:attachments].each do |identifier, attachment|
         | 
| 281 | 
            +
                      add_attachment_to_multipart_message(multipart_message, attachment, identifier)
         | 
| 282 | 
            +
                    end
         | 
| 283 | 
            +
                  elsif @locals[:attachments].is_a? Array
         | 
| 284 | 
            +
                    # array example: [ '/path/to/att1', '/path/to/att2' ]
         | 
| 285 | 
            +
                    # array example: [ { filename: 'att1.xml', content: '<x/>' }, { filename: 'att2.xml', content: '<y/>' } ]
         | 
| 286 | 
            +
                    @locals[:attachments].each do |attachment|
         | 
| 287 | 
            +
                      add_attachment_to_multipart_message(multipart_message, attachment, attachment.is_a?(String) ? File.basename(attachment) : attachment[:filename])
         | 
| 288 | 
            +
                    end
         | 
| 289 | 
            +
                  end
         | 
| 290 | 
            +
                end
         | 
| 291 | 
            +
             | 
| 292 | 
            +
                def add_attachment_to_multipart_message(multipart_message, attachment, identifier)
         | 
| 293 | 
            +
                  multipart_message.add_file attachment.clone
         | 
| 294 | 
            +
                  multipart_message.parts.last.content_id = multipart_message.parts.last.content_location = identifier.to_s
         | 
| 295 | 
            +
                end
         | 
| 230 296 | 
             
              end
         | 
| 231 297 | 
             
            end
         | 
    
        data/lib/savon/client.rb
    CHANGED
    
    
    
        data/lib/savon/header.rb
    CHANGED
    
    | @@ -1,3 +1,4 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 1 2 | 
             
            require "akami"
         | 
| 2 3 | 
             
            require "gyoku"
         | 
| 3 4 | 
             
            require "securerandom"
         | 
| @@ -61,12 +62,7 @@ module Savon | |
| 61 62 | 
             
                   convert_to_xml({
         | 
| 62 63 | 
             
                     'wsa:Action' => @locals[:soap_action],
         | 
| 63 64 | 
             
                     'wsa:To' => @globals[:endpoint],
         | 
| 64 | 
            -
                     'wsa:MessageID' => "urn:uuid:#{SecureRandom.uuid}" | 
| 65 | 
            -
                     attributes!: {
         | 
| 66 | 
            -
                      'wsa:MessageID' => {
         | 
| 67 | 
            -
                        "xmlns:wsa" => "http://schemas.xmlsoap.org/ws/2004/08/addressing"
         | 
| 68 | 
            -
                      }
         | 
| 69 | 
            -
                     }
         | 
| 65 | 
            +
                     'wsa:MessageID' => "urn:uuid:#{SecureRandom.uuid}"
         | 
| 70 66 | 
             
                   })
         | 
| 71 67 | 
             
                end
         | 
| 72 68 |  | 
    
        data/lib/savon/http_error.rb
    CHANGED
    
    | @@ -1,4 +1,4 @@ | |
| 1 | 
            -
             | 
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 3 | 
             
            module Savon
         | 
| 4 4 | 
             
              class HTTPError < Error
         | 
| @@ -14,9 +14,9 @@ module Savon | |
| 14 14 | 
             
                attr_reader :http
         | 
| 15 15 |  | 
| 16 16 | 
             
                def to_s
         | 
| 17 | 
            -
                   | 
| 18 | 
            -
             | 
| 19 | 
            -
                   | 
| 17 | 
            +
                  String.new("HTTP error (#{@http.code})").tap do |str_error|
         | 
| 18 | 
            +
                    str_error << ": #{@http.body}" unless @http.body.empty?
         | 
| 19 | 
            +
                  end
         | 
| 20 20 | 
             
                end
         | 
| 21 21 |  | 
| 22 22 | 
             
                def to_hash
         | 
    
        data/lib/savon/log_message.rb
    CHANGED
    
    
    
        data/lib/savon/message.rb
    CHANGED
    
    
    
        data/lib/savon/mock.rb
    CHANGED
    
    
    
        data/lib/savon/model.rb
    CHANGED
    
    
    
        data/lib/savon/operation.rb
    CHANGED
    
    | @@ -1,3 +1,4 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 1 2 | 
             
            require "savon/options"
         | 
| 2 3 | 
             
            require "savon/block_interface"
         | 
| 3 4 | 
             
            require "savon/request"
         | 
| @@ -5,10 +6,16 @@ require "savon/builder" | |
| 5 6 | 
             
            require "savon/response"
         | 
| 6 7 | 
             
            require "savon/request_logger"
         | 
| 7 8 | 
             
            require "savon/http_error"
         | 
| 9 | 
            +
            require "mail"
         | 
| 8 10 |  | 
| 9 11 | 
             
            module Savon
         | 
| 10 12 | 
             
              class Operation
         | 
| 11 13 |  | 
| 14 | 
            +
                SOAP_REQUEST_TYPE = {
         | 
| 15 | 
            +
                  1 => "text/xml",
         | 
| 16 | 
            +
                  2 => "application/soap+xml"
         | 
| 17 | 
            +
                }
         | 
| 18 | 
            +
             | 
| 12 19 | 
             
                def self.create(operation_name, wsdl, globals)
         | 
| 13 20 | 
             
                  if wsdl.document?
         | 
| 14 21 | 
             
                    ensure_name_is_symbol! operation_name
         | 
| @@ -66,21 +73,7 @@ module Savon | |
| 66 73 | 
             
                private
         | 
| 67 74 |  | 
| 68 75 | 
             
                def create_response(response)
         | 
| 69 | 
            -
                   | 
| 70 | 
            -
                    Multipart::Response.new(response, @globals, @locals)
         | 
| 71 | 
            -
                  else
         | 
| 72 | 
            -
                    Response.new(response, @globals, @locals)
         | 
| 73 | 
            -
                  end
         | 
| 74 | 
            -
                end
         | 
| 75 | 
            -
             | 
| 76 | 
            -
                def multipart_supported?
         | 
| 77 | 
            -
                  return false unless @globals[:multipart] || @locals[:multipart]
         | 
| 78 | 
            -
             | 
| 79 | 
            -
                  if Savon.const_defined? :Multipart
         | 
| 80 | 
            -
                    true
         | 
| 81 | 
            -
                  else
         | 
| 82 | 
            -
                    raise 'Unable to find Savon::Multipart. Make sure the savon-multipart gem is installed and loaded.'
         | 
| 83 | 
            -
                  end
         | 
| 76 | 
            +
                  Response.new(response, @globals, @locals)
         | 
| 84 77 | 
             
                end
         | 
| 85 78 |  | 
| 86 79 | 
             
                def set_locals(locals, block)
         | 
| @@ -107,6 +100,15 @@ module Savon | |
| 107 100 | 
             
                  request.url = endpoint
         | 
| 108 101 | 
             
                  request.body = builder.to_s
         | 
| 109 102 |  | 
| 103 | 
            +
                  if builder.multipart
         | 
| 104 | 
            +
                    request.gzip
         | 
| 105 | 
            +
                    request.headers["Content-Type"] = ["multipart/related",
         | 
| 106 | 
            +
                                                       "type=\"#{SOAP_REQUEST_TYPE[@globals[:soap_version]]}\"",
         | 
| 107 | 
            +
                                                       "start=\"#{builder.multipart[:start]}\"",
         | 
| 108 | 
            +
                                                       "boundary=\"#{builder.multipart[:multipart_boundary]}\""].join("; ")
         | 
| 109 | 
            +
                    request.headers["MIME-Version"] = "1.0"
         | 
| 110 | 
            +
                  end
         | 
| 111 | 
            +
             | 
| 110 112 | 
             
                  # TODO: could HTTPI do this automatically in case the header
         | 
| 111 113 | 
             
                  #       was not specified manually? [dh, 2013-01-04]
         | 
| 112 114 | 
             
                  request.headers["Content-Length"] = request.body.bytesize.to_s
         | 
| @@ -119,11 +121,11 @@ module Savon | |
| 119 121 | 
             
                  return if @locals.include?(:soap_action) && !@locals[:soap_action]
         | 
| 120 122 |  | 
| 121 123 | 
             
                  # get the soap_action from local options
         | 
| 122 | 
            -
                   | 
| 124 | 
            +
                  @locals[:soap_action] ||
         | 
| 123 125 | 
             
                  # with no local option, but a wsdl, ask it for the soap_action
         | 
| 124 | 
            -
                   | 
| 126 | 
            +
                  @wsdl.document? && @wsdl.soap_action(@name.to_sym) ||
         | 
| 125 127 | 
             
                  # if there is no soap_action up to this point, fallback to a simple default
         | 
| 126 | 
            -
                   | 
| 128 | 
            +
                  Gyoku.xml_tag(@name, :key_converter => @globals[:convert_request_keys_to])
         | 
| 127 129 | 
             
                end
         | 
| 128 130 |  | 
| 129 131 | 
             
                def endpoint
         | 
    
        data/lib/savon/options.rb
    CHANGED
    
    | @@ -1,3 +1,4 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 1 2 | 
             
            require "logger"
         | 
| 2 3 | 
             
            require "httpi"
         | 
| 3 4 |  | 
| @@ -78,6 +79,7 @@ module Savon | |
| 78 79 | 
             
                    :namespaces                  => {},
         | 
| 79 80 | 
             
                    :logger                      => Logger.new($stdout),
         | 
| 80 81 | 
             
                    :log                         => false,
         | 
| 82 | 
            +
                    :log_headers                 => true,
         | 
| 81 83 | 
             
                    :filters                     => [],
         | 
| 82 84 | 
             
                    :pretty_print_xml            => false,
         | 
| 83 85 | 
             
                    :raise_errors                => true,
         | 
| @@ -155,6 +157,11 @@ module Savon | |
| 155 157 | 
             
                  @options[:read_timeout] = read_timeout
         | 
| 156 158 | 
             
                end
         | 
| 157 159 |  | 
| 160 | 
            +
                # Write timeout in seconds.
         | 
| 161 | 
            +
                def write_timeout(write_timeout)
         | 
| 162 | 
            +
                  @options[:write_timeout] = write_timeout
         | 
| 163 | 
            +
                end
         | 
| 164 | 
            +
             | 
| 158 165 | 
             
                # The encoding to use. Defaults to "UTF-8".
         | 
| 159 166 | 
             
                def encoding(encoding)
         | 
| 160 167 | 
             
                  @options[:encoding] = encoding
         | 
| @@ -213,6 +220,11 @@ module Savon | |
| 213 220 | 
             
                  @options[:logger].level = levels[level]
         | 
| 214 221 | 
             
                end
         | 
| 215 222 |  | 
| 223 | 
            +
                # To log headers or not.
         | 
| 224 | 
            +
                def log_headers(log_headers)
         | 
| 225 | 
            +
                  @options[:log_headers] = log_headers
         | 
| 226 | 
            +
                end
         | 
| 227 | 
            +
             | 
| 216 228 | 
             
                # A list of XML tags to filter from logged SOAP messages.
         | 
| 217 229 | 
             
                def filters(*filters)
         | 
| 218 230 | 
             
                  @options[:filters] = filters.flatten
         | 
| @@ -228,6 +240,16 @@ module Savon | |
| 228 240 | 
             
                  @options[:ssl_version] = version
         | 
| 229 241 | 
             
                end
         | 
| 230 242 |  | 
| 243 | 
            +
                # Specifies the SSL version to use.
         | 
| 244 | 
            +
                def ssl_min_version(version)
         | 
| 245 | 
            +
                  @options[:ssl_min_version] = version
         | 
| 246 | 
            +
                end
         | 
| 247 | 
            +
             | 
| 248 | 
            +
                # Specifies the SSL version to use.
         | 
| 249 | 
            +
                def ssl_max_version(version)
         | 
| 250 | 
            +
                  @options[:ssl_max_version] = version
         | 
| 251 | 
            +
                end
         | 
| 252 | 
            +
             | 
| 231 253 | 
             
                # Whether and how to to verify the connection.
         | 
| 232 254 | 
             
                def ssl_verify_mode(verify_mode)
         | 
| 233 255 | 
             
                  @options[:ssl_verify_mode] = verify_mode
         | 
| @@ -396,6 +418,40 @@ module Savon | |
| 396 418 | 
             
                  @options[:attributes] = attributes
         | 
| 397 419 | 
             
                end
         | 
| 398 420 |  | 
| 421 | 
            +
                # Attachments for the SOAP message (https://www.w3.org/TR/SOAP-attachments)
         | 
| 422 | 
            +
                #
         | 
| 423 | 
            +
                # should pass an Array or a Hash; items should be path strings or
         | 
| 424 | 
            +
                #  { filename: 'file.name', content: 'content' } objects
         | 
| 425 | 
            +
                # The Content-ID in multipart message sections will be the filename or the key if Hash is given
         | 
| 426 | 
            +
                #
         | 
| 427 | 
            +
                # usage examples:
         | 
| 428 | 
            +
                #
         | 
| 429 | 
            +
                #    response = client.call :operation1 do
         | 
| 430 | 
            +
                #      message param1: 'value'
         | 
| 431 | 
            +
                #      attachments [
         | 
| 432 | 
            +
                #        { filename: 'x1.xml', content: '<xml>abc</xml>'},
         | 
| 433 | 
            +
                #        { filename: 'x2.xml', content: '<xml>abc</xml>'}
         | 
| 434 | 
            +
                #      ]
         | 
| 435 | 
            +
                #    end
         | 
| 436 | 
            +
                #    # Content-ID will be x1.xml and x2.xml
         | 
| 437 | 
            +
                #
         | 
| 438 | 
            +
                #    response = client.call :operation1 do
         | 
| 439 | 
            +
                #      message param1: 'value'
         | 
| 440 | 
            +
                #      attachments 'x1.xml' => '/tmp/1281ab7d7d.xml', 'x2.xml' => '/tmp/4c5v8e833a.xml'
         | 
| 441 | 
            +
                #    end
         | 
| 442 | 
            +
                #    # Content-ID will be x1.xml and x2.xml
         | 
| 443 | 
            +
                #
         | 
| 444 | 
            +
                #    response = client.call :operation1 do
         | 
| 445 | 
            +
                #      message param1: 'value'
         | 
| 446 | 
            +
                #      attachments [ '/tmp/1281ab7d7d.xml', '/tmp/4c5v8e833a.xml']
         | 
| 447 | 
            +
                #    end
         | 
| 448 | 
            +
                #    # Content-ID will be 1281ab7d7d.xml and 4c5v8e833a.xml
         | 
| 449 | 
            +
                #
         | 
| 450 | 
            +
                # The Content-ID is important if you want to refer to the attachments from the SOAP request
         | 
| 451 | 
            +
                def attachments(attachments)
         | 
| 452 | 
            +
                  @options[:attachments] = attachments
         | 
| 453 | 
            +
                end
         | 
| 454 | 
            +
             | 
| 399 455 | 
             
                # Value of the SOAPAction HTTP header.
         | 
| 400 456 | 
             
                def soap_action(soap_action)
         | 
| 401 457 | 
             
                  @options[:soap_action] = soap_action
         | 
| @@ -1,3 +1,4 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 1 2 | 
             
            require "gyoku"
         | 
| 2 3 |  | 
| 3 4 | 
             
            module Savon
         | 
| @@ -43,8 +44,8 @@ module Savon | |
| 43 44 | 
             
                  Array(values).collect do |value|
         | 
| 44 45 | 
             
                    translated_value = translate_tag(value)
         | 
| 45 46 | 
             
                    namespace_path   = path + [translated_value]
         | 
| 46 | 
            -
                    namespace        = @used_namespaces[namespace_path]
         | 
| 47 | 
            -
                    namespace. | 
| 47 | 
            +
                    namespace        = @used_namespaces[namespace_path] || ''
         | 
| 48 | 
            +
                    namespace.empty? ? value : "#{namespace}:#{translated_value}"
         | 
| 48 49 | 
             
                  end
         | 
| 49 50 | 
             
                end
         | 
| 50 51 | 
             
              end
         | 
    
        data/lib/savon/request.rb
    CHANGED
    
    | @@ -1,3 +1,4 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 1 2 | 
             
            require "httpi"
         | 
| 2 3 |  | 
| 3 4 | 
             
            module Savon
         | 
| @@ -21,10 +22,14 @@ module Savon | |
| 21 22 | 
             
                def configure_timeouts
         | 
| 22 23 | 
             
                  @http_request.open_timeout = @globals[:open_timeout] if @globals.include? :open_timeout
         | 
| 23 24 | 
             
                  @http_request.read_timeout = @globals[:read_timeout] if @globals.include? :read_timeout
         | 
| 25 | 
            +
                  @http_request.write_timeout = @globals[:write_timeout] if @globals.include? :write_timeout
         | 
| 24 26 | 
             
                end
         | 
| 25 27 |  | 
| 26 28 | 
             
                def configure_ssl
         | 
| 27 29 | 
             
                  @http_request.auth.ssl.ssl_version   = @globals[:ssl_version]       if @globals.include? :ssl_version
         | 
| 30 | 
            +
                  @http_request.auth.ssl.min_version   = @globals[:ssl_min_version]   if @globals.include? :ssl_min_version
         | 
| 31 | 
            +
                  @http_request.auth.ssl.max_version   = @globals[:ssl_max_version]   if @globals.include? :ssl_max_version
         | 
| 32 | 
            +
             | 
| 28 33 | 
             
                  @http_request.auth.ssl.verify_mode   = @globals[:ssl_verify_mode]   if @globals.include? :ssl_verify_mode
         | 
| 29 34 | 
             
                  @http_request.auth.ssl.ciphers       = @globals[:ssl_ciphers]       if @globals.include? :ssl_ciphers
         | 
| 30 35 |  | 
    
        data/lib/savon/request_logger.rb
    CHANGED
    
    | @@ -1,3 +1,4 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 1 2 | 
             
            require "savon/log_message"
         | 
| 2 3 |  | 
| 3 4 | 
             
            module Savon
         | 
| @@ -23,21 +24,26 @@ module Savon | |
| 23 24 | 
             
                  @globals[:log]
         | 
| 24 25 | 
             
                end
         | 
| 25 26 |  | 
| 27 | 
            +
                def log_headers?
         | 
| 28 | 
            +
                  @globals[:log_headers]
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
             | 
| 26 31 | 
             
                private
         | 
| 27 32 |  | 
| 28 33 | 
             
                def log_request(request)
         | 
| 29 34 | 
             
                  logger.info  { "SOAP request: #{request.url}" }
         | 
| 30 | 
            -
                  logger.info  { headers_to_log(request.headers) }
         | 
| 35 | 
            +
                  logger.info  { headers_to_log(request.headers) } if log_headers?
         | 
| 31 36 | 
             
                  logger.debug { body_to_log(request.body) }
         | 
| 32 37 | 
             
                end
         | 
| 33 38 |  | 
| 34 39 | 
             
                def log_response(response)
         | 
| 35 40 | 
             
                  logger.info  { "SOAP response (status #{response.code})" }
         | 
| 41 | 
            +
                  logger.debug { headers_to_log(response.headers) } if log_headers?
         | 
| 36 42 | 
             
                  logger.debug { body_to_log(response.body) }
         | 
| 37 43 | 
             
                end
         | 
| 38 44 |  | 
| 39 45 | 
             
                def headers_to_log(headers)
         | 
| 40 | 
            -
                  headers.map { |key, value| "#{key}: #{value}" }.join(" | 
| 46 | 
            +
                  headers.map { |key, value| "#{key}: #{value}" }.join("\n")
         | 
| 41 47 | 
             
                end
         | 
| 42 48 |  | 
| 43 49 | 
             
                def body_to_log(body)
         | 
    
        data/lib/savon/response.rb
    CHANGED
    
    | @@ -1,14 +1,20 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 1 2 | 
             
            require "nori"
         | 
| 2 3 | 
             
            require "savon/soap_fault"
         | 
| 3 4 | 
             
            require "savon/http_error"
         | 
| 4 5 |  | 
| 5 6 | 
             
            module Savon
         | 
| 6 7 | 
             
              class Response
         | 
| 8 | 
            +
                CRLF = /\r\n/
         | 
| 9 | 
            +
                WSP  = /[#{%Q|\x9\x20|}]/
         | 
| 7 10 |  | 
| 8 11 | 
             
                def initialize(http, globals, locals)
         | 
| 9 12 | 
             
                  @http    = http
         | 
| 10 13 | 
             
                  @globals = globals
         | 
| 11 14 | 
             
                  @locals  = locals
         | 
| 15 | 
            +
                  @attachments = []
         | 
| 16 | 
            +
                  @xml     = ''
         | 
| 17 | 
            +
                  @has_parsed_body = false
         | 
| 12 18 |  | 
| 13 19 | 
             
                  build_soap_and_http_errors!
         | 
| 14 20 | 
             
                  raise_soap_and_http_errors! if @globals[:raise_errors]
         | 
| @@ -53,7 +59,12 @@ module Savon | |
| 53 59 | 
             
                end
         | 
| 54 60 |  | 
| 55 61 | 
             
                def xml
         | 
| 56 | 
            -
                   | 
| 62 | 
            +
                  if multipart?
         | 
| 63 | 
            +
                    parse_body unless @has_parsed_body
         | 
| 64 | 
            +
                    @xml
         | 
| 65 | 
            +
                  else
         | 
| 66 | 
            +
                    @http.body
         | 
| 67 | 
            +
                  end
         | 
| 57 68 | 
             
                end
         | 
| 58 69 |  | 
| 59 70 | 
             
                alias_method :to_xml, :xml
         | 
| @@ -74,8 +85,44 @@ module Savon | |
| 74 85 | 
             
                  nori.find(envelope, *path)
         | 
| 75 86 | 
             
                end
         | 
| 76 87 |  | 
| 88 | 
            +
                def attachments
         | 
| 89 | 
            +
                  if multipart?
         | 
| 90 | 
            +
                    parse_body unless @has_parsed_body
         | 
| 91 | 
            +
                    @attachments
         | 
| 92 | 
            +
                  else
         | 
| 93 | 
            +
                    []
         | 
| 94 | 
            +
                  end
         | 
| 95 | 
            +
                end
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                def multipart?
         | 
| 98 | 
            +
                  !(http.headers['content-type'] =~ /^multipart/im).nil?
         | 
| 99 | 
            +
                end
         | 
| 100 | 
            +
             | 
| 77 101 | 
             
                private
         | 
| 78 102 |  | 
| 103 | 
            +
                def boundary
         | 
| 104 | 
            +
                  return unless multipart?
         | 
| 105 | 
            +
                  Mail::Field.new('content-type', http.headers['content-type']).parameters['boundary']
         | 
| 106 | 
            +
                end
         | 
| 107 | 
            +
             | 
| 108 | 
            +
                def parse_body
         | 
| 109 | 
            +
                  http.body.force_encoding Encoding::ASCII_8BIT
         | 
| 110 | 
            +
                  parts = http.body.split(/(?:\A|\r\n)--#{Regexp.escape(boundary)}(?=(?:--)?\s*$)/)
         | 
| 111 | 
            +
                  parts[1..-1].to_a.each_with_index do |part, index|
         | 
| 112 | 
            +
                    header_part, body_part = part.lstrip.split(/#{CRLF}#{CRLF}|#{CRLF}#{WSP}*#{CRLF}(?!#{WSP})/m, 2)
         | 
| 113 | 
            +
                    section = Mail::Part.new(
         | 
| 114 | 
            +
                      body: body_part
         | 
| 115 | 
            +
                    )
         | 
| 116 | 
            +
                    section.header = header_part
         | 
| 117 | 
            +
                    if index == 0
         | 
| 118 | 
            +
                      @xml = section.body.to_s
         | 
| 119 | 
            +
                    else
         | 
| 120 | 
            +
                      @attachments << section
         | 
| 121 | 
            +
                    end
         | 
| 122 | 
            +
                  end
         | 
| 123 | 
            +
                  @has_parsed_body = true
         | 
| 124 | 
            +
                end
         | 
| 125 | 
            +
             | 
| 79 126 | 
             
                def build_soap_and_http_errors!
         | 
| 80 127 | 
             
                  @soap_fault = SOAPFault.new(@http, nori, xml) if soap_fault?
         | 
| 81 128 | 
             
                  @http_error = HTTPError.new(@http) if http_error?
         | 
    
        data/lib/savon/soap_fault.rb
    CHANGED
    
    
    
        data/lib/savon/version.rb
    CHANGED
    
    
    
        data/lib/savon.rb
    CHANGED
    
    
    
        data/savon.gemspec
    CHANGED
    
    | @@ -12,29 +12,30 @@ Gem::Specification.new do |s| | |
| 12 12 | 
             
              s.homepage    = "http://savonrb.com"
         | 
| 13 13 | 
             
              s.summary     = "Heavy metal SOAP client"
         | 
| 14 14 | 
             
              s.description = s.summary
         | 
| 15 | 
            -
              s.required_ruby_version = '>=  | 
| 15 | 
            +
              s.required_ruby_version = '>= 2.5.0'
         | 
| 16 16 |  | 
| 17 | 
            -
              s.rubyforge_project = s.name
         | 
| 18 17 | 
             
              s.license = 'MIT'
         | 
| 19 18 |  | 
| 20 19 | 
             
              s.add_dependency "nori",     "~> 2.4"
         | 
| 21 | 
            -
              s.add_dependency "httpi",    " | 
| 20 | 
            +
              s.add_dependency "httpi",    ">= 2.4.5"
         | 
| 22 21 | 
             
              s.add_dependency "wasabi",   "~> 3.4"
         | 
| 23 22 | 
             
              s.add_dependency "akami",    "~> 1.2"
         | 
| 24 23 | 
             
              s.add_dependency "gyoku",    "~> 1.2"
         | 
| 25 24 | 
             
              s.add_dependency "builder",  ">= 2.1.2"
         | 
| 26 25 | 
             
              s.add_dependency "nokogiri", ">= 1.8.1"
         | 
| 26 | 
            +
              s.add_dependency "mail",     "~> 2.5"
         | 
| 27 27 |  | 
| 28 28 | 
             
              s.add_development_dependency "rack"
         | 
| 29 | 
            -
              s.add_development_dependency "puma",  " | 
| 29 | 
            +
              s.add_development_dependency "puma",  ">= 4.3.8"
         | 
| 30 30 |  | 
| 31 | 
            -
              s.add_development_dependency " | 
| 32 | 
            -
              s.add_development_dependency " | 
| 31 | 
            +
              s.add_development_dependency "byebug"
         | 
| 32 | 
            +
              s.add_development_dependency "rake",  ">= 12.3.3"
         | 
| 33 | 
            +
              s.add_development_dependency "rspec", "~> 3.9"
         | 
| 33 34 | 
             
              s.add_development_dependency "mocha", "~> 0.14"
         | 
| 34 | 
            -
              s.add_development_dependency "json",  " | 
| 35 | 
            +
              s.add_development_dependency "json",  ">= 2.3.0"
         | 
| 35 36 |  | 
| 36 37 | 
             
              ignores  = File.readlines(".gitignore").grep(/\S+/).map(&:chomp)
         | 
| 37 | 
            -
              dotfiles = %w[.gitignore . | 
| 38 | 
            +
              dotfiles = %w[.gitignore .yardopts]
         | 
| 38 39 |  | 
| 39 40 | 
             
              all_files_without_ignores = Dir["**/*"].reject { |f|
         | 
| 40 41 | 
             
                File.directory?(f) || ignores.any? { |i| File.fnmatch(i, f) }
         |