savon 2.17.4 → 3.0.0.rc1
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/CHANGELOG.md +17 -115
- data/README.md +52 -52
- data/Rakefile +2 -6
- data/lib/savon/block_interface.rb +4 -3
- data/lib/savon/builder.rb +37 -60
- data/lib/savon/client.rb +13 -44
- data/lib/savon/header.rb +40 -87
- data/lib/savon/http_error.rb +9 -9
- data/lib/savon/log_message.rb +3 -2
- data/lib/savon/message.rb +7 -6
- data/lib/savon/mock/expectation.rb +24 -38
- data/lib/savon/mock/spec_helper.rb +11 -7
- data/lib/savon/mock.rb +0 -1
- data/lib/savon/model.rb +25 -25
- data/lib/savon/operation.rb +92 -112
- data/lib/savon/options.rb +175 -276
- data/lib/savon/qualified_message.rb +1 -2
- data/lib/savon/request.rb +147 -0
- data/lib/savon/request_logger.rb +57 -0
- data/lib/savon/response.rb +31 -45
- data/lib/savon/soap_fault.rb +6 -6
- data/lib/savon/string_utils.rb +4 -3
- data/lib/savon/version.rb +1 -2
- data/lib/savon.rb +11 -2
- metadata +130 -71
- data/lib/savon/addressing.rb +0 -60
- data/lib/savon/effective_options.rb +0 -124
- data/lib/savon/faraday_migration_hint.rb +0 -186
- data/lib/savon/transport/faraday.rb +0 -101
- data/lib/savon/transport/httpi.rb +0 -138
- data/lib/savon/transport/logging.rb +0 -60
- data/lib/savon/transport/response.rb +0 -73
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3f58a5268715a082369bc199231506447defea5f760fae67cb51507a9011836c
|
|
4
|
+
data.tar.gz: d349d7d546fb236e45f433047bd575dd6f6ec206254707094f02420ec58dfc07
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 91d6cd948edbb82b643fa5aaeef265dcfeef5037459478f0815f01bdacbe04cb159165301a9f0ebc75fd897470ff2a0b51de1fa256123843698a14925f5ebf43
|
|
7
|
+
data.tar.gz: 3a9fe95352e4f85e9e88f7eaffba5fb04df58085380ed7c62e8c0f31c0b110b5127c494125f373e97489c789991b6c1271e68df000606c3e1a3311a17edc951a
|
data/CHANGELOG.md
CHANGED
|
@@ -1,113 +1,22 @@
|
|
|
1
1
|
# Savon changelog
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
*
|
|
15
|
-
|
|
16
|
-
*
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
24
|
-
**Fix Savon::HTTPError compatibility with Faraday transport**
|
|
25
|
-
|
|
26
|
-
### Fixed
|
|
27
|
-
|
|
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`.
|
|
29
|
-
|
|
30
|
-
## [2.17.2] - 2026-06-10
|
|
31
|
-
|
|
32
|
-
**Fix CVE-2026-53510 and restore 2.17.0 cookie regressions**
|
|
33
|
-
|
|
34
|
-
### Fixed
|
|
35
|
-
|
|
36
|
-
* **Fix CVE-2026-53510** `Savon::Model` generated SOAP operation methods by interpolating operation names into Ruby source passed to `module_eval`. An attacker who can control the operation names of a WSDL, can inject Ruby code that executes in the application process. This affects only the `.all_operations` class method provided by `Savon::Model` to automatically register all operations provided by the WSDL. Configuring `Savon::Model` with trusted operation names via `.operations` is safe. Thanks to @connorshea for securely disclosing this, providing a proof and a great report.
|
|
37
|
-
* **`:cookies` request option works again.** The 2.17.0 transport refactor reimplemented cookie handling on top of `Array#map`, which broke callers passing an object that responds to `#cookies` and lost cookie-name de-duplication via `HTTPI::CookieStore`. The HTTPI transport delegates to `HTTPI::Request#set_cookies` again, restoring both shapes.
|
|
38
|
-
* **`response.http.cookies` works again.** 2.17.0's `Savon::Transport::Response` only exposed `code`, `headers`, and `body`. The HTTPI transport now returns `Array<HTTPI::Cookie>` (matching 2.12.1). The Faraday transport returns `Hash<String, String>` so Faraday callers do not need HTTPI types.
|
|
39
|
-
* **`:attachments` now works with a user-supplied `:xml` envelope** ([#761](https://github.com/savonrb/savon/pull/761)). Multipart support shipped in 2.13.0 but only wrapped envelopes Savon built itself. When a caller passed their own `:xml`, attachments were silently dropped.
|
|
40
|
-
|
|
41
|
-
### Added
|
|
42
|
-
|
|
43
|
-
* **Faraday `:cookies` option accepts a `String` or `Hash`.** Strings are used verbatim, Hashes are formatted as `"name=value; name=value"`. Round-trippable with the Faraday response shape.
|
|
44
|
-
* **Three Nori response-parsing options exposed as Savon globals:** `:empty_tag_value` (default `nil`), `:convert_dashes_to_underscores` (default `true`), and `:scrub_xml` (default `true`). Defaults match Nori's own for backwards compatibility.
|
|
45
|
-
|
|
46
|
-
### Changed
|
|
47
|
-
|
|
48
|
-
* **Minimum Nori version is now `~> 2.7`** (was `~> 2.4`). Needed for the new parsing options (`:empty_tag_value` arrived in Nori 2.6.0, `:scrub_xml` in 2.7.0). The 2.5–2.7 series also brings fixes callers benefit from automatically: invalid byte sequences parse instead of raising, REXML no longer turns `<` inside CDATA into `<`, `xs:date`/`xs:time`/`xs:dateTime` typecasting was corrected, and Nori stopped monkey-patching `String` and `Object`.
|
|
49
|
-
* **Faraday migration hints are now value-aware and verified.** Each hint prints the caller's actual option value and spells out the full gem/require/setup where needed. Fixed several incorrect examples and added tests to verify every hint.
|
|
50
|
-
|
|
51
|
-
### Deprecated
|
|
52
|
-
|
|
53
|
-
* Deprecated the global and local `:multipart` options. They have been no-ops since v2.13.0. Specifically since commit 4e7ae5e. Savon detects multipart responses by checking the `Content-Type` header.
|
|
54
|
-
|
|
55
|
-
## [2.17.1] - 2026-05-21
|
|
56
|
-
|
|
57
|
-
### Fixed
|
|
58
|
-
|
|
59
|
-
* [#1008](https://github.com/savonrb/savon/pull/1008) - The HTTPI and Faraday transports no longer set an explicit `Content-Length` request header. The underlying HTTP library already computes it from the body. Sending it as well produced a duplicate header on adapters that do not deduplicate (e.g. httpclient), which some servers reject.
|
|
60
|
-
* Requests using `attachments` were sent with a plain `text/xml` Content-Type instead of `multipart/related`. The 2.17.0 transport refactor assembled the request headers before the multipart body was built, leaving `Builder#multipart` empty at header time, so servers received a multipart body labelled as plain XML. 2.16.x and earlier are unaffected.
|
|
61
|
-
|
|
62
|
-
## [2.17.0] - 2026-05-19
|
|
63
|
-
|
|
64
|
-
**Add opt-in Faraday transport**
|
|
65
|
-
|
|
66
|
-
Callers who set `transport: :faraday` get a memoized `Faraday::Connection` via `client.faraday` and full control over middleware, SSL, auth, and timeouts. Callers who do not set this option see no behavior change. HTTPI remains the default for 2.x.
|
|
67
|
-
|
|
68
|
-
### Added
|
|
69
|
-
|
|
70
|
-
* `transport: :faraday` global option. Defaults to `:httpi` (#992).
|
|
71
|
-
* `client.faraday` returns a memoized `Faraday::Connection` for configuring middleware, SSL, auth, and timeouts when using the Faraday transport.
|
|
72
|
-
* `Savon.client` raises if `transport: :faraday` is set but the faraday gem is not installed, or if any httpi-specific global option (`proxy`, timeouts, `ssl`, auth, `adapter`) is set alongside it. All conflicts are reported with their Faraday equivalents.
|
|
73
|
-
|
|
74
|
-
### Changed
|
|
75
|
-
|
|
76
|
-
* Observers must return `Savon::Transport::Response` (or `nil`) instead of `HTTPI::Response`.
|
|
77
|
-
|
|
78
|
-
### Deprecated
|
|
79
|
-
|
|
80
|
-
* The HTTPI transport (currently the default) and all HTTPI-specific global options enumerated in `Savon::FaradayMigrationHint::OPTIONS` (`proxy`, `open_timeout`, `read_timeout`, `write_timeout`, the `ssl_*` family, `basic_auth`, `digest_auth`, `ntlm`, `follow_redirects`, `adapter`) will be removed in 3.0. Migrate to `transport: :faraday`. `FaradayMigrationHint` shows the Faraday equivalent for each option.
|
|
81
|
-
* Returning `HTTPI::Response` from observers emits a deprecation warning and will be removed in 3.0. Return `Savon::Transport::Response` instead.
|
|
82
|
-
|
|
83
|
-
The Faraday transport unblocks:
|
|
84
|
-
|
|
85
|
-
* redirect following for WSDL fetches via `faraday-follow-redirects` middleware (#1033, savonrb/wasabi#18)
|
|
86
|
-
* digest authentication via `faraday-digestauth` middleware (#1021, savonrb/httpi#250)
|
|
87
|
-
* proxy authentication with special characters in passwords (#941)
|
|
88
|
-
* and setting an `Accept` header for WSDL requests from Rails apps (savonrb/wasabi#115)
|
|
89
|
-
|
|
90
|
-
## [2.16.0] - 2026-05-18
|
|
91
|
-
|
|
92
|
-
**Restore compatibility**
|
|
93
|
-
|
|
94
|
-
If you stayed on 2.12.1 because a later version broke something, this release is for you. The fixes below target the most commonly reported upgrade blockers. Existing code should work without modification.
|
|
95
|
-
|
|
96
|
-
### Fixed
|
|
97
|
-
|
|
98
|
-
* Restore `Savon::Response#hash` removed in 2.14.0 (#985). Callers on 2.12.1 that use `response.hash` get the soap body back instead of Ruby's integer object id.
|
|
99
|
-
* Require wasabi >= 5.1.0 (#1015, #1016). Wasabi 4.x used message names as soap body element names and SOAPAction header values instead of operation names (savonrb/wasabi#122), causing servers to return a fault or reject the action for operations whose message name carried an `In` suffix.
|
|
100
|
-
* Stop dumping all WSDL namespaces into every soap envelope (#1014, #942). 2.13.0 injected every namespace from the entire WSDL document into each request, including structural ones that have no place in a request body. Strict servers reject envelopes with unexpected or duplicate declarations.
|
|
101
|
-
* Raise a proper `SOAPFault` instead of a raw exception when `soap:Fault` contains invalid encoding (#923).
|
|
102
|
-
* `SOAPFault.present?` was ignoring its `xml` argument and always operating on the instance's own body.
|
|
103
|
-
|
|
104
|
-
### Changed
|
|
105
|
-
|
|
106
|
-
* Added Ruby 3.4 (#1024) and Ruby 4.0 (#1039) to the CI test matrix.
|
|
107
|
-
|
|
108
|
-
### Deprecated
|
|
109
|
-
|
|
110
|
-
* `Savon::Response#hash` emits a deprecation warning on each call. Use `#full_hash` going forward. Will be removed in 3.0.
|
|
3
|
+
## Unreleased
|
|
4
|
+
* Add your PR changelog line here
|
|
5
|
+
|
|
6
|
+
## 3.0.0.rc1 (2024-07-15)
|
|
7
|
+
|
|
8
|
+
* Use Faraday instead of HTTPI
|
|
9
|
+
* BC BREAKING Cookies are handled differently now
|
|
10
|
+
* BC BREAKING Multiple pieces of functionality will rely on faraday libraries to be provided by the consuming codebase
|
|
11
|
+
* BC BREAKING Adapter overrides now utilize the faraday model
|
|
12
|
+
* BC BREAKING Multiple hard deprecations due to a lack of feature parity between Faraday and HTTPI
|
|
13
|
+
* Deprecates digest auth
|
|
14
|
+
* Deprecates ssl_cert_key_file auth, upgrade path is to read the key
|
|
15
|
+
in and provide it
|
|
16
|
+
* Deprecates encrypted ssl keys, upgrade path is to
|
|
17
|
+
decrypt the key and pass it to faraday in code
|
|
18
|
+
* Deprecates providing a ca cert, upgrade path is to provide a ca cert file
|
|
19
|
+
* deprecates overriding ssl ciphers, as faraday does not support this
|
|
111
20
|
|
|
112
21
|
## 2.15.1 (2024-07-08)
|
|
113
22
|
|
|
@@ -1261,10 +1170,3 @@ Pay attention to the following list and read the updated Wiki: http://wiki.githu
|
|
|
1261
1170
|
## 0.5.0 (2009-11-29)
|
|
1262
1171
|
|
|
1263
1172
|
* Complete rewrite and public release.
|
|
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
|
|
1267
|
-
[2.17.2]: https://github.com/savonrb/savon/compare/v2.17.1...v2.17.2
|
|
1268
|
-
[2.17.1]: https://github.com/savonrb/savon/compare/v2.17.0...v2.17.1
|
|
1269
|
-
[2.17.0]: https://github.com/savonrb/savon/compare/v2.16.0...v2.17.0
|
|
1270
|
-
[2.16.0]: https://github.com/savonrb/savon/compare/v2.15.1...v2.16.0
|
data/README.md
CHANGED
|
@@ -2,88 +2,88 @@
|
|
|
2
2
|
|
|
3
3
|
Heavy metal SOAP client
|
|
4
4
|
|
|
5
|
+
[Documentation](https://www.rubydoc.info/gems/savon/) | [Support](https://stackoverflow.com/questions/tagged/savon) |
|
|
6
|
+
[Mailing list](https://groups.google.com/forum/#!forum/savonrb) | [Twitter](http://twitter.com/savonrb)
|
|
7
|
+
|
|
5
8
|
[](https://github.com/savonrb/savon/actions/workflows/ci.yml)
|
|
6
9
|
[](http://badge.fury.io/rb/savon)
|
|
10
|
+
[](https://codeclimate.com/github/savonrb/savon)
|
|
7
11
|
[](https://coveralls.io/r/savonrb/savon)
|
|
8
12
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
Full documentation is at [savonrb.com](https://savonrb.com).
|
|
13
|
+
If you're reading this on GitHub, note that this README is for the main branch and that features/changes described here might not correspond to your version. You can find the documentation for your release [at rubydoc.info](https://www.rubydoc.info/find/gems?q=savon).
|
|
12
14
|
|
|
13
15
|
## Installation
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
+
Savon is available through [Rubygems](http://rubygems.org/gems/savon) and can be installed via:
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
$ gem install savon
|
|
17
21
|
```
|
|
18
22
|
|
|
19
|
-
|
|
23
|
+
or add it to your Gemfile like this:
|
|
20
24
|
|
|
21
|
-
```
|
|
25
|
+
```
|
|
26
|
+
gem 'savon', '~> 3.0.0'
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Usage example
|
|
30
|
+
|
|
31
|
+
``` ruby
|
|
22
32
|
require 'savon'
|
|
23
33
|
|
|
24
|
-
#
|
|
25
|
-
client = Savon.client(wsdl: '
|
|
34
|
+
# create a client for the service
|
|
35
|
+
client = Savon.client(wsdl: 'http://service.example.com?wsdl')
|
|
36
|
+
|
|
37
|
+
# or: create a client with a wsdl provided as a string
|
|
38
|
+
client = Savon.client do |config|
|
|
39
|
+
wsdl_content = File.read("/path/to/wsdl")
|
|
40
|
+
config.wsdl wsdl_content
|
|
41
|
+
end
|
|
26
42
|
|
|
27
|
-
# See what the service exposes
|
|
28
43
|
client.operations
|
|
29
|
-
# => [:find_user, :
|
|
44
|
+
# => [:find_user, :list_users]
|
|
30
45
|
|
|
31
|
-
#
|
|
46
|
+
# call the 'findUser' operation
|
|
32
47
|
response = client.call(:find_user, message: { id: 42 })
|
|
33
|
-
response.body[:find_user_response]
|
|
34
|
-
# => { id: 42, name: "Hoff" }
|
|
35
48
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
puts e.to_hash.dig(:fault, :faultstring)
|
|
49
|
+
response.body
|
|
50
|
+
# => { find_user_response: { id: 42, name: 'Hoff' } }
|
|
39
51
|
```
|
|
40
52
|
|
|
41
|
-
|
|
53
|
+
For more examples, you should check out the
|
|
54
|
+
[integration tests](https://github.com/savonrb/savon/tree/version2/spec/integration).
|
|
42
55
|
|
|
43
|
-
|
|
44
|
-
client = Savon.client(
|
|
45
|
-
wsdl: 'https://service.example.com?wsdl',
|
|
46
|
-
pretty_print_xml: true,
|
|
47
|
-
log: true
|
|
48
|
-
)
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
### Authentication
|
|
52
|
-
|
|
53
|
-
Most enterprise services require authentication. Common options:
|
|
54
|
-
|
|
55
|
-
```ruby
|
|
56
|
-
# HTTP basic auth
|
|
57
|
-
Savon.client(wsdl: '...', basic_auth: ['user', 'secret'])
|
|
56
|
+
## Upgrading from v2.x to v3.x
|
|
58
57
|
|
|
59
|
-
|
|
60
|
-
Savon.client(wsdl: '...', wsse_auth: ['user', 'secret', :digest], wsse_timestamp: true)
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
See [Authentication](https://savonrb.com/version2/globals.html) on the website for HTTP digest, NTLM, and certificate-based options.
|
|
58
|
+
See [UPGRADING.md](UPGRADING.md) for more information.
|
|
64
59
|
|
|
65
|
-
|
|
60
|
+
## Ruby version support
|
|
66
61
|
|
|
67
|
-
|
|
62
|
+
Every savon release is tested with contemporary supported versions of ruby. Historical compatibility information:
|
|
68
63
|
|
|
69
|
-
|
|
64
|
+
* `main` - same support as Ruby
|
|
65
|
+
* 2.15.x - MRI 3.0, 3.1, 3.2, 3.3
|
|
66
|
+
* 2.13.x, 2.14.x - MRI 2.7, 3.0, 3.1
|
|
67
|
+
* 2.12.x - MRI 2.2, 2.3, 2.4, 2.5
|
|
68
|
+
* 2.11.x - MRI 2.0, 2.1, 2.2, and 2.3
|
|
70
69
|
|
|
71
|
-
|
|
70
|
+
If you are running MRI 1.8.7, try a 2.6.x release.
|
|
72
71
|
|
|
73
|
-
|
|
72
|
+
Most changes are not backported to older versions of savon, or unsupported ruby versions.
|
|
74
73
|
|
|
75
|
-
|
|
74
|
+
## Running tests
|
|
76
75
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
76
|
+
```bash
|
|
77
|
+
$ bundle install
|
|
78
|
+
$ bundle exec rspec
|
|
79
|
+
```
|
|
80
80
|
|
|
81
|
-
|
|
81
|
+
## FAQ
|
|
82
82
|
|
|
83
|
-
|
|
83
|
+
* URI::InvalidURIError -- if you see this error, then it is likely that the http client you are using cannot parse the URI for your WSDL. Try `gem install httpclient` or add it to your `Gemfile`.
|
|
84
|
+
- See https://github.com/savonrb/savon/issues/488 for more info
|
|
84
85
|
|
|
85
|
-
**WSDL imports are not followed.** Savon parses only the root WSDL document via [Wasabi](https://github.com/savonrb/wasabi). Messages, port types, and bindings defined in imported files are invisible to Savon. If you control the WSDL, merge the imported elements into the root document and pass that to Savon as a string. If you need full import support, the [WSDL](https://rubygems.org/gems/wsdl) gem is an alternative.
|
|
86
86
|
|
|
87
|
-
##
|
|
87
|
+
## Documentation
|
|
88
88
|
|
|
89
|
-
|
|
89
|
+
Please be sure to [read the documentation](https://www.rubydoc.info/github/savonrb/savon/).
|
data/Rakefile
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
1
|
require "bundler/gem_tasks"
|
|
4
2
|
require "rspec/core/rake_task"
|
|
5
3
|
|
|
@@ -12,7 +10,5 @@ RSpec::Core::RakeTask.new "spec:integration" do |t|
|
|
|
12
10
|
t.pattern = "spec/integration/**/*_spec.rb"
|
|
13
11
|
end
|
|
14
12
|
|
|
15
|
-
|
|
16
|
-
task test
|
|
17
|
-
|
|
18
|
-
task default: :spec
|
|
13
|
+
task :default => :spec
|
|
14
|
+
task :test => :spec
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
|
|
3
2
|
module Savon
|
|
4
3
|
class BlockInterface
|
|
4
|
+
|
|
5
5
|
def initialize(target)
|
|
6
6
|
@target = target
|
|
7
7
|
end
|
|
8
8
|
|
|
9
9
|
def evaluate(block)
|
|
10
|
-
if block.arity
|
|
10
|
+
if block.arity > 0
|
|
11
11
|
block.call(@target)
|
|
12
12
|
else
|
|
13
|
-
@original = eval("self", block.binding
|
|
13
|
+
@original = eval("self", block.binding)
|
|
14
14
|
instance_eval(&block)
|
|
15
15
|
end
|
|
16
16
|
end
|
|
@@ -22,5 +22,6 @@ module Savon
|
|
|
22
22
|
rescue NoMethodError
|
|
23
23
|
@original.send(method, *args, &block)
|
|
24
24
|
end
|
|
25
|
+
|
|
25
26
|
end
|
|
26
27
|
end
|
data/lib/savon/builder.rb
CHANGED
|
@@ -1,59 +1,40 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "savon/addressing"
|
|
4
2
|
require "savon/header"
|
|
5
3
|
require "savon/message"
|
|
6
|
-
require "savon/effective_options"
|
|
7
4
|
require "nokogiri"
|
|
8
5
|
require "builder"
|
|
9
6
|
require "gyoku"
|
|
10
7
|
|
|
11
8
|
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.
|
|
20
9
|
class Builder
|
|
21
10
|
attr_reader :multipart
|
|
22
11
|
|
|
23
12
|
SCHEMA_TYPES = {
|
|
24
13
|
"xmlns:xsd" => "http://www.w3.org/2001/XMLSchema",
|
|
25
14
|
"xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance"
|
|
26
|
-
}
|
|
15
|
+
}
|
|
27
16
|
|
|
28
17
|
SOAP_NAMESPACE = {
|
|
29
18
|
1 => "http://schemas.xmlsoap.org/soap/envelope/",
|
|
30
19
|
2 => "http://www.w3.org/2003/05/soap-envelope"
|
|
31
|
-
}
|
|
20
|
+
}
|
|
32
21
|
|
|
33
|
-
|
|
34
|
-
# be removed in Savon 3.0.
|
|
35
|
-
WSA_NAMESPACE = Addressing::NAMESPACE
|
|
22
|
+
WSA_NAMESPACE = "http://www.w3.org/2005/08/addressing"
|
|
36
23
|
|
|
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
|
|
42
24
|
def initialize(operation_name, wsdl, globals, locals)
|
|
43
25
|
@operation_name = operation_name
|
|
44
26
|
|
|
45
27
|
@wsdl = wsdl
|
|
46
28
|
@globals = globals
|
|
47
29
|
@locals = locals
|
|
48
|
-
@
|
|
49
|
-
@signature = @effective.wsse_signature
|
|
30
|
+
@signature = @locals[:wsse_signature] || @globals[:wsse_signature]
|
|
50
31
|
|
|
51
32
|
@types = convert_type_definitions_to_hash
|
|
52
33
|
@used_namespaces = convert_type_namespaces_to_hash
|
|
53
34
|
end
|
|
54
35
|
|
|
55
36
|
def pretty
|
|
56
|
-
Nokogiri.XML(to_s).to_xml(indent
|
|
37
|
+
Nokogiri.XML(to_s).to_xml(:indent => 2)
|
|
57
38
|
end
|
|
58
39
|
|
|
59
40
|
def build_document
|
|
@@ -80,57 +61,43 @@ module Savon
|
|
|
80
61
|
end
|
|
81
62
|
end
|
|
82
63
|
|
|
83
|
-
# Returns the XML attributes of the envelope's Header element, provided
|
|
84
|
-
# by the header's sections.
|
|
85
|
-
#
|
|
86
|
-
# @return [Hash{String => String}]
|
|
87
64
|
def header_attributes
|
|
88
|
-
|
|
65
|
+
@globals[:use_wsa_headers] ? { 'xmlns:wsa' => WSA_NAMESPACE } : {}
|
|
89
66
|
end
|
|
90
67
|
|
|
91
68
|
def body_attributes
|
|
92
69
|
@body_attributes ||= @signature.nil? ? {} : @signature.body_attributes
|
|
93
70
|
end
|
|
94
71
|
|
|
95
|
-
# Returns the request body as a String. When the caller supplies a pre-built
|
|
96
|
-
# envelope via the :xml local option it is used verbatim, but it must still
|
|
97
|
-
# be wrapped in a multipart message when :attachments are present.
|
|
98
|
-
# Otherwise the attachments are silently dropped.
|
|
99
72
|
def to_s
|
|
100
|
-
if @locals.include?
|
|
101
|
-
|
|
102
|
-
@locals[:attachments] ? build_multipart_message(xml) : xml
|
|
103
|
-
else
|
|
104
|
-
build_document
|
|
105
|
-
end
|
|
73
|
+
return @locals[:xml] if @locals.include? :xml
|
|
74
|
+
build_document
|
|
106
75
|
end
|
|
107
76
|
|
|
108
77
|
private
|
|
109
78
|
|
|
110
79
|
def convert_type_definitions_to_hash
|
|
111
|
-
@wsdl.type_definitions.
|
|
80
|
+
@wsdl.type_definitions.inject({}) do |memo, (path, type)|
|
|
112
81
|
memo[path] = type
|
|
82
|
+
memo
|
|
113
83
|
end
|
|
114
84
|
end
|
|
115
85
|
|
|
116
86
|
def convert_type_namespaces_to_hash
|
|
117
|
-
@wsdl.type_namespaces.
|
|
87
|
+
@wsdl.type_namespaces.inject({}) do |memo, (path, uri)|
|
|
118
88
|
key, value = use_namespace(path, uri)
|
|
119
89
|
memo[key] = value
|
|
90
|
+
memo
|
|
120
91
|
end
|
|
121
92
|
end
|
|
122
93
|
|
|
123
94
|
def use_namespace(path, uri)
|
|
124
95
|
@internal_namespace_count ||= 0
|
|
125
96
|
|
|
126
|
-
unless
|
|
127
|
-
|
|
128
|
-
# The prefix may already be taken by the target namespace or a user-supplied
|
|
129
|
-
# :namespaces override - fall back to ins0, ins1... rather than overwriting it.
|
|
130
|
-
wsdl_identifier = nil if wsdl_identifier && namespaces.key?("xmlns:#{wsdl_identifier}")
|
|
131
|
-
identifier = wsdl_identifier || "ins#{@internal_namespace_count}"
|
|
97
|
+
unless identifier = namespace_by_uri(uri)
|
|
98
|
+
identifier = "ins#{@internal_namespace_count}"
|
|
132
99
|
namespaces["xmlns:#{identifier}"] = uri
|
|
133
|
-
@internal_namespace_count += 1
|
|
100
|
+
@internal_namespace_count += 1
|
|
134
101
|
end
|
|
135
102
|
|
|
136
103
|
[path, identifier]
|
|
@@ -149,9 +116,21 @@ module Savon
|
|
|
149
116
|
@globals[:namespace] || @wsdl.namespace
|
|
150
117
|
|
|
151
118
|
# check env_namespace
|
|
152
|
-
namespaces["xmlns#{env_namespace && env_namespace !=
|
|
119
|
+
namespaces["xmlns#{env_namespace && env_namespace != "" ? ":#{env_namespace}" : ''}"] =
|
|
153
120
|
SOAP_NAMESPACE[@globals[:soap_version]]
|
|
154
121
|
|
|
122
|
+
if @wsdl&.document
|
|
123
|
+
@wsdl.parser.namespaces.each do |identifier, path|
|
|
124
|
+
next if identifier == 'xmlns' # Do not include xmlns namespace as this causes issues for some servers (https://github.com/savonrb/savon/issues/986)
|
|
125
|
+
|
|
126
|
+
prefixed_identifier = "xmlns:#{identifier}"
|
|
127
|
+
|
|
128
|
+
next if namespaces.key?(prefixed_identifier)
|
|
129
|
+
|
|
130
|
+
namespaces[prefixed_identifier] = path
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
155
134
|
namespaces
|
|
156
135
|
end
|
|
157
136
|
end
|
|
@@ -161,14 +140,13 @@ module Savon
|
|
|
161
140
|
end
|
|
162
141
|
|
|
163
142
|
def header
|
|
164
|
-
@header ||= Header.new(@globals, @
|
|
143
|
+
@header ||= Header.new(@globals, @locals)
|
|
165
144
|
end
|
|
166
145
|
|
|
167
146
|
def namespaced_message_tag
|
|
168
147
|
tag_name = message_tag
|
|
169
|
-
return [tag_name] if @wsdl.document?
|
|
170
|
-
|
|
171
|
-
if namespace_identifier.nil?
|
|
148
|
+
return [tag_name] if @wsdl.document? and @wsdl.soap_input(@operation_name.to_sym).is_a?(Hash)
|
|
149
|
+
if namespace_identifier == nil
|
|
172
150
|
[tag_name, message_attributes]
|
|
173
151
|
elsif @used_namespaces[[tag_name.to_s]]
|
|
174
152
|
[@used_namespaces[[tag_name.to_s]], tag_name, message_attributes]
|
|
@@ -186,7 +164,6 @@ module Savon
|
|
|
186
164
|
message_tag = serialized_message_tag[1]
|
|
187
165
|
@wsdl.soap_input(@operation_name.to_sym)[message_tag].each_pair do |message, type|
|
|
188
166
|
break if @locals[:message].nil?
|
|
189
|
-
|
|
190
167
|
message_locals = @locals[:message][StringUtils.snakecase(message).to_sym]
|
|
191
168
|
message_content = Message.new(message_tag, namespace_identifier, @types, @used_namespaces, message_locals, :unqualified, @globals[:convert_request_keys_to], @globals[:unwrap]).to_s
|
|
192
169
|
messages += "<#{message} xsi:type=\"#{type.join(':')}\">#{message_content}</#{message}>"
|
|
@@ -200,7 +177,7 @@ module Savon
|
|
|
200
177
|
message_tag = wsdl_tag_name.keys.first if wsdl_tag_name.is_a?(Hash)
|
|
201
178
|
message_tag ||= @locals[:message_tag]
|
|
202
179
|
message_tag ||= wsdl_tag_name
|
|
203
|
-
message_tag ||= Gyoku.xml_tag(@operation_name, key_converter
|
|
180
|
+
message_tag ||= Gyoku.xml_tag(@operation_name, :key_converter => @globals[:convert_request_keys_to])
|
|
204
181
|
|
|
205
182
|
message_tag.to_sym
|
|
206
183
|
end
|
|
@@ -210,7 +187,7 @@ module Savon
|
|
|
210
187
|
end
|
|
211
188
|
|
|
212
189
|
def body_message
|
|
213
|
-
if @wsdl.document?
|
|
190
|
+
if @wsdl.document? and @wsdl.soap_input(@operation_name.to_sym).is_a?(Hash)
|
|
214
191
|
serialized_messages
|
|
215
192
|
else
|
|
216
193
|
message.to_s
|
|
@@ -244,7 +221,7 @@ module Savon
|
|
|
244
221
|
|
|
245
222
|
def builder
|
|
246
223
|
builder = ::Builder::XmlMarkup.new
|
|
247
|
-
builder.instruct!(:xml, encoding
|
|
224
|
+
builder.instruct!(:xml, :encoding => @globals[:encoding])
|
|
248
225
|
builder
|
|
249
226
|
end
|
|
250
227
|
|
|
@@ -277,7 +254,7 @@ module Savon
|
|
|
277
254
|
|
|
278
255
|
# the mail.body.encoded algorithm reorders the parts, default order is [ "text/plain", "text/enriched", "text/html" ]
|
|
279
256
|
# should redefine the sort order, because the soap request xml should be the first
|
|
280
|
-
multipart_message.body.set_sort_order ["text/xml"]
|
|
257
|
+
multipart_message.body.set_sort_order [ "text/xml" ]
|
|
281
258
|
|
|
282
259
|
multipart_message.body.encoded(multipart_message.content_transfer_encoding)
|
|
283
260
|
end
|
|
@@ -292,10 +269,10 @@ module Savon
|
|
|
292
269
|
end
|
|
293
270
|
multipart_message.add_part xml_part
|
|
294
271
|
|
|
295
|
-
#
|
|
272
|
+
#request.headers["Content-Type"] = "multipart/related; boundary=\"#{multipart_message.body.boundary}\"; type=\"text/xml\"; start=\"#{xml_part.content_id}\""
|
|
296
273
|
@multipart = {
|
|
297
274
|
multipart_boundary: multipart_message.body.boundary,
|
|
298
|
-
start: xml_part.content_id
|
|
275
|
+
start: xml_part.content_id,
|
|
299
276
|
}
|
|
300
277
|
|
|
301
278
|
multipart_message
|