savon 2.17.1 → 2.17.2
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 +78 -20
- data/README.md +12 -2
- data/lib/savon/builder.rb +10 -3
- data/lib/savon/faraday_migration_hint.rb +186 -0
- data/lib/savon/model.rb +15 -10
- data/lib/savon/operation.rb +7 -2
- data/lib/savon/options.rb +86 -69
- data/lib/savon/response.rb +3 -0
- data/lib/savon/transport/faraday.rb +34 -5
- data/lib/savon/transport/httpi.rb +7 -6
- data/lib/savon/transport/response.rb +11 -11
- data/lib/savon/version.rb +1 -1
- metadata +9 -14
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ffcf3a770d4dd548ec193c9f6d8b8723eef3a661273dc50e8bb5a06ba99f0f13
|
|
4
|
+
data.tar.gz: d03d1de9d1bc5b1af722b56dc71e99acc1a46f951f45d377b9eea2974016f2ed
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5a5e490cbf30a0e2216439dc9ceb51493c5b5fec107ce3ff4219eb8c5ded1c9e3b612b6bb8b44c4163d12a97c73415f441f33542b35e22bd7b1908f8cb123fa2
|
|
7
|
+
data.tar.gz: c6d5c85d39f406f75e5e0b005b250e5a97da5d60e42ca44fe827d72f97b2fd967a054684be73e8134c19506e8da6e8dcc7222769fa017d2b7c02cf2336e6dc12
|
data/CHANGELOG.md
CHANGED
|
@@ -1,38 +1,91 @@
|
|
|
1
1
|
# Savon changelog
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
-
## 2.17.
|
|
8
|
+
## [2.17.2] - 2026-06-10
|
|
9
|
+
|
|
10
|
+
**Fix CVE-2026-53510 and restore 2.17.0 cookie regressions**
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
* **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.
|
|
15
|
+
* **`: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.
|
|
16
|
+
* **`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.
|
|
17
|
+
* **`: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.
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
* **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.
|
|
22
|
+
* **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.
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
* **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`.
|
|
27
|
+
* **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.
|
|
28
|
+
|
|
29
|
+
### Deprecated
|
|
30
|
+
|
|
31
|
+
* 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.
|
|
32
|
+
|
|
33
|
+
## [2.17.1] - 2026-05-21
|
|
34
|
+
|
|
35
|
+
### Fixed
|
|
36
|
+
|
|
37
|
+
* [#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.
|
|
38
|
+
* 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.
|
|
39
|
+
|
|
40
|
+
## [2.17.0] - 2026-05-19
|
|
9
41
|
|
|
10
42
|
**Add opt-in Faraday transport**
|
|
11
43
|
|
|
12
44
|
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.
|
|
13
45
|
|
|
14
|
-
|
|
15
|
-
* Add: `client.faraday` returns a memoized `Faraday::Connection` for configuring middleware, SSL, auth, and timeouts when using the Faraday transport.
|
|
16
|
-
* Add: `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.
|
|
17
|
-
* Change: Observers must return `Savon::Transport::Response` (or `nil`) instead of `HTTPI::Response`. Returning `HTTPI::Response` still works but emits a deprecation warning.
|
|
18
|
-
* Unblocks:
|
|
19
|
-
* redirect following for WSDL fetches via `faraday-follow-redirects` middleware (#1033, savonrb/wasabi#18)
|
|
20
|
-
* digest authentication via `faraday-digestauth` middleware (#1021, savonrb/httpi#250)
|
|
21
|
-
* proxy authentication with special characters in passwords (#941)
|
|
22
|
-
* and setting an `Accept` header for WSDL requests from Rails apps (savonrb/wasabi#115)
|
|
46
|
+
### Added
|
|
23
47
|
|
|
24
|
-
|
|
48
|
+
* `transport: :faraday` global option. Defaults to `:httpi` (#992).
|
|
49
|
+
* `client.faraday` returns a memoized `Faraday::Connection` for configuring middleware, SSL, auth, and timeouts when using the Faraday transport.
|
|
50
|
+
* `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.
|
|
51
|
+
|
|
52
|
+
### Changed
|
|
53
|
+
|
|
54
|
+
* Observers must return `Savon::Transport::Response` (or `nil`) instead of `HTTPI::Response`.
|
|
55
|
+
|
|
56
|
+
### Deprecated
|
|
57
|
+
|
|
58
|
+
* 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.
|
|
59
|
+
* Returning `HTTPI::Response` from observers emits a deprecation warning and will be removed in 3.0. Return `Savon::Transport::Response` instead.
|
|
60
|
+
|
|
61
|
+
The Faraday transport unblocks:
|
|
62
|
+
|
|
63
|
+
* redirect following for WSDL fetches via `faraday-follow-redirects` middleware (#1033, savonrb/wasabi#18)
|
|
64
|
+
* digest authentication via `faraday-digestauth` middleware (#1021, savonrb/httpi#250)
|
|
65
|
+
* proxy authentication with special characters in passwords (#941)
|
|
66
|
+
* and setting an `Accept` header for WSDL requests from Rails apps (savonrb/wasabi#115)
|
|
67
|
+
|
|
68
|
+
## [2.16.0] - 2026-05-18
|
|
25
69
|
|
|
26
70
|
**Restore compatibility**
|
|
27
71
|
|
|
28
72
|
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.
|
|
29
73
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
74
|
+
### Fixed
|
|
75
|
+
|
|
76
|
+
* 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.
|
|
77
|
+
* 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.
|
|
78
|
+
* 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.
|
|
79
|
+
* Raise a proper `SOAPFault` instead of a raw exception when `soap:Fault` contains invalid encoding (#923).
|
|
80
|
+
* `SOAPFault.present?` was ignoring its `xml` argument and always operating on the instance's own body.
|
|
81
|
+
|
|
82
|
+
### Changed
|
|
83
|
+
|
|
84
|
+
* Added Ruby 3.4 (#1024) and Ruby 4.0 (#1039) to the CI test matrix.
|
|
85
|
+
|
|
86
|
+
### Deprecated
|
|
87
|
+
|
|
88
|
+
* `Savon::Response#hash` emits a deprecation warning on each call. Use `#full_hash` going forward. Will be removed in 3.0.
|
|
36
89
|
|
|
37
90
|
## 2.15.1 (2024-07-08)
|
|
38
91
|
|
|
@@ -1186,3 +1239,8 @@ Pay attention to the following list and read the updated Wiki: http://wiki.githu
|
|
|
1186
1239
|
## 0.5.0 (2009-11-29)
|
|
1187
1240
|
|
|
1188
1241
|
* Complete rewrite and public release.
|
|
1242
|
+
|
|
1243
|
+
[2.17.2]: https://github.com/savonrb/savon/compare/v2.17.1...v2.17.2
|
|
1244
|
+
[2.17.1]: https://github.com/savonrb/savon/compare/v2.17.0...v2.17.1
|
|
1245
|
+
[2.17.0]: https://github.com/savonrb/savon/compare/v2.16.0...v2.17.0
|
|
1246
|
+
[2.16.0]: https://github.com/savonrb/savon/compare/v2.15.1...v2.16.0
|
data/README.md
CHANGED
|
@@ -6,7 +6,7 @@ Heavy metal SOAP client
|
|
|
6
6
|
[](http://badge.fury.io/rb/savon)
|
|
7
7
|
[](https://coveralls.io/r/savonrb/savon)
|
|
8
8
|
|
|
9
|
-
Savon is a SOAP client for Ruby. [SOAP is the protocol](https://www.w3.org/TR/soap/) spoken by many enterprise systems
|
|
9
|
+
Savon is a SOAP client for Ruby. [SOAP is the protocol](https://www.w3.org/TR/soap/) spoken by many enterprise systems. When they hand you a WSDL URL or file instead of a REST spec, it's SOAP. Savon reads the WSDL, maps available operations to Ruby symbols, converts Ruby hashes to SOAP envelopes and turns XML responses into hashes you can work with.
|
|
10
10
|
|
|
11
11
|
Full documentation is at [savonrb.com](https://savonrb.com).
|
|
12
12
|
|
|
@@ -68,7 +68,17 @@ Savon uses [HTTPI](https://github.com/savonrb/httpi) for HTTP by default. Since
|
|
|
68
68
|
|
|
69
69
|
## Ruby support
|
|
70
70
|
|
|
71
|
-
Savon 2.x requires Ruby 3.0 or
|
|
71
|
+
Savon 2.x requires Ruby >= 3.0.0, kept as the lower bound for backward compatibility. Note that Ruby 3.0–3.3 are EOL or security-only. Ruby 3.4+ is the minimum version with active maintenance.
|
|
72
|
+
|
|
73
|
+
## Versioning & stability
|
|
74
|
+
|
|
75
|
+
Savon follows [Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0.html). The changelog format is [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
76
|
+
|
|
77
|
+
**The 2.x line is anchored on 2.12.1.** Every 2.x release is supposed to be safe to upgrade to from 2.12.1, and anything that worked in 2.12.1 keeps working. We do not remove or rename public APIs in the 2.x line. New behavior is opt-in and requires an explicit option change. If you found a problem with that, please let us know.
|
|
78
|
+
|
|
79
|
+
We only soft-deprecate APIs we plan to remove. The `Deprecated` sections of the [changelog](CHANGELOG.md) list every API that is planned to be removed with version 3.0.
|
|
80
|
+
|
|
81
|
+
Callers on 2.13.0–2.15.x may see specific post-2.12.1 behaviors restored to the 2.12.1 contract. This is intentional.
|
|
72
82
|
|
|
73
83
|
## Known limitations
|
|
74
84
|
|
data/lib/savon/builder.rb
CHANGED
|
@@ -70,10 +70,17 @@ module Savon
|
|
|
70
70
|
@body_attributes ||= @signature.nil? ? {} : @signature.body_attributes
|
|
71
71
|
end
|
|
72
72
|
|
|
73
|
+
# Returns the request body as a String. When the caller supplies a pre-built
|
|
74
|
+
# envelope via the :xml local option it is used verbatim, but it must still
|
|
75
|
+
# be wrapped in a multipart message when :attachments are present.
|
|
76
|
+
# Otherwise the attachments are silently dropped.
|
|
73
77
|
def to_s
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
78
|
+
if @locals.include?(:xml)
|
|
79
|
+
xml = @locals[:xml]
|
|
80
|
+
@locals[:attachments] ? build_multipart_message(xml) : xml
|
|
81
|
+
else
|
|
82
|
+
build_document
|
|
83
|
+
end
|
|
77
84
|
end
|
|
78
85
|
|
|
79
86
|
private
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "uri"
|
|
4
|
+
|
|
5
|
+
module Savon
|
|
6
|
+
# Formats the replacement Faraday setup for one HTTPI-only option.
|
|
7
|
+
class FaradayMigrationHint
|
|
8
|
+
VALUE_AWARE_OPTIONS = %i[
|
|
9
|
+
proxy
|
|
10
|
+
open_timeout
|
|
11
|
+
read_timeout
|
|
12
|
+
write_timeout
|
|
13
|
+
ssl_version
|
|
14
|
+
ssl_min_version
|
|
15
|
+
ssl_max_version
|
|
16
|
+
ssl_verify_mode
|
|
17
|
+
ssl_cert_key_file
|
|
18
|
+
ssl_cert_file
|
|
19
|
+
ssl_ca_cert_file
|
|
20
|
+
ssl_ciphers
|
|
21
|
+
ssl_ca_cert_path
|
|
22
|
+
adapter
|
|
23
|
+
].freeze
|
|
24
|
+
|
|
25
|
+
STATIC_HINTS = {
|
|
26
|
+
ssl_cert_key: "client.faraday.ssl.client_key = key",
|
|
27
|
+
ssl_cert_key_password: [
|
|
28
|
+
"key = OpenSSL::PKey.read(File.read(key_path), password)",
|
|
29
|
+
"client.faraday.ssl.client_key = key"
|
|
30
|
+
],
|
|
31
|
+
ssl_cert: "client.faraday.ssl.client_cert = cert",
|
|
32
|
+
ssl_ca_cert: [
|
|
33
|
+
"store = OpenSSL::X509::Store.new",
|
|
34
|
+
"store.set_default_paths",
|
|
35
|
+
"store.add_cert(cert)",
|
|
36
|
+
"client.faraday.ssl.cert_store = store"
|
|
37
|
+
],
|
|
38
|
+
ssl_cert_store: "client.faraday.ssl.cert_store = store",
|
|
39
|
+
basic_auth: "client.faraday.request :authorization, :basic, user, pass",
|
|
40
|
+
digest_auth: [
|
|
41
|
+
"gem 'faraday-digestauth'",
|
|
42
|
+
"require 'faraday/digestauth'",
|
|
43
|
+
"client.faraday.request :digest, user, pass"
|
|
44
|
+
],
|
|
45
|
+
ntlm: [
|
|
46
|
+
"gem 'faraday-ntlm_auth'",
|
|
47
|
+
"require 'faraday/ntlm_auth'",
|
|
48
|
+
"client.faraday.adapter :net_http_persistent",
|
|
49
|
+
"client.faraday.request :ntlm_auth, auth: [user, pass, domain]"
|
|
50
|
+
],
|
|
51
|
+
follow_redirects: [
|
|
52
|
+
"gem 'faraday-follow_redirects'",
|
|
53
|
+
"require 'faraday/follow_redirects'",
|
|
54
|
+
"client.faraday.response :follow_redirects"
|
|
55
|
+
]
|
|
56
|
+
}.freeze
|
|
57
|
+
|
|
58
|
+
OPTIONS = (VALUE_AWARE_OPTIONS + STATIC_HINTS.keys).freeze
|
|
59
|
+
|
|
60
|
+
def initialize(option, value)
|
|
61
|
+
@option = option
|
|
62
|
+
@value = value
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def message
|
|
66
|
+
hint = hint_lines
|
|
67
|
+
return " #{option} - Use: #{hint}" unless hint.is_a?(Array)
|
|
68
|
+
|
|
69
|
+
" #{option} - Use:\n#{hint.map { |line| " #{line}" }.join("\n")}"
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
attr_reader :option, :value
|
|
75
|
+
|
|
76
|
+
def hint_lines
|
|
77
|
+
case option
|
|
78
|
+
when :proxy
|
|
79
|
+
"client.faraday.proxy = #{redacted_proxy_value}"
|
|
80
|
+
when :open_timeout, :read_timeout, :write_timeout
|
|
81
|
+
"client.faraday.options.#{option} = #{value.inspect}"
|
|
82
|
+
when :ssl_version, :ssl_min_version, :ssl_max_version
|
|
83
|
+
ssl_version_hint
|
|
84
|
+
when :ssl_verify_mode
|
|
85
|
+
ssl_verify_mode_hint
|
|
86
|
+
when :ssl_cert_key_file
|
|
87
|
+
ssl_cert_key_file_hint
|
|
88
|
+
when :ssl_cert_file
|
|
89
|
+
ssl_cert_file_hint
|
|
90
|
+
when :ssl_ca_cert_file
|
|
91
|
+
"client.faraday.ssl.ca_file = #{value.inspect}"
|
|
92
|
+
when :ssl_ciphers
|
|
93
|
+
"client.faraday.ssl.ciphers = #{value.inspect}"
|
|
94
|
+
when :ssl_ca_cert_path
|
|
95
|
+
"client.faraday.ssl.ca_path = #{value.inspect}"
|
|
96
|
+
when :adapter
|
|
97
|
+
adapter_hint
|
|
98
|
+
else
|
|
99
|
+
STATIC_HINTS.fetch(option)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def redacted_proxy_value
|
|
104
|
+
uri = URI.parse(value.to_s)
|
|
105
|
+
return value.inspect unless uri.absolute? && uri.host
|
|
106
|
+
|
|
107
|
+
redacted = uri.dup
|
|
108
|
+
redacted.userinfo = "..." if redacted.userinfo
|
|
109
|
+
redacted.path = "" if redacted.respond_to?(:path=)
|
|
110
|
+
redacted.query = nil if redacted.respond_to?(:query=)
|
|
111
|
+
redacted.fragment = nil if redacted.respond_to?(:fragment=)
|
|
112
|
+
redacted.to_s.inspect
|
|
113
|
+
rescue URI::InvalidURIError
|
|
114
|
+
'"[redacted proxy URL]"'
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def ssl_version_hint
|
|
118
|
+
faraday_option = {
|
|
119
|
+
ssl_version: "version",
|
|
120
|
+
ssl_min_version: "min_version",
|
|
121
|
+
ssl_max_version: "max_version"
|
|
122
|
+
}.fetch(option)
|
|
123
|
+
|
|
124
|
+
"client.faraday.ssl.#{faraday_option} = #{value.inspect}"
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def ssl_cert_key_file_hint
|
|
128
|
+
[
|
|
129
|
+
"key = OpenSSL::PKey.read(File.read(#{value.inspect}))",
|
|
130
|
+
"client.faraday.ssl.client_key = key"
|
|
131
|
+
]
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def ssl_cert_file_hint
|
|
135
|
+
[
|
|
136
|
+
"cert = OpenSSL::X509::Certificate.new(File.read(#{value.inspect}))",
|
|
137
|
+
"client.faraday.ssl.client_cert = cert"
|
|
138
|
+
]
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def adapter_hint
|
|
142
|
+
case value
|
|
143
|
+
when :net_http
|
|
144
|
+
"client.faraday.adapter :net_http"
|
|
145
|
+
when :httpclient
|
|
146
|
+
[
|
|
147
|
+
"gem 'faraday-httpclient'",
|
|
148
|
+
"require 'faraday/httpclient'",
|
|
149
|
+
"client.faraday.adapter :httpclient"
|
|
150
|
+
]
|
|
151
|
+
when :excon
|
|
152
|
+
[
|
|
153
|
+
"gem 'faraday-excon'",
|
|
154
|
+
"require 'faraday/excon'",
|
|
155
|
+
"client.faraday.adapter :excon"
|
|
156
|
+
]
|
|
157
|
+
when :net_http_persistent
|
|
158
|
+
[
|
|
159
|
+
"gem 'faraday-net_http_persistent'",
|
|
160
|
+
"require 'faraday/net_http_persistent'",
|
|
161
|
+
"client.faraday.adapter :net_http_persistent"
|
|
162
|
+
]
|
|
163
|
+
else
|
|
164
|
+
[
|
|
165
|
+
"choose and install a Faraday adapter matching #{value.inspect}",
|
|
166
|
+
"client.faraday.adapter :net_http"
|
|
167
|
+
]
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def ssl_verify_mode_hint
|
|
172
|
+
case value
|
|
173
|
+
when :none
|
|
174
|
+
"client.faraday.ssl.verify_mode = OpenSSL::SSL::VERIFY_NONE"
|
|
175
|
+
when :peer
|
|
176
|
+
"client.faraday.ssl.verify_mode = OpenSSL::SSL::VERIFY_PEER"
|
|
177
|
+
when :fail_if_no_peer_cert
|
|
178
|
+
"client.faraday.ssl.verify_mode = OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT"
|
|
179
|
+
when :client_once
|
|
180
|
+
"client.faraday.ssl.verify_mode = OpenSSL::SSL::VERIFY_CLIENT_ONCE"
|
|
181
|
+
else
|
|
182
|
+
"client.faraday.ssl.verify_mode = OpenSSL::SSL::VERIFY_PEER or OpenSSL::SSL::VERIFY_NONE"
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|
data/lib/savon/model.rb
CHANGED
|
@@ -28,20 +28,25 @@ module Savon
|
|
|
28
28
|
|
|
29
29
|
# Defines a class-level SOAP operation.
|
|
30
30
|
def define_class_operation(operation)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
method_name = operation_method_name(operation)
|
|
32
|
+
|
|
33
|
+
class_operation_module.define_method(method_name) do |locals = {}|
|
|
34
|
+
client.call operation, locals
|
|
35
|
+
end
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
# Defines an instance-level SOAP operation.
|
|
39
39
|
def define_instance_operation(operation)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
method_name = operation_method_name(operation)
|
|
41
|
+
|
|
42
|
+
instance_operation_module.define_method(method_name) do |locals = {}|
|
|
43
|
+
self.class.public_send(method_name, locals)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Returns the generated Ruby method name for a SOAP operation.
|
|
48
|
+
def operation_method_name(operation)
|
|
49
|
+
StringUtils.snakecase(operation.to_s).to_sym
|
|
45
50
|
end
|
|
46
51
|
|
|
47
52
|
# Class methods.
|
data/lib/savon/operation.rb
CHANGED
|
@@ -177,8 +177,13 @@ module Savon
|
|
|
177
177
|
return response if response.is_a?(Transport::Response)
|
|
178
178
|
|
|
179
179
|
if response.is_a?(HTTPI::Response)
|
|
180
|
-
warn "Observers returning HTTPI::Response is deprecated - return Savon::Transport::Response instead."
|
|
181
|
-
return Transport::Response.
|
|
180
|
+
warn "Observers returning HTTPI::Response is deprecated - return Savon::Transport::Response instead.", uplevel: 1
|
|
181
|
+
return Transport::Response.new(
|
|
182
|
+
response.code,
|
|
183
|
+
response.headers,
|
|
184
|
+
response.body,
|
|
185
|
+
cookies: HTTPI::Cookie.list_from_headers(response.headers)
|
|
186
|
+
)
|
|
182
187
|
end
|
|
183
188
|
|
|
184
189
|
raise Error, "Observers need to return a Savon::Transport::Response " \
|
data/lib/savon/options.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "logger"
|
|
4
4
|
require "httpi"
|
|
5
|
+
require "savon/faraday_migration_hint"
|
|
5
6
|
|
|
6
7
|
module Savon
|
|
7
8
|
# Base class for GlobalOptions and LocalOptions.
|
|
@@ -73,46 +74,24 @@ module Savon
|
|
|
73
74
|
end
|
|
74
75
|
end
|
|
75
76
|
|
|
76
|
-
#
|
|
77
|
+
# Options that belong to HTTPI's transport layer.
|
|
77
78
|
#
|
|
78
|
-
#
|
|
79
|
-
#
|
|
80
|
-
#
|
|
79
|
+
# They still work with the default HTTPI transport. With `transport: :faraday`,
|
|
80
|
+
# Savon does not translate them because Faraday exposes its own setup API for
|
|
81
|
+
# proxy, timeout, TLS, auth, redirects, and adapter choices.
|
|
81
82
|
module HTTPITransportOptions
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
# sets a value that differs from the GlobalOptions default.
|
|
86
|
-
FARADAY_INCOMPATIBLE_GLOBALS = {
|
|
87
|
-
proxy: { hint: "client.faraday.proxy = url" },
|
|
88
|
-
open_timeout: { hint: "client.faraday.options.timeout = N" },
|
|
89
|
-
read_timeout: { hint: "client.faraday.options.timeout = N" },
|
|
90
|
-
write_timeout: { hint: "client.faraday.options.write_timeout = N" },
|
|
91
|
-
ssl_version: { hint: "client.faraday.ssl.version = version" },
|
|
92
|
-
ssl_min_version: { hint: "client.faraday.ssl.min_version = version" },
|
|
93
|
-
ssl_max_version: { hint: "client.faraday.ssl.max_version = version" },
|
|
94
|
-
ssl_verify_mode: { hint: "client.faraday.ssl.verify = true/false" },
|
|
95
|
-
ssl_cert_key_file: { hint: "client.faraday.ssl.client_key_file = path" },
|
|
96
|
-
ssl_cert_key: { hint: "client.faraday.ssl.client_key = key" },
|
|
97
|
-
ssl_cert_key_password: { hint: "configure ssl context on client.faraday.ssl" },
|
|
98
|
-
ssl_cert_file: { hint: "client.faraday.ssl.client_cert_file = path" },
|
|
99
|
-
ssl_cert: { hint: "client.faraday.ssl.client_cert = cert" },
|
|
100
|
-
ssl_ca_cert_file: { hint: "client.faraday.ssl.ca_file = path" },
|
|
101
|
-
ssl_ca_cert: { hint: "client.faraday.ssl.ca_cert = cert" },
|
|
102
|
-
ssl_ciphers: { hint: "client.faraday.ssl.ciphers = ciphers" },
|
|
103
|
-
ssl_ca_cert_path: { hint: "client.faraday.ssl.ca_path = path" },
|
|
104
|
-
ssl_cert_store: { hint: "client.faraday.ssl.cert_store = store" },
|
|
105
|
-
basic_auth: { hint: "client.faraday.request :basic_auth, user, pass" },
|
|
106
|
-
digest_auth: { hint: "client.faraday.request :authorization, :Digest, credentials" },
|
|
107
|
-
ntlm: { hint: "client.faraday.request :ntlm, user, pass" },
|
|
108
|
-
follow_redirects: { hint: "client.faraday.use :follow_redirects", default: false },
|
|
109
|
-
adapter: { hint: "client.faraday.adapter :net_http", default: nil }
|
|
83
|
+
TRANSPORT_DEFAULTS = {
|
|
84
|
+
adapter: nil,
|
|
85
|
+
follow_redirects: false
|
|
110
86
|
}.freeze
|
|
111
87
|
|
|
112
|
-
#
|
|
113
|
-
#
|
|
114
|
-
|
|
115
|
-
|
|
88
|
+
# These are rejected for `transport: :faraday` once the caller has set a
|
|
89
|
+
# non-default value. The error points to the matching Faraday setup.
|
|
90
|
+
FARADAY_INCOMPATIBLE_GLOBALS = FaradayMigrationHint::OPTIONS
|
|
91
|
+
|
|
92
|
+
# Runs after all global options have been assigned, including options set in
|
|
93
|
+
# the client block. Reports every HTTPI-only option in one error so callers
|
|
94
|
+
# can move their transport setup to Faraday in one pass.
|
|
116
95
|
def validate_transport!
|
|
117
96
|
return unless self[:transport] == :faraday
|
|
118
97
|
|
|
@@ -122,11 +101,11 @@ module Savon
|
|
|
122
101
|
"Add to your Gemfile: gem 'faraday'"
|
|
123
102
|
end
|
|
124
103
|
|
|
125
|
-
violations = FARADAY_INCOMPATIBLE_GLOBALS.filter_map { |option
|
|
104
|
+
violations = FARADAY_INCOMPATIBLE_GLOBALS.filter_map { |option|
|
|
126
105
|
next unless include?(option)
|
|
127
|
-
next if
|
|
106
|
+
next if default_transport_option?(option)
|
|
128
107
|
|
|
129
|
-
|
|
108
|
+
FaradayMigrationHint.new(option, self[option]).message
|
|
130
109
|
}
|
|
131
110
|
|
|
132
111
|
return if violations.empty?
|
|
@@ -252,6 +231,10 @@ module Savon
|
|
|
252
231
|
|
|
253
232
|
private
|
|
254
233
|
|
|
234
|
+
def default_transport_option?(option)
|
|
235
|
+
TRANSPORT_DEFAULTS.key?(option) && self[option] == TRANSPORT_DEFAULTS[option]
|
|
236
|
+
end
|
|
237
|
+
|
|
255
238
|
# Attempts to load faraday. Returns true if available, false on LoadError.
|
|
256
239
|
def faraday_loaded?
|
|
257
240
|
require "faraday"
|
|
@@ -271,33 +254,6 @@ module Savon
|
|
|
271
254
|
|
|
272
255
|
def initialize(options = {})
|
|
273
256
|
@option_type = :global
|
|
274
|
-
|
|
275
|
-
defaults = {
|
|
276
|
-
encoding: "UTF-8",
|
|
277
|
-
soap_version: 1,
|
|
278
|
-
namespaces: {},
|
|
279
|
-
logger: Logger.new($stdout),
|
|
280
|
-
log: false,
|
|
281
|
-
log_headers: true,
|
|
282
|
-
filters: [],
|
|
283
|
-
pretty_print_xml: false,
|
|
284
|
-
raise_errors: true,
|
|
285
|
-
strip_namespaces: true,
|
|
286
|
-
delete_namespace_attributes: false,
|
|
287
|
-
convert_response_tags_to: ->(tag) { StringUtils.snakecase(tag).to_sym },
|
|
288
|
-
convert_attributes_to: ->(k, v) { [k, v] },
|
|
289
|
-
multipart: false,
|
|
290
|
-
use_wsa_headers: false,
|
|
291
|
-
no_message_tag: false,
|
|
292
|
-
unwrap: false,
|
|
293
|
-
host: nil,
|
|
294
|
-
transport: :httpi,
|
|
295
|
-
|
|
296
|
-
# httpi transport defaults
|
|
297
|
-
adapter: nil,
|
|
298
|
-
follow_redirects: false
|
|
299
|
-
}
|
|
300
|
-
|
|
301
257
|
options = defaults.merge(options)
|
|
302
258
|
|
|
303
259
|
# this option is a shortcut on the logger which needs to be set
|
|
@@ -427,6 +383,26 @@ module Savon
|
|
|
427
383
|
@options[:delete_namespace_attributes] = delete_namespace_attributes
|
|
428
384
|
end
|
|
429
385
|
|
|
386
|
+
# The value Nori assigns to empty XML tags in the SOAP response.
|
|
387
|
+
# Defaults to nil, matching Nori's default; set to "" to map empty tags
|
|
388
|
+
# to an empty String instead.
|
|
389
|
+
def empty_tag_value(value)
|
|
390
|
+
@options[:empty_tag_value] = value
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
# Instruct Nori whether to convert dashes in response tag names to
|
|
394
|
+
# underscores before they become Hash keys. Defaults to true.
|
|
395
|
+
def convert_dashes_to_underscores(convert)
|
|
396
|
+
@options[:convert_dashes_to_underscores] = convert
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
# Instruct Nori whether to scrub invalid byte sequences from the response
|
|
400
|
+
# body before parsing it. Defaults to true, which lets responses containing
|
|
401
|
+
# invalid characters still be parsed.
|
|
402
|
+
def scrub_xml(scrub)
|
|
403
|
+
@options[:scrub_xml] = scrub
|
|
404
|
+
end
|
|
405
|
+
|
|
430
406
|
# Tell Gyoku how to convert Hash key Symbols to XML tags.
|
|
431
407
|
# Accepts one of :lower_camelcase, :camelcase, :upcase, or :none.
|
|
432
408
|
def convert_request_keys_to(converter)
|
|
@@ -455,6 +431,12 @@ module Savon
|
|
|
455
431
|
|
|
456
432
|
# Instruct Savon to create a multipart response if available.
|
|
457
433
|
def multipart(multipart)
|
|
434
|
+
if multipart
|
|
435
|
+
warn "The global :multipart option has been a no-op since v2.13.0. " \
|
|
436
|
+
"Savon detects whether the response is multipart by checking if the " \
|
|
437
|
+
"response Content-Type header contains 'multipart'. You can remove" \
|
|
438
|
+
"this option from your code to make this warning disappear.", uplevel: 1
|
|
439
|
+
end
|
|
458
440
|
@options[:multipart] = multipart
|
|
459
441
|
end
|
|
460
442
|
|
|
@@ -469,12 +451,41 @@ module Savon
|
|
|
469
451
|
end
|
|
470
452
|
|
|
471
453
|
# HTTP transport to use. Accepts :httpi (default) or :faraday.
|
|
472
|
-
#
|
|
473
|
-
#
|
|
474
|
-
# the HTTPITransportOptions.
|
|
454
|
+
# With :faraday, set proxy, timeout, TLS, auth, redirect, and adapter
|
|
455
|
+
# details on `client.faraday`; the HTTPI transport options are not copied.
|
|
475
456
|
def transport(transport)
|
|
476
457
|
@options[:transport] = transport
|
|
477
458
|
end
|
|
459
|
+
|
|
460
|
+
private
|
|
461
|
+
|
|
462
|
+
# The default value for every global option.
|
|
463
|
+
def defaults
|
|
464
|
+
HTTPITransportOptions::TRANSPORT_DEFAULTS.merge(
|
|
465
|
+
encoding: "UTF-8",
|
|
466
|
+
soap_version: 1,
|
|
467
|
+
namespaces: {},
|
|
468
|
+
logger: Logger.new($stdout),
|
|
469
|
+
log: false,
|
|
470
|
+
log_headers: true,
|
|
471
|
+
filters: [],
|
|
472
|
+
pretty_print_xml: false,
|
|
473
|
+
raise_errors: true,
|
|
474
|
+
strip_namespaces: true,
|
|
475
|
+
delete_namespace_attributes: false,
|
|
476
|
+
empty_tag_value: nil,
|
|
477
|
+
convert_dashes_to_underscores: true,
|
|
478
|
+
scrub_xml: true,
|
|
479
|
+
convert_response_tags_to: ->(tag) { StringUtils.snakecase(tag).to_sym },
|
|
480
|
+
convert_attributes_to: ->(k, v) { [k, v] },
|
|
481
|
+
multipart: false,
|
|
482
|
+
use_wsa_headers: false,
|
|
483
|
+
no_message_tag: false,
|
|
484
|
+
unwrap: false,
|
|
485
|
+
host: nil,
|
|
486
|
+
transport: :httpi
|
|
487
|
+
)
|
|
488
|
+
end
|
|
478
489
|
end
|
|
479
490
|
|
|
480
491
|
# Per-request options passed to client.call.
|
|
@@ -578,6 +589,12 @@ module Savon
|
|
|
578
589
|
|
|
579
590
|
# Instruct Savon to create a multipart response if available.
|
|
580
591
|
def multipart(multipart)
|
|
592
|
+
if multipart
|
|
593
|
+
warn "The local :multipart option has been a no-op since v2.13.0. " \
|
|
594
|
+
"Savon detects whether the response is multipart by checking if the " \
|
|
595
|
+
"response Content-Type header contains 'multipart'. You can remove" \
|
|
596
|
+
"this option from your code to make this warning disappear.", uplevel: 1
|
|
597
|
+
end
|
|
581
598
|
@options[:multipart] = multipart
|
|
582
599
|
end
|
|
583
600
|
|
data/lib/savon/response.rb
CHANGED
|
@@ -155,6 +155,9 @@ module Savon
|
|
|
155
155
|
nori_options = {
|
|
156
156
|
delete_namespace_attributes: @globals[:delete_namespace_attributes],
|
|
157
157
|
strip_namespaces: @globals[:strip_namespaces],
|
|
158
|
+
empty_tag_value: @globals[:empty_tag_value],
|
|
159
|
+
convert_dashes_to_underscores: @globals[:convert_dashes_to_underscores],
|
|
160
|
+
scrub_xml: @globals[:scrub_xml],
|
|
158
161
|
convert_tags_to: @globals[:convert_response_tags_to],
|
|
159
162
|
convert_attributes_to: @globals[:convert_attributes_to],
|
|
160
163
|
advanced_typecasting: @locals[:advanced_typecasting],
|
|
@@ -38,13 +38,31 @@ module Savon
|
|
|
38
38
|
log_request(url, headers, body) if log?
|
|
39
39
|
|
|
40
40
|
faraday_response = @connection.post(url, body, headers)
|
|
41
|
-
response = Response.
|
|
41
|
+
response = Response.new(
|
|
42
|
+
faraday_response.status,
|
|
43
|
+
faraday_response.headers.to_h,
|
|
44
|
+
faraday_response.body,
|
|
45
|
+
cookies: self.class.parse_cookies(faraday_response.headers)
|
|
46
|
+
)
|
|
42
47
|
|
|
43
48
|
log_response(response) if log?
|
|
44
|
-
|
|
45
49
|
response
|
|
46
50
|
end
|
|
47
51
|
|
|
52
|
+
# Parses Set-Cookie headers into a Hash of name => value. Accepts both
|
|
53
|
+
# the Array and String form. Attributes after the first ';' are discarded.
|
|
54
|
+
def self.parse_cookies(headers)
|
|
55
|
+
raw = headers["set-cookie"] || headers["Set-Cookie"]
|
|
56
|
+
return {} unless raw
|
|
57
|
+
|
|
58
|
+
raw_array = raw.is_a?(Array) ? raw : raw.split(/,\s*/)
|
|
59
|
+
raw_array.each_with_object({}) do |cookie_str, hash|
|
|
60
|
+
name_value = cookie_str.split(";", 2).first.to_s.strip
|
|
61
|
+
name, value = name_value.split("=", 2)
|
|
62
|
+
hash[name] = value if name && !name.empty?
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
48
66
|
private
|
|
49
67
|
|
|
50
68
|
# Merges all header sources in precedence order:
|
|
@@ -58,12 +76,23 @@ module Savon
|
|
|
58
76
|
# soap_headers are lowest priority
|
|
59
77
|
soap_headers.each do |k, v| headers[k] ||= v end
|
|
60
78
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
end
|
|
79
|
+
cookie_header = format_cookies(locals[:cookies])
|
|
80
|
+
headers["Cookie"] = cookie_header if cookie_header
|
|
64
81
|
|
|
65
82
|
headers
|
|
66
83
|
end
|
|
84
|
+
|
|
85
|
+
# Builds the Cookie header from a given value.
|
|
86
|
+
# Accepts:
|
|
87
|
+
# * String - passed through verbatim
|
|
88
|
+
# * Hash - formatted as "name=value; name=value" (browser style)
|
|
89
|
+
# Returns nil when no cookies were supplied.
|
|
90
|
+
def format_cookies(cookies)
|
|
91
|
+
return nil if cookies.nil?
|
|
92
|
+
return cookies if cookies.is_a?(String)
|
|
93
|
+
|
|
94
|
+
cookies.map { |name, value| "#{name}=#{value}" }.join("; ")
|
|
95
|
+
end
|
|
67
96
|
end
|
|
68
97
|
end
|
|
69
98
|
end
|
|
@@ -34,10 +34,14 @@ module Savon
|
|
|
34
34
|
log_request(http_request.url, http_request.headers, http_request.body) if log?
|
|
35
35
|
|
|
36
36
|
http_response = ::HTTPI.post(http_request, @globals[:adapter])
|
|
37
|
-
response = Response.
|
|
37
|
+
response = Response.new(
|
|
38
|
+
http_response.code,
|
|
39
|
+
http_response.headers,
|
|
40
|
+
http_response.body,
|
|
41
|
+
cookies: ::HTTPI::Cookie.list_from_headers(http_response.headers)
|
|
42
|
+
)
|
|
38
43
|
|
|
39
44
|
log_response(response) if log?
|
|
40
|
-
|
|
41
45
|
response
|
|
42
46
|
end
|
|
43
47
|
|
|
@@ -56,14 +60,11 @@ module Savon
|
|
|
56
60
|
# soap_headers are lowest priority
|
|
57
61
|
soap_headers.each do |k, v| headers[k] ||= v end
|
|
58
62
|
|
|
59
|
-
if locals[:cookies]&.any?
|
|
60
|
-
headers["Cookie"] = locals[:cookies].map(&:name_and_value).join(";")
|
|
61
|
-
end
|
|
62
|
-
|
|
63
63
|
http_request = ::HTTPI::Request.new
|
|
64
64
|
http_request.url = url
|
|
65
65
|
http_request.body = body
|
|
66
66
|
http_request.headers = headers
|
|
67
|
+
http_request.set_cookies(locals[:cookies]) if locals[:cookies]
|
|
67
68
|
configure_http_request(http_request)
|
|
68
69
|
http_request
|
|
69
70
|
end
|
|
@@ -6,24 +6,20 @@ module Savon
|
|
|
6
6
|
#
|
|
7
7
|
# Every transport produces a Transport::Response so that higher-level code
|
|
8
8
|
# never depends on transport-specific code. Immutable once constructed.
|
|
9
|
+
#
|
|
10
|
+
# The shape of #cookies is transport-specific: HTTPI responses expose an
|
|
11
|
+
# Array of HTTPI::Cookie, while Faraday responses expose a plain Hash so
|
|
12
|
+
# Faraday users do not depend on HTTPI types.
|
|
9
13
|
class Response
|
|
10
|
-
# Creates a Transport::Response from an HTTPI::Response.
|
|
11
|
-
def self.from_httpi(httpi_response)
|
|
12
|
-
new(httpi_response.code, httpi_response.headers, httpi_response.body)
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
# Creates a Transport::Response from a Faraday::Response.
|
|
16
|
-
def self.from_faraday(faraday_response)
|
|
17
|
-
new(faraday_response.status, faraday_response.headers.to_h, faraday_response.body)
|
|
18
|
-
end
|
|
19
|
-
|
|
20
14
|
# @param code [Integer] HTTP status code
|
|
21
15
|
# @param headers [Hash] response headers
|
|
22
16
|
# @param body [String] response body
|
|
23
|
-
|
|
17
|
+
# @param cookies [Object] parsed cookies in a transport-specific shape
|
|
18
|
+
def initialize(code, headers, body, cookies: nil)
|
|
24
19
|
@code = code
|
|
25
20
|
@headers = headers
|
|
26
21
|
@body = body
|
|
22
|
+
@cookies = cookies
|
|
27
23
|
end
|
|
28
24
|
|
|
29
25
|
# Returns the HTTP status code.
|
|
@@ -35,6 +31,10 @@ module Savon
|
|
|
35
31
|
# Returns the response body string.
|
|
36
32
|
attr_reader :body
|
|
37
33
|
|
|
34
|
+
# Returns the parsed cookies in a transport-specific shape.
|
|
35
|
+
# See class-level docs.
|
|
36
|
+
attr_reader :cookies
|
|
37
|
+
|
|
38
38
|
# Returns true when the HTTP status code indicates an error (>= 300).
|
|
39
39
|
def error?
|
|
40
40
|
@code >= 300
|
data/lib/savon/version.rb
CHANGED
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.
|
|
4
|
+
version: 2.17.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Daniel Harrington
|
|
@@ -105,14 +105,14 @@ dependencies:
|
|
|
105
105
|
requirements:
|
|
106
106
|
- - "~>"
|
|
107
107
|
- !ruby/object:Gem::Version
|
|
108
|
-
version: '2.
|
|
108
|
+
version: '2.7'
|
|
109
109
|
type: :runtime
|
|
110
110
|
prerelease: false
|
|
111
111
|
version_requirements: !ruby/object:Gem::Requirement
|
|
112
112
|
requirements:
|
|
113
113
|
- - "~>"
|
|
114
114
|
- !ruby/object:Gem::Version
|
|
115
|
-
version: '2.
|
|
115
|
+
version: '2.7'
|
|
116
116
|
- !ruby/object:Gem::Dependency
|
|
117
117
|
name: wasabi
|
|
118
118
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -151,22 +151,16 @@ dependencies:
|
|
|
151
151
|
name: puma
|
|
152
152
|
requirement: !ruby/object:Gem::Requirement
|
|
153
153
|
requirements:
|
|
154
|
-
- - "
|
|
155
|
-
- !ruby/object:Gem::Version
|
|
156
|
-
version: 4.3.8
|
|
157
|
-
- - "<"
|
|
154
|
+
- - "~>"
|
|
158
155
|
- !ruby/object:Gem::Version
|
|
159
|
-
version:
|
|
156
|
+
version: 8.0.2
|
|
160
157
|
type: :development
|
|
161
158
|
prerelease: false
|
|
162
159
|
version_requirements: !ruby/object:Gem::Requirement
|
|
163
160
|
requirements:
|
|
164
|
-
- - "
|
|
165
|
-
- !ruby/object:Gem::Version
|
|
166
|
-
version: 4.3.8
|
|
167
|
-
- - "<"
|
|
161
|
+
- - "~>"
|
|
168
162
|
- !ruby/object:Gem::Version
|
|
169
|
-
version:
|
|
163
|
+
version: 8.0.2
|
|
170
164
|
- !ruby/object:Gem::Dependency
|
|
171
165
|
name: rack
|
|
172
166
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -265,6 +259,7 @@ files:
|
|
|
265
259
|
- lib/savon/block_interface.rb
|
|
266
260
|
- lib/savon/builder.rb
|
|
267
261
|
- lib/savon/client.rb
|
|
262
|
+
- lib/savon/faraday_migration_hint.rb
|
|
268
263
|
- lib/savon/header.rb
|
|
269
264
|
- lib/savon/http_error.rb
|
|
270
265
|
- lib/savon/log_message.rb
|
|
@@ -290,7 +285,7 @@ licenses:
|
|
|
290
285
|
metadata:
|
|
291
286
|
rubygems_mfa_required: 'true'
|
|
292
287
|
source_code_uri: https://github.com/savonrb/savon
|
|
293
|
-
changelog_uri: https://github.com/savonrb/savon/blob/
|
|
288
|
+
changelog_uri: https://github.com/savonrb/savon/blob/main/CHANGELOG.md
|
|
294
289
|
bug_tracker_uri: https://github.com/savonrb/savon/issues
|
|
295
290
|
rdoc_options: []
|
|
296
291
|
require_paths:
|