savon 2.17.0 → 2.17.1

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: e494bfc08714bcce13c230100c2d5677ac2b40cca1a5afb2312534b41c4c81e9
4
- data.tar.gz: fb3b31cb0684a0e7742f247f121a9d3d5749fc932ea089ae26bf4af59fd48e24
3
+ metadata.gz: 00ae14802f108cb0df2757b85888a35df4b6b0e462ae0d01c1eedc9b4f9c123c
4
+ data.tar.gz: cee1d2d13ed6e869611817271c43352c470c4c7fb79e72dc7e6e7a4d9ac37191
5
5
  SHA512:
6
- metadata.gz: b79c099e954cb9bb176a898f515d12bcaaa557d5d15045155029e6322f3121b5304daf76e07d0927323f3e6481b43aee754d0bfa516eb0890807f12ecc6c6053
7
- data.tar.gz: f39e6773c7c20d254857a420f6c54f38b74e1e0e64cf2e51254ca4ab43b482e2bb910860fc1f86ce86c18653e940caae115b5ace3dcad8c83db819370dd77b8c
6
+ metadata.gz: 8b754234ac51e2213f92254a88345743cc54e09026e220d85931b8bfe0ba78f5a417f81e561047b37410f52713f3583120c6d3095ab68a2b9705427f03822d07
7
+ data.tar.gz: 49a3260e064073d9370b80b376db5f9c75bab5e38dbdf7db2ffc8533dc2a58652bb0699830468f1e59319a98fd7210503a6c999b386e3ec6c9f4d23ac7f813e1
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Savon changelog
2
2
 
3
+ ## 2.17.1 (2026-05-21)
4
+
5
+ * Fix: [#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.
6
+ * Fix: 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.
7
+
3
8
  ## 2.17.0 (2026-05-19)
4
9
 
5
10
  **Add opt-in Faraday transport**
data/README.md CHANGED
@@ -2,103 +2,78 @@
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)
7
-
8
5
  [![Ruby](https://github.com/savonrb/savon/actions/workflows/ci.yml/badge.svg)](https://github.com/savonrb/savon/actions/workflows/ci.yml)
9
6
  [![Gem Version](https://badge.fury.io/rb/savon.svg)](http://badge.fury.io/rb/savon)
10
7
  [![Coverage Status](https://coveralls.io/repos/savonrb/savon/badge.svg)](https://coveralls.io/r/savonrb/savon)
11
8
 
9
+ Savon is a SOAP client for Ruby. [SOAP is the protocol](https://www.w3.org/TR/soap/) spoken by many enterprise systems in banking, government, ERP or payroll. 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.
12
10
 
13
- ## Installation
14
-
15
- Savon is available through [Rubygems](http://rubygems.org/gems/savon) and can be installed via:
16
-
17
- ```
18
- $ gem install savon
19
- ```
11
+ Full documentation is at [savonrb.com](https://savonrb.com).
20
12
 
21
- or add it to your Gemfile like this:
13
+ ## Installation
22
14
 
23
- ```
24
- gem 'savon', '~> 2.15.0'
15
+ ```ruby
16
+ gem 'savon', '~> 2.17'
25
17
  ```
26
18
 
27
- ## Usage example
19
+ ## Usage
28
20
 
29
- ``` ruby
21
+ ```ruby
30
22
  require 'savon'
31
23
 
32
- # create a client for the service
33
- client = Savon.client(wsdl: 'http://service.example.com?wsdl')
34
-
35
- # or: create a client with a wsdl provided as a string
36
- client = Savon.client do |config|
37
- wsdl_content = File.read("/path/to/wsdl")
38
- config.wsdl wsdl_content
39
- end
24
+ # Point Savon to a local or remote WSDL document
25
+ client = Savon.client(wsdl: 'https://service.example.com?wsdl')
40
26
 
27
+ # See what the service exposes
41
28
  client.operations
42
- # => [:find_user, :list_users]
29
+ # => [:find_user, :create_user]
43
30
 
44
- # call the 'findUser' operation
31
+ # Make a request and work with the response
45
32
  response = client.call(:find_user, message: { id: 42 })
33
+ response.body[:find_user_response]
34
+ # => { id: 42, name: "Hoff" }
46
35
 
47
- response.body
48
- # => { find_user_response: { id: 42, name: 'Hoff' } }
36
+ # Savon raises a Savon::SOAPFault when the server returns a SOAP fault
37
+ rescue Savon::SOAPFault => e
38
+ puts e.to_hash.dig(:fault, :faultstring)
49
39
  ```
50
40
 
51
- For more examples, you should check out the
52
- [integration tests](https://github.com/savonrb/savon/tree/version2/spec/integration).
53
-
54
- ## Faraday transport
55
-
56
- Savon uses HTTPI for HTTP by default. To opt into Faraday instead, add `faraday` to your Gemfile and set `transport: :faraday`:
41
+ Enable logging to see the raw SOAP envelopes. That's probably the single most useful thing while getting a new integration working:
57
42
 
58
43
  ```ruby
59
44
  client = Savon.client(
60
- transport: :faraday,
61
- wsdl: "http://service.example.com?wsdl"
45
+ wsdl: 'https://service.example.com?wsdl',
46
+ pretty_print_xml: true,
47
+ log: true
62
48
  )
63
49
  ```
64
50
 
65
- Configure SSL, auth, timeouts, middleware, and anything else transport-related on the `Faraday::Connection` before making calls:
51
+ ### Authentication
66
52
 
67
- ```ruby
68
- client.faraday.ssl.verify = false
69
- client.faraday.options.timeout = 30
70
- client.faraday.options.open_timeout = 5
71
- client.faraday.headers["Authorization"] = "Bearer #{token}"
72
- client.faraday.use Faraday::Response::Logger
73
- ```
53
+ Most enterprise services require authentication. Common options:
74
54
 
75
- ## Ruby version support
76
-
77
- Every savon release is tested with contemporary supported versions of ruby. Historical compatibility information:
55
+ ```ruby
56
+ # HTTP basic auth
57
+ Savon.client(wsdl: '...', basic_auth: ['user', 'secret'])
78
58
 
79
- * `main` - same support as Ruby
80
- * 2.15.x - MRI 3.0, 3.1, 3.2, 3.3
81
- * 2.13.x, 2.14.x - MRI 2.7, 3.0, 3.1
82
- * 2.12.x - MRI 2.2, 2.3, 2.4, 2.5
83
- * 2.11.x - MRI 2.0, 2.1, 2.2, and 2.3
59
+ # WS-Security (WSSE)
60
+ Savon.client(wsdl: '...', wsse_auth: ['user', 'secret', :digest], wsse_timestamp: true)
61
+ ```
84
62
 
85
- If you are running MRI 1.8.7, try a 2.6.x release.
63
+ See [Authentication](https://savonrb.com/version2/globals.html) on the website for HTTP digest, NTLM, and certificate-based options.
86
64
 
87
- Most changes are not backported to older versions of savon, or unsupported ruby versions.
65
+ ### Transport
88
66
 
89
- ## Running tests
67
+ Savon uses [HTTPI](https://github.com/savonrb/httpi) for HTTP by default. Since v2.17.0 you can opt-in to use Faraday by passing `transport: :faraday` which exposes the Faraday connection at `client.faraday` for you to configure. Savon is only adding some SOAP-specifics on top.
90
68
 
91
- ```bash
92
- $ bundle install
93
- $ bundle exec rspec
94
- ```
69
+ ## Ruby support
95
70
 
96
- ## FAQ
71
+ Savon 2.x requires Ruby 3.0 or later. See the [changelog](CHANGELOG.md) for historical compatibility.
97
72
 
98
- * 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`.
99
- - See https://github.com/savonrb/savon/issues/488 for more info
73
+ ## Known limitations
100
74
 
75
+ **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.
101
76
 
102
- ## Documentation
77
+ ## Contributing
103
78
 
104
- Please be sure to [read the documentation](https://www.rubydoc.info/github/savonrb/savon/).
79
+ See [CONTRIBUTING.md](CONTRIBUTING.md). MIT licensed.
@@ -78,7 +78,9 @@ module Savon
78
78
 
79
79
  response =
80
80
  if response.nil?
81
- @transport.post(endpoint.to_s, soap_headers(builder), builder.to_s, @locals)
81
+ body = builder.to_s
82
+ headers = soap_headers(builder)
83
+ @transport.post(endpoint.to_s, headers, body, @locals)
82
84
  else
83
85
  normalize_observer_response(response)
84
86
  end
@@ -97,7 +99,9 @@ module Savon
97
99
  end
98
100
 
99
101
  builder = build(locals, &block)
100
- @transport.to_httpi_request(endpoint.to_s, soap_headers(builder), builder.to_s, @locals)
102
+ # Build the body before soap_headers - see #call.
103
+ body = builder.to_s
104
+ @transport.to_httpi_request(endpoint.to_s, soap_headers(builder), body, @locals)
101
105
  end
102
106
 
103
107
  private
@@ -8,7 +8,7 @@ module Savon
8
8
  # Faraday-backed HTTP transport for the opt-in Faraday path.
9
9
  #
10
10
  # Encapsulates everything Faraday-specific:
11
- # * header assembly (SOAP + global + local + cookies + Content-Length)
11
+ # * header assembly (SOAP + global + local + cookies)
12
12
  # * request execution via the caller-configured Faraday::Connection
13
13
  #
14
14
  # Transport-level concerns (SSL, auth, proxy, timeouts, middleware) are
@@ -33,7 +33,7 @@ module Savon
33
33
  # @param locals [Savon::LocalOptions] per-request options
34
34
  # @return [Transport::Response]
35
35
  def post(url, soap_headers, body, locals)
36
- headers = build_headers(soap_headers, body, locals)
36
+ headers = build_headers(soap_headers, locals)
37
37
 
38
38
  log_request(url, headers, body) if log?
39
39
 
@@ -49,8 +49,8 @@ module Savon
49
49
 
50
50
  # Merges all header sources in precedence order:
51
51
  # locals[:headers] > globals[:headers] > soap_headers
52
- # Appends Cookie from locals[:cookies] and Content-Length from body.
53
- def build_headers(soap_headers, body, locals)
52
+ # Appends Cookie from locals[:cookies].
53
+ def build_headers(soap_headers, locals)
54
54
  headers = {}
55
55
  headers.merge!(@globals[:headers]) if @globals.include?(:headers)
56
56
  headers.merge!(locals[:headers]) if locals.include?(:headers)
@@ -62,7 +62,6 @@ module Savon
62
62
  headers["Cookie"] = locals[:cookies].map(&:name_and_value).join(";")
63
63
  end
64
64
 
65
- headers["Content-Length"] = body.bytesize.to_s
66
65
  headers
67
66
  end
68
67
  end
@@ -60,8 +60,6 @@ module Savon
60
60
  headers["Cookie"] = locals[:cookies].map(&:name_and_value).join(";")
61
61
  end
62
62
 
63
- headers["Content-Length"] = body.bytesize.to_s
64
-
65
63
  http_request = ::HTTPI::Request.new
66
64
  http_request.url = url
67
65
  http_request.body = body
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.0'
4
+ VERSION = '2.17.1'
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.0
4
+ version: 2.17.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Harrington