savon 2.17.3 → 2.17.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cde79beaab26839d499267e94eee281c4f35f34906fd6dfdd4fc4590dc2c710a
4
- data.tar.gz: 93f661f0156d8331a68e440176d9aaef92d2744974858ee4f866e63f14a210a1
3
+ metadata.gz: 2bba4a9ced6c62286d20de586b56ce7263413fa5e4b51771e87fded4c2433f39
4
+ data.tar.gz: 9327a65ee629c258691084a822b635738b4d31ea38c5bd38f8e3399f11b16cd9
5
5
  SHA512:
6
- metadata.gz: 3f7436bc2d8c53b89a96a373ab015ce7461b86501549034b99a5ae585a8f25d113bd7e1d1897acc613360d8487bb198eeccf5d0cf4835be7e8f8e44dd4b637a3
7
- data.tar.gz: 8e45f5f6494c8256878b54b4092f7a253686ddc5c9ea1f98cd372c9ed4b0c784a4b7c885604c70036a1398fe0ac5ff08847a13def8ed931c8ce7d5da6f7475df
6
+ metadata.gz: a1d50a4b5f588cc1e094189f41aada85a2bbecf7b6dbf92b27c6041d7b3eb882808ec69c0d2f0e907cc572cea73c17bad18f82d74c400e4019836200ce1255e9
7
+ data.tar.gz: 9a61b3a21ae8a2c16761fb851c85355cd3e2575e540447ec7ac0fa5cbd42ae5bfa456c26813a5dce907fb9924f2cd6dece545f841f65a970a87445123fe90005
data/CHANGELOG.md CHANGED
@@ -5,12 +5,26 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
- ## [2.17.3] - 2026-06-23
8
+ ## [2.17.4] - 2026-07-03
9
+
10
+ **Restore WS-Addressing headers and fix `:wsse_signature` resolution**
9
11
 
10
12
  ### Fixed
11
13
 
14
+ * **WS-Addressing headers are populated from the WSDL again** ([#1057](https://github.com/savonrb/savon/issues/1057)). With `use_wsa_headers: true` and no explicit `:soap_action` or `:endpoint`, Savon emitted empty `<wsa:Action xsi:nil="true"/>` and `<wsa:To xsi:nil="true"/>` elements instead of the operation's SOAPAction and service endpoint. Up to 2.16.0 those values were populated as a side effect of building the HTTP request. The 2.17.0 transport refactor removed that step.
15
+ * **A global `:host` override no longer rewrites the WSDL's endpoint.** Resolving the request endpoint with `:host` set replaced host and port of the parsed WSDL address in place, visible as a permanently changed `client.wsdl.endpoint` after the first call. Endpoint and SOAPAction resolution moved into `Savon::EffectiveOptions`, which applies the override to a copy and never touches the WSDL document.
16
+ * **A local `:wsse_signature` no longer crashes when set to `false`.** Passing `wsse_signature: false` to a call raised `NoMethodError: undefined method 'have_document?' for false` while building the WSSE header. The envelope builder and the SOAP header also resolved the option with different rules, so they could disagree on which signature applies. Both now read it through `Savon::EffectiveOptions`, the one place that resolves options settable in both the global and the local scope. A falsy local `:wsse_signature` falls back to the global one. Setting a signature object in either scope is unaffected.
17
+
18
+ ### Deprecated
19
+
20
+ * **`Savon::Builder::WSA_NAMESPACE`.** WS-Addressing emission moved into `Savon::Addressing`, which owns the namespace as `Savon::Addressing::NAMESPACE`. The constant on `Savon::Builder` stays available as an alias and will be removed in Savon 3.
21
+
22
+ ## [2.17.3] - 2026-06-23
23
+
12
24
  **Fix Savon::HTTPError compatibility with Faraday transport**
13
25
 
26
+ ### Fixed
27
+
14
28
  * **`Savon::HTTPError` works with the Faraday transport** ([#1050](https://github.com/savonrb/savon/issues/1050)). When a the WSDL could not be fetched under `transport: :faraday`, `Savon::HTTPError#to_s` and `#to_hash` raised `NoMethodError: undefined method 'code'` because they were handed a raw `Faraday::Response`, which exposes `#status` rather than `#code`. Transports now normalize adapter-specific response objects into `Savon::Transport::Response` before they reach `Savon::HTTPError`.
15
29
 
16
30
  ## [2.17.2] - 2026-06-10
@@ -1248,6 +1262,8 @@ Pay attention to the following list and read the updated Wiki: http://wiki.githu
1248
1262
 
1249
1263
  * Complete rewrite and public release.
1250
1264
 
1265
+ [2.17.4]: https://github.com/savonrb/savon/compare/v2.17.3...v2.17.4
1266
+ [2.17.3]: https://github.com/savonrb/savon/compare/v2.17.2...v2.17.3
1251
1267
  [2.17.2]: https://github.com/savonrb/savon/compare/v2.17.1...v2.17.2
1252
1268
  [2.17.1]: https://github.com/savonrb/savon/compare/v2.17.0...v2.17.1
1253
1269
  [2.17.0]: https://github.com/savonrb/savon/compare/v2.16.0...v2.17.0
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "gyoku"
4
+ require "securerandom"
5
+
6
+ module Savon
7
+ # Emits the WS-Addressing message addressing properties of a request
8
+ # (WS-Addressing 1.0 - Core §3.2): +wsa:Action+, +wsa:To+ and +wsa:MessageID+.
9
+ #
10
+ # This object owns everything Savon knows about WS-Addressing: the namespace,
11
+ # whether the headers are emitted at all, and how the resolved SOAPAction and
12
+ # endpoint map onto the addressing properties. It works on plain values.
13
+ # {Savon::Header} resolves them via {Savon::EffectiveOptions} and constructs
14
+ # this object, keeping option resolution and WSA emission separate.
15
+ class Addressing
16
+ # The WS-Addressing 1.0 namespace,
17
+ # https://www.w3.org/TR/ws-addr-core/#namespaces.
18
+ NAMESPACE = "http://www.w3.org/2005/08/addressing"
19
+
20
+ # @param enabled [Object] whether the headers are emitted, any truthy value
21
+ # counts (the +:use_wsa_headers+ global)
22
+ # @param action [String, nil] the resolved SOAPAction, emitted as
23
+ # +wsa:Action+, or +nil+ when the caller disabled it
24
+ # @param to [URI, String, nil] the resolved endpoint, emitted as +wsa:To+
25
+ def initialize(enabled:, action: nil, to: nil)
26
+ @enabled = enabled
27
+ @action = action
28
+ @to = to
29
+ end
30
+
31
+ # @return [Boolean] whether the WS-Addressing headers are emitted
32
+ def enabled?
33
+ !!@enabled
34
+ end
35
+
36
+ # Returns the namespace declaration the enclosing Header element needs.
37
+ # The +wsa+ prefix is declared once on the Header, not on each child.
38
+ #
39
+ # @return [Hash{String => String}] +{"xmlns:wsa" => NAMESPACE}+, or an
40
+ # empty Hash when disabled
41
+ def namespace_attributes
42
+ enabled? ? { "xmlns:wsa" => NAMESPACE } : {}
43
+ end
44
+
45
+ # Renders the three addressing properties, each rendering with a freshly
46
+ # generated +wsa:MessageID+. A +nil+ action or destination is emitted as
47
+ # an +xsi:nil+ element rather than being dropped.
48
+ #
49
+ # @return [String] the header elements, or an empty String when disabled
50
+ def to_xml
51
+ return "" unless enabled?
52
+
53
+ Gyoku.xml(
54
+ "wsa:Action" => @action,
55
+ "wsa:To" => @to,
56
+ "wsa:MessageID" => "urn:uuid:#{SecureRandom.uuid}"
57
+ )
58
+ end
59
+ end
60
+ end
data/lib/savon/builder.rb CHANGED
@@ -1,12 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "savon/addressing"
3
4
  require "savon/header"
4
5
  require "savon/message"
6
+ require "savon/effective_options"
5
7
  require "nokogiri"
6
8
  require "builder"
7
9
  require "gyoku"
8
10
 
9
11
  module Savon
12
+ # Builds the SOAP request for an operation: the XML envelope, or
13
+ # a multipart/related message when the request carries attachments
14
+ # (SOAP with Attachments, https://www.w3.org/TR/SOAP-attachments).
15
+ #
16
+ # It owns the envelope structure, its namespace declarations, and the body,
17
+ # and signs the document when a WSSE signature is present. Message-body
18
+ # serialization is delegated to {Savon::Message}, the Header element to
19
+ # {Savon::Header}, and Hash-to-XML conversion to Gyoku.
10
20
  class Builder
11
21
  attr_reader :multipart
12
22
 
@@ -20,15 +30,23 @@ module Savon
20
30
  2 => "http://www.w3.org/2003/05/soap-envelope"
21
31
  }.freeze
22
32
 
23
- WSA_NAMESPACE = "http://www.w3.org/2005/08/addressing"
33
+ # @deprecated Use {Savon::Addressing::NAMESPACE} instead. This alias will
34
+ # be removed in Savon 3.0.
35
+ WSA_NAMESPACE = Addressing::NAMESPACE
24
36
 
37
+ # @param operation_name [Symbol] the SOAP operation being called
38
+ # @param wsdl [Wasabi::Document] the parsed WSDL, or an empty document when
39
+ # the client was configured without one
40
+ # @param globals [Savon::GlobalOptions] client-level options
41
+ # @param locals [Savon::LocalOptions] per-request options
25
42
  def initialize(operation_name, wsdl, globals, locals)
26
43
  @operation_name = operation_name
27
44
 
28
45
  @wsdl = wsdl
29
46
  @globals = globals
30
47
  @locals = locals
31
- @signature = @locals[:wsse_signature] || @globals[:wsse_signature]
48
+ @effective = EffectiveOptions.new(operation_name, wsdl, globals, locals)
49
+ @signature = @effective.wsse_signature
32
50
 
33
51
  @types = convert_type_definitions_to_hash
34
52
  @used_namespaces = convert_type_namespaces_to_hash
@@ -62,8 +80,12 @@ module Savon
62
80
  end
63
81
  end
64
82
 
83
+ # Returns the XML attributes of the envelope's Header element, provided
84
+ # by the header's sections.
85
+ #
86
+ # @return [Hash{String => String}]
65
87
  def header_attributes
66
- @globals[:use_wsa_headers] ? { 'xmlns:wsa' => WSA_NAMESPACE } : {}
88
+ header.attributes
67
89
  end
68
90
 
69
91
  def body_attributes
@@ -139,7 +161,7 @@ module Savon
139
161
  end
140
162
 
141
163
  def header
142
- @header ||= Header.new(@globals, @locals)
164
+ @header ||= Header.new(@globals, @effective)
143
165
  end
144
166
 
145
167
  def namespaced_message_tag
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "gyoku"
4
+ require "uri"
5
+
6
+ module Savon
7
+ # Resolves the effective value of options whose answer combines more than
8
+ # one source: the per-request options, the client options, the WSDL
9
+ # document, and built-in defaults.
10
+ #
11
+ # {Savon::GlobalOptions} and {Savon::LocalOptions} store what the caller
12
+ # said. EffectiveOptions answers what the request uses. It owns the
13
+ # precedence rules and exposes one reader per resolvable option, so every
14
+ # consumer of an option resolves it identically. Resolution is a pure read.
15
+ # It never mutates the options or the WSDL document.
16
+ class EffectiveOptions
17
+ # @param operation_name [Symbol] the SOAP operation being called
18
+ # @param wsdl [Wasabi::Document] the parsed WSDL, or an empty document
19
+ # when the client was configured without one
20
+ # @param globals [Savon::GlobalOptions] the client-level options
21
+ # @param locals [Savon::LocalOptions] the per-request options
22
+ def initialize(operation_name, wsdl, globals, locals)
23
+ @operation_name = operation_name
24
+ @wsdl = wsdl
25
+ @globals = globals
26
+ @locals = locals
27
+ end
28
+
29
+ # Resolves the SOAPAction of the request (SOAP 1.1 §6.1.1).
30
+ #
31
+ # An explicit local +:soap_action+ wins. A local +false+ or +nil+ disables
32
+ # the action, so no SOAPAction HTTP header is sent and an enabled
33
+ # +wsa:Action+ header stays empty. Without a local value the WSDL provides
34
+ # the soapAction of the operation. Without a WSDL document the operation
35
+ # name is converted to an XML tag as a best-effort default.
36
+ #
37
+ # @return [String, nil] the action, or +nil+ when explicitly disabled
38
+ def soap_action
39
+ return if @locals.include?(:soap_action) && !@locals[:soap_action]
40
+
41
+ @locals[:soap_action] ||
42
+ (@wsdl.document? && @wsdl.soap_action(@operation_name.to_sym)) ||
43
+ Gyoku.xml_tag(@operation_name, key_converter: @globals[:convert_request_keys_to])
44
+ end
45
+
46
+ # Resolves the endpoint URL the request is sent to.
47
+ #
48
+ # A global +:endpoint+ wins over the service address of the WSDL. The
49
+ # global +:host+ option replaces host and port of the WSDL address and
50
+ # keeps scheme, path and query. The override is applied to a copy. The
51
+ # WSDL document keeps its parsed address.
52
+ #
53
+ # @return [URI, String, nil] the endpoint as provided by the winning source
54
+ def endpoint
55
+ return @globals[:endpoint] if @globals[:endpoint]
56
+ return @wsdl.endpoint unless @globals[:host]
57
+
58
+ host_url = URI.parse(@globals[:host])
59
+ url = @wsdl.endpoint.dup
60
+ url.host = host_url.host
61
+ url.port = host_url.port
62
+ url
63
+ end
64
+
65
+ # Resolves the WSSE auth credentials passed to Akami. A local value takes
66
+ # precedence over the global one. A local +false+ disables auth even when a
67
+ # global value is set. A local +nil+ leaves the option unset and falls
68
+ # through to the global value.
69
+ #
70
+ # @return [Array<String>, false, nil] the credentials, +false+ to disable,
71
+ # or +nil+ when unset in both scopes
72
+ def wsse_auth
73
+ prefer_local(:wsse_auth)
74
+ end
75
+
76
+ # Resolves whether Akami emits a +wsu:Timestamp+ header, using the same
77
+ # local-over-global rule as {#wsse_auth}. A local +false+ disables it and a
78
+ # local +nil+ keeps the global value.
79
+ #
80
+ # @return [Boolean, nil]
81
+ def wsse_timestamp
82
+ prefer_local(:wsse_timestamp)
83
+ end
84
+
85
+ # Resolves the WSSE signature used to sign the request. This option is a
86
+ # signature object or +nil+ and has no "disable" value, so any falsy local
87
+ # value falls back to the global one. {Savon::Builder} and {Savon::Header}
88
+ # both read it and must resolve it identically.
89
+ #
90
+ # @return [Akami::WSSE::Signature, nil]
91
+ def wsse_signature
92
+ @locals[:wsse_signature] || @globals[:wsse_signature]
93
+ end
94
+
95
+ # Resolves the SOAP header content. When both scopes provide a Hash the two
96
+ # are merged and local keys win. Otherwise the local value is preferred and
97
+ # falls back to the global one. A Hash is rendered to XML by Gyoku. A String,
98
+ # or any object responding to +#to_s+, is used verbatim.
99
+ #
100
+ # @return [Hash, String, nil]
101
+ def soap_header
102
+ global = @globals[:soap_header]
103
+ local = @locals[:soap_header]
104
+
105
+ if global.is_a?(Hash) && local.is_a?(Hash)
106
+ global.merge(local)
107
+ else
108
+ local || global
109
+ end
110
+ end
111
+
112
+ private
113
+
114
+ # Resolves an option where a local +false+ is meaningful and disables it.
115
+ # Only a local +nil+ falls through to the global value. Contrast with
116
+ # {#wsse_signature}, which treats any falsy local as unset.
117
+ #
118
+ # @param key [Symbol] the option name
119
+ # @return [Object, nil] the local value unless it is +nil+, else the global
120
+ def prefer_local(key)
121
+ @locals[key].nil? ? @globals[key] : @locals[key]
122
+ end
123
+ end
124
+ end
data/lib/savon/header.rb CHANGED
@@ -2,71 +2,113 @@
2
2
 
3
3
  require "akami"
4
4
  require "gyoku"
5
- require "securerandom"
5
+ require "savon/addressing"
6
6
 
7
7
  module Savon
8
+ # Assembles the SOAP +Header+ element of a request envelope and renders it to
9
+ # XML.
10
+ #
11
+ # A SOAP Header carries a request's out-of-band metadata. This object is
12
+ # responsible for every header block Savon can emit, concatenated in this
13
+ # order:
14
+ #
15
+ # 1. application headers supplied by the caller (the +soap_header+ option),
16
+ # 2. the WS-Addressing headers, rendered by {Savon::Addressing},
17
+ # 3. the WS-Security header with UsernameToken credentials, a +wsu:Timestamp+
18
+ # and an XML Signature (OASIS WSS SOAP Message Security 1.1).
19
+ #
20
+ # WS-Security markup is delegated to Akami and Hash-to-XML conversion to Gyoku.
8
21
  class Header
9
- def initialize(globals, locals)
22
+ # @param globals [Savon::GlobalOptions] client-level options, read for the
23
+ # Gyoku key converter (+:convert_request_keys_to+) and the WS-Addressing
24
+ # toggle (+:use_wsa_headers+)
25
+ # @param effective [Savon::EffectiveOptions] resolves the WSSE, soap_header,
26
+ # soap_action and endpoint values for the request
27
+ def initialize(globals, effective)
10
28
  @gyoku_options = { key_converter: globals[:convert_request_keys_to] }
11
29
 
12
- @wsse_auth = locals[:wsse_auth].nil? ? globals[:wsse_auth] : locals[:wsse_auth]
13
- @wsse_timestamp = locals[:wsse_timestamp].nil? ? globals[:wsse_timestamp] : locals[:wsse_timestamp]
14
- @wsse_signature = locals[:wsse_signature].nil? ? globals[:wsse_signature] : locals[:wsse_signature]
30
+ @wsse_auth = effective.wsse_auth
31
+ @wsse_timestamp = effective.wsse_timestamp
32
+ @wsse_signature = effective.wsse_signature
33
+ @soap_header = effective.soap_header
15
34
 
16
- @global_header = globals[:soap_header]
17
- @local_header = locals[:soap_header]
35
+ @addressing = build_addressing(globals, effective)
36
+ @header = build
37
+ end
18
38
 
19
- @globals = globals
20
- @locals = locals
39
+ attr_reader :gyoku_options, :wsse_auth, :wsse_timestamp, :wsse_signature
21
40
 
22
- @header = build
41
+ # Returns the XML attributes of the enclosing Header element. Only the
42
+ # WS-Addressing section declares one, the +xmlns:wsa+ prefix.
43
+ #
44
+ # @return [Hash{String => String}]
45
+ def attributes
46
+ @addressing.namespace_attributes
23
47
  end
24
48
 
25
- attr_reader :local_header, :global_header, :gyoku_options,
26
- :wsse_auth, :wsse_timestamp, :wsse_signature
27
-
49
+ # @return [Boolean] whether the rendered header is empty, i.e. there is no
50
+ # header content to include in the envelope
28
51
  def empty?
29
52
  @header.empty?
30
53
  end
31
54
 
55
+ # Returns the rendered SOAP header XML. The header is built once during
56
+ # construction and cached, so this is a cheap, repeatable read.
57
+ #
58
+ # @return [String] the header XML (may be empty)
32
59
  def to_s
33
60
  @header
34
61
  end
35
62
 
36
63
  private
37
64
 
65
+ # Constructs the WS-Addressing section from the resolved request values.
66
+ # Resolving the endpoint raises without a WSDL document and without a
67
+ # global +:endpoint+, so action and endpoint are only resolved when the
68
+ # +:use_wsa_headers+ global asks for the headers.
69
+ #
70
+ # @return [Savon::Addressing]
71
+ def build_addressing(globals, effective)
72
+ return Addressing.new(enabled: false) unless globals[:use_wsa_headers]
73
+
74
+ Addressing.new(
75
+ enabled: true,
76
+ action: effective.soap_action,
77
+ to: effective.endpoint
78
+ )
79
+ end
80
+
81
+ # Concatenates the three header sections in document order: caller content,
82
+ # WS-Addressing, then WSSE security.
83
+ #
84
+ # @return [String]
38
85
  def build
39
- build_header + build_wsa_header + build_wsse_header
86
+ build_header + @addressing.to_xml + build_wsse_header
40
87
  end
41
88
 
89
+ # Renders the resolved soap_header. {Savon::EffectiveOptions#soap_header}
90
+ # merges the global and local values, so there is nothing to combine here.
91
+ # Hash-to-XML conversion only. Strings pass through.
92
+ #
93
+ # @return [String]
42
94
  def build_header
43
- header =
44
- if global_header.is_a?(Hash) && local_header.is_a?(Hash)
45
- global_header.merge(local_header)
46
- elsif local_header
47
- local_header
48
- else
49
- global_header
50
- end
51
-
52
- convert_to_xml(header)
95
+ convert_to_xml(@soap_header)
53
96
  end
54
97
 
98
+ # Builds the WS-Security header via Akami, or an empty string when no WSSE
99
+ # content (credentials, timestamp or signature) applies.
100
+ #
101
+ # @return [String]
55
102
  def build_wsse_header
56
103
  wsse_header = akami
57
104
  wsse_header.respond_to?(:to_xml) ? wsse_header.to_xml : ""
58
105
  end
59
106
 
60
- def build_wsa_header
61
- return '' unless @globals[:use_wsa_headers]
62
-
63
- convert_to_xml({
64
- 'wsa:Action' => @locals[:soap_action],
65
- 'wsa:To' => @globals[:endpoint],
66
- 'wsa:MessageID' => "urn:uuid:#{SecureRandom.uuid}"
67
- })
68
- end
69
-
107
+ # Renders a header value to XML. A Hash goes through Gyoku (honouring the
108
+ # configured key converter), anything else is coerced with +#to_s+.
109
+ #
110
+ # @param hash_or_string [Hash, #to_s] the header content
111
+ # @return [String]
70
112
  def convert_to_xml(hash_or_string)
71
113
  if hash_or_string.is_a? Hash
72
114
  Gyoku.xml(hash_or_string, gyoku_options)
@@ -75,6 +117,11 @@ module Savon
75
117
  end
76
118
  end
77
119
 
120
+ # Configures an Akami WSSE object from the resolved WSSE options. Sets the
121
+ # UsernameToken credentials, enables the wsu:Timestamp, and attaches the XML
122
+ # Signature when a document has been assigned to it.
123
+ #
124
+ # @return [Akami::WSSE] configured, not yet serialized
78
125
  def akami
79
126
  wsse = Akami.wsse
80
127
  wsse.credentials(*wsse_auth) if wsse_auth
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "savon/options"
4
+ require "savon/effective_options"
4
5
  require "savon/block_interface"
5
6
  require "savon/builder"
6
7
  require "savon/response"
@@ -114,6 +115,13 @@ module Savon
114
115
  @locals = locals
115
116
  end
116
117
 
118
+ # The effective options view for the current request. Built on demand
119
+ # instead of being cached, so it always reflects the current per-request
120
+ # options.
121
+ def effective_options
122
+ EffectiveOptions.new(@name, @wsdl, @globals, @locals)
123
+ end
124
+
117
125
  # Assembles the SOAP-level request headers for the given builder.
118
126
  #
119
127
  # Our responsibility regardless of transport:
@@ -143,26 +151,16 @@ module Savon
143
151
  headers
144
152
  end
145
153
 
154
+ # The resolved SOAPAction of the request, used for the HTTP header.
155
+ # {Savon::EffectiveOptions#soap_action} owns the precedence rules.
146
156
  def soap_action
147
- # soap_action explicitly set to something falsy
148
- return if @locals.include?(:soap_action) && !@locals[:soap_action]
149
-
150
- # get the soap_action from local options
151
- @locals[:soap_action] ||
152
- # with no local option, but a wsdl, ask it for the soap_action
153
- @wsdl.document? && @wsdl.soap_action(@name.to_sym) ||
154
- # if there is no soap_action up to this point, fallback to a simple default
155
- Gyoku.xml_tag(@name, key_converter: @globals[:convert_request_keys_to])
157
+ effective_options.soap_action
156
158
  end
157
159
 
160
+ # The resolved endpoint URL the request is sent to.
161
+ # {Savon::EffectiveOptions#endpoint} owns the precedence rules.
158
162
  def endpoint
159
- @globals[:endpoint] || @wsdl.endpoint.tap do |url|
160
- if @globals[:host]
161
- host_url = URI.parse(@globals[:host])
162
- url.host = host_url.host
163
- url.port = host_url.port
164
- end
165
- end
163
+ effective_options.endpoint
166
164
  end
167
165
 
168
166
  # Normalizes an observer return value into a Transport::Response.
data/lib/savon/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Savon
4
- VERSION = '2.17.3'
4
+ VERSION = '2.17.4'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: savon
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.17.3
4
+ version: 2.17.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Harrington
@@ -256,9 +256,11 @@ files:
256
256
  - README.md
257
257
  - Rakefile
258
258
  - lib/savon.rb
259
+ - lib/savon/addressing.rb
259
260
  - lib/savon/block_interface.rb
260
261
  - lib/savon/builder.rb
261
262
  - lib/savon/client.rb
263
+ - lib/savon/effective_options.rb
262
264
  - lib/savon/faraday_migration_hint.rb
263
265
  - lib/savon/header.rb
264
266
  - lib/savon/http_error.rb