savon 2.12.0 → 2.13.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +2 -0
- data/CHANGELOG.md +98 -76
- data/CONTRIBUTING.md +15 -19
- data/Gemfile +2 -7
- data/README.md +15 -19
- data/RELEASING.md +10 -0
- data/lib/savon/block_interface.rb +1 -0
- data/lib/savon/builder.rb +98 -29
- data/lib/savon/client.rb +1 -0
- data/lib/savon/core_ext/string.rb +1 -0
- data/lib/savon/header.rb +2 -6
- data/lib/savon/http_error.rb +4 -4
- data/lib/savon/log_message.rb +1 -0
- data/lib/savon/message.rb +1 -0
- data/lib/savon/mock/expectation.rb +1 -0
- data/lib/savon/mock/spec_helper.rb +1 -0
- data/lib/savon/mock.rb +1 -0
- data/lib/savon/model.rb +1 -0
- data/lib/savon/operation.rb +20 -18
- data/lib/savon/options.rb +56 -0
- data/lib/savon/qualified_message.rb +4 -3
- data/lib/savon/request.rb +5 -0
- data/lib/savon/request_logger.rb +8 -2
- data/lib/savon/response.rb +48 -1
- data/lib/savon/soap_fault.rb +2 -1
- data/lib/savon/version.rb +2 -1
- data/lib/savon.rb +1 -0
- data/savon.gemspec +9 -8
- data/spec/fixtures/response/empty_soap_fault.xml +13 -0
- data/spec/fixtures/wsdl/elements_in_types.xml +43 -0
- data/spec/integration/support/application.rb +33 -1
- data/spec/integration/support/server.rb +1 -0
- data/spec/integration/zipcode_example_spec.rb +5 -8
- data/spec/savon/builder_spec.rb +2 -1
- data/spec/savon/client_spec.rb +5 -4
- data/spec/savon/core_ext/string_spec.rb +2 -1
- data/spec/savon/features/message_tag_spec.rb +2 -1
- data/spec/savon/http_error_spec.rb +9 -1
- data/spec/savon/log_message_spec.rb +2 -1
- data/spec/savon/message_spec.rb +2 -11
- data/spec/savon/mock_spec.rb +2 -1
- data/spec/savon/model_spec.rb +2 -1
- data/spec/savon/multipart_request_spec.rb +46 -0
- data/spec/savon/observers_spec.rb +2 -1
- data/spec/savon/operation_spec.rb +20 -43
- data/spec/savon/options_spec.rb +51 -1
- data/spec/savon/qualified_message_spec.rb +2 -1
- data/spec/savon/request_logger_spec.rb +2 -1
- data/spec/savon/request_spec.rb +47 -6
- data/spec/savon/response_spec.rb +2 -1
- data/spec/savon/soap_fault_spec.rb +12 -1
- data/spec/savon/softlayer_spec.rb +17 -2
- data/spec/spec_helper.rb +5 -4
- data/spec/support/adapters.rb +1 -0
- data/spec/support/endpoint.rb +1 -0
- data/spec/support/fixture.rb +1 -0
- data/spec/support/integration.rb +1 -0
- data/spec/support/stdout.rb +1 -0
- metadata +57 -34
- data/.travis.yml +0 -18
- data/donate.png +0 -0
- data/spec/integration/centra_spec.rb +0 -67
- data/spec/integration/email_example_spec.rb +0 -32
- data/spec/integration/random_quote_spec.rb +0 -23
- data/spec/integration/ratp_example_spec.rb +0 -28
- data/spec/integration/stockquote_example_spec.rb +0 -34
- data/spec/integration/temperature_example_spec.rb +0 -46
data/Gemfile
CHANGED
@@ -1,13 +1,8 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
|
+
|
2
3
|
gemspec
|
3
4
|
|
4
5
|
gem "httpclient", "~> 2.7.1"
|
5
6
|
|
6
7
|
gem "simplecov", :require => false
|
7
|
-
gem "
|
8
|
-
|
9
|
-
platform :rbx do
|
10
|
-
gem 'racc'
|
11
|
-
gem 'rubysl'
|
12
|
-
gem 'rubinius-coverage'
|
13
|
-
end
|
8
|
+
gem "net-smtp" if RUBY_VERSION >= "3.1.0"
|
data/README.md
CHANGED
@@ -2,13 +2,13 @@
|
|
2
2
|
|
3
3
|
Heavy metal SOAP client
|
4
4
|
|
5
|
-
[Documentation](
|
5
|
+
[Documentation](https://www.rubydoc.info/gems/savon/) | [Support](https://stackoverflow.com/questions/tagged/savon) |
|
6
6
|
[Mailing list](https://groups.google.com/forum/#!forum/savonrb) | [Twitter](http://twitter.com/savonrb)
|
7
7
|
|
8
|
-
[![
|
8
|
+
[![Ruby](https://github.com/savonrb/savon/actions/workflows/ci.yml/badge.svg)](https://github.com/savonrb/savon/actions/workflows/ci.yml)
|
9
9
|
[![Gem Version](https://badge.fury.io/rb/savon.svg)](http://badge.fury.io/rb/savon)
|
10
10
|
[![Code Climate](https://codeclimate.com/github/savonrb/savon.svg)](https://codeclimate.com/github/savonrb/savon)
|
11
|
-
[![Coverage Status](https://coveralls.io/repos/savonrb/savon/badge.svg
|
11
|
+
[![Coverage Status](https://coveralls.io/repos/savonrb/savon/badge.svg)](https://coveralls.io/r/savonrb/savon)
|
12
12
|
|
13
13
|
|
14
14
|
## Version 2
|
@@ -22,7 +22,7 @@ $ gem install savon
|
|
22
22
|
or add it to your Gemfile like this:
|
23
23
|
|
24
24
|
```
|
25
|
-
gem 'savon', '~> 2.
|
25
|
+
gem 'savon', '~> 2.13.0'
|
26
26
|
```
|
27
27
|
|
28
28
|
## Usage example
|
@@ -33,6 +33,12 @@ require 'savon'
|
|
33
33
|
# create a client for the service
|
34
34
|
client = Savon.client(wsdl: 'http://service.example.com?wsdl')
|
35
35
|
|
36
|
+
# or: create a client with a wsdl provided as a string
|
37
|
+
client = Savon.client do |config|
|
38
|
+
wsdl_content = File.read("/path/to/wsdl")
|
39
|
+
config.wsdl wsdl_content
|
40
|
+
end
|
41
|
+
|
36
42
|
client.operations
|
37
43
|
# => [:find_user, :list_users]
|
38
44
|
|
@@ -47,10 +53,12 @@ For more examples, you should check out the
|
|
47
53
|
[integration tests](https://github.com/savonrb/savon/tree/version2/spec/integration).
|
48
54
|
|
49
55
|
## Ruby version support
|
50
|
-
|
56
|
+
|
57
|
+
* `master` - MRI 2.7, 3.0, 3.1 (same support as Ruby)
|
58
|
+
* 2.12.x - MRI 2.2, 2.3, 2.4, 2.5
|
51
59
|
* 2.11.x - MRI 2.0, 2.1, 2.2, and 2.3
|
52
60
|
|
53
|
-
If you are running MRI 1.8.7, try
|
61
|
+
If you are running MRI 1.8.7, try a 2.6.x release.
|
54
62
|
|
55
63
|
## Running tests
|
56
64
|
|
@@ -64,19 +72,7 @@ $ bundle exec rspec
|
|
64
72
|
* 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`.
|
65
73
|
- See https://github.com/savonrb/savon/issues/488 for more info
|
66
74
|
|
67
|
-
## Give back
|
68
|
-
|
69
|
-
If you're using Savon and you or your company is making money from it, then please consider
|
70
|
-
donating via [Gittip](https://www.gittip.com/tjarratt/) so that I can continue to improve it.
|
71
|
-
|
72
|
-
[![donate](donate.png)](https://www.gittip.com/tjarratt/)
|
73
|
-
|
74
75
|
|
75
76
|
## Documentation
|
76
77
|
|
77
|
-
Please
|
78
|
-
|
79
|
-
And if you find any problems with it or if you think something's missing,
|
80
|
-
feel free to [help out and improve the documentation](https://github.com/savonrb/savonrb.com).
|
81
|
-
|
82
|
-
Donate icon from the [Noun Project](http://thenounproject.com/noun/donate/#icon-No285).
|
78
|
+
Please be sure to [read the documentation](https://www.rubydoc.info/github/savonrb/savon/).
|
data/RELEASING.md
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
_You'll need write access to the repository and the [rubygems](https://rubygems.org/gems/savon) project to create a release._
|
2
|
+
|
3
|
+
1. On master, edit [CHANGELOG.md](https://github.com/savonrb/savon/blob/master/CHANGELOG.md) to finalize the new version number and list of all changes.
|
4
|
+
2. Bump [version.rb](https://github.com/savonrb/savon/blob/master/lib/savon/version.rb) to the version you picked in previous step.
|
5
|
+
3. **Final check**: make sure all tests are green, and that `gem build savon.gemspec` on master succeeds. If not, merge any fixes back to master and go to step 1.
|
6
|
+
4. [Draft a new release](https://github.com/savonrb/savon/releases/new) on Github.
|
7
|
+
- Create a tag matching the version in previous step - e.g. `v2.12.1` - prepend the version number with a "v".
|
8
|
+
- Use `v[version]` for the release title, and copy the changelog into the release notes.
|
9
|
+
- Click "Publish release" to commit the tag on Github.
|
10
|
+
5. `git checkout` the newly commited tag, then `gem build savon.gemspec` to build the gem package locally. Use `gem push savon-[version].gem` to publish to rubygems.
|
data/lib/savon/builder.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require "savon/header"
|
2
3
|
require "savon/message"
|
3
4
|
require "nokogiri"
|
@@ -6,6 +7,7 @@ require "gyoku"
|
|
6
7
|
|
7
8
|
module Savon
|
8
9
|
class Builder
|
10
|
+
attr_reader :multipart
|
9
11
|
|
10
12
|
SCHEMA_TYPES = {
|
11
13
|
"xmlns:xsd" => "http://www.w3.org/2001/XMLSchema",
|
@@ -36,14 +38,7 @@ module Savon
|
|
36
38
|
end
|
37
39
|
|
38
40
|
def build_document
|
39
|
-
xml_result =
|
40
|
-
tag(xml, :Header, header_attributes) { xml << header.to_s } unless header.empty?
|
41
|
-
if @globals[:no_message_tag]
|
42
|
-
tag(xml, :Body, body_attributes) { xml << message.to_s }
|
43
|
-
else
|
44
|
-
tag(xml, :Body, body_attributes) { xml.tag!(*namespaced_message_tag) { xml << body_message } }
|
45
|
-
end
|
46
|
-
end
|
41
|
+
xml_result = build_xml
|
47
42
|
|
48
43
|
# if we have a signature sign the document
|
49
44
|
if @signature
|
@@ -51,20 +46,19 @@ module Savon
|
|
51
46
|
|
52
47
|
2.times do
|
53
48
|
@header = nil
|
54
|
-
@signature.document =
|
55
|
-
tag(xml, :Header, header_attributes) { xml << header.to_s } unless header.empty?
|
56
|
-
if @globals[:no_message_tag]
|
57
|
-
tag(xml, :Body, body_attributes) { xml << message.to_s }
|
58
|
-
else
|
59
|
-
tag(xml, :Body, body_attributes) { xml.tag!(*namespaced_message_tag) { xml << message.to_s } }
|
60
|
-
end
|
61
|
-
end
|
49
|
+
@signature.document = build_xml
|
62
50
|
end
|
63
51
|
|
64
52
|
xml_result = @signature.document
|
65
53
|
end
|
66
54
|
|
67
|
-
|
55
|
+
# if there are attachments for the request, we should build a multipart message according to
|
56
|
+
# https://www.w3.org/TR/SOAP-attachments
|
57
|
+
if @locals[:attachments]
|
58
|
+
build_multipart_message(xml_result)
|
59
|
+
else
|
60
|
+
xml_result
|
61
|
+
end
|
68
62
|
end
|
69
63
|
|
70
64
|
def header_attributes
|
@@ -117,15 +111,24 @@ module Savon
|
|
117
111
|
@namespaces ||= begin
|
118
112
|
namespaces = SCHEMA_TYPES.dup
|
119
113
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
114
|
+
# check namespace_identifier
|
115
|
+
namespaces["xmlns#{namespace_identifier.nil? ? '' : ":#{namespace_identifier}"}"] =
|
116
|
+
@globals[:namespace] || @wsdl.namespace
|
117
|
+
|
118
|
+
# check env_namespace
|
119
|
+
namespaces["xmlns#{env_namespace && env_namespace != "" ? ":#{env_namespace}" : ''}"] =
|
120
|
+
SOAP_NAMESPACE[@globals[:soap_version]]
|
121
|
+
|
122
|
+
if @wsdl&.document
|
123
|
+
@wsdl.parser.namespaces.each do |identifier, path|
|
124
|
+
prefixed_identifier = identifier
|
125
|
+
prefixed_identifier = "xmlns:#{prefixed_identifier}" unless prefixed_identifier == 'xmlns'
|
125
126
|
|
126
|
-
|
127
|
-
|
128
|
-
|
127
|
+
next if namespaces.key?(prefixed_identifier)
|
128
|
+
|
129
|
+
namespaces[prefixed_identifier] = path
|
130
|
+
end
|
131
|
+
end
|
129
132
|
|
130
133
|
namespaces
|
131
134
|
end
|
@@ -162,18 +165,20 @@ module Savon
|
|
162
165
|
break if @locals[:message].nil?
|
163
166
|
message_locals = @locals[:message][message.snakecase.to_sym]
|
164
167
|
message_content = Message.new(message_tag, namespace_identifier, @types, @used_namespaces, message_locals, :unqualified, @globals[:convert_request_keys_to], @globals[:unwrap]).to_s
|
165
|
-
messages
|
168
|
+
messages += "<#{message} xsi:type=\"#{type.join(':')}\">#{message_content}</#{message}>"
|
166
169
|
end
|
167
170
|
messages
|
168
171
|
end
|
169
172
|
|
170
173
|
def message_tag
|
171
|
-
|
174
|
+
wsdl_tag_name = @wsdl.document? && @wsdl.soap_input(@operation_name.to_sym)
|
175
|
+
|
176
|
+
message_tag = wsdl_tag_name.keys.first if wsdl_tag_name.is_a?(Hash)
|
172
177
|
message_tag ||= @locals[:message_tag]
|
173
|
-
message_tag ||=
|
178
|
+
message_tag ||= wsdl_tag_name
|
174
179
|
message_tag ||= Gyoku.xml_tag(@operation_name, :key_converter => @globals[:convert_request_keys_to])
|
175
180
|
|
176
|
-
|
181
|
+
message_tag.to_sym
|
177
182
|
end
|
178
183
|
|
179
184
|
def message_attributes
|
@@ -227,5 +232,69 @@ module Savon
|
|
227
232
|
end
|
228
233
|
end
|
229
234
|
|
235
|
+
def build_xml
|
236
|
+
tag(builder, :Envelope, namespaces_with_globals) do |xml|
|
237
|
+
tag(xml, :Header, header_attributes) { xml << header.to_s } unless header.empty?
|
238
|
+
tag(xml, :Body, body_attributes) do
|
239
|
+
if @globals[:no_message_tag]
|
240
|
+
xml << message.to_s
|
241
|
+
else
|
242
|
+
xml.tag!(*namespaced_message_tag) { xml << body_message }
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
def build_multipart_message(message_xml)
|
249
|
+
multipart_message = init_multipart_message(message_xml)
|
250
|
+
add_attachments_to_multipart_message(multipart_message)
|
251
|
+
|
252
|
+
multipart_message.ready_to_send!
|
253
|
+
|
254
|
+
# the mail.body.encoded algorithm reorders the parts, default order is [ "text/plain", "text/enriched", "text/html" ]
|
255
|
+
# should redefine the sort order, because the soap request xml should be the first
|
256
|
+
multipart_message.body.set_sort_order [ "text/xml" ]
|
257
|
+
|
258
|
+
multipart_message.body.encoded(multipart_message.content_transfer_encoding)
|
259
|
+
end
|
260
|
+
|
261
|
+
def init_multipart_message(message_xml)
|
262
|
+
multipart_message = Mail.new
|
263
|
+
xml_part = Mail::Part.new do
|
264
|
+
content_type 'text/xml'
|
265
|
+
body message_xml
|
266
|
+
# in Content-Type the start parameter is recommended (RFC 2387)
|
267
|
+
content_id '<soap-request-body@soap>'
|
268
|
+
end
|
269
|
+
multipart_message.add_part xml_part
|
270
|
+
|
271
|
+
#request.headers["Content-Type"] = "multipart/related; boundary=\"#{multipart_message.body.boundary}\"; type=\"text/xml\"; start=\"#{xml_part.content_id}\""
|
272
|
+
@multipart = {
|
273
|
+
multipart_boundary: multipart_message.body.boundary,
|
274
|
+
start: xml_part.content_id,
|
275
|
+
}
|
276
|
+
|
277
|
+
multipart_message
|
278
|
+
end
|
279
|
+
|
280
|
+
def add_attachments_to_multipart_message(multipart_message)
|
281
|
+
if @locals[:attachments].is_a? Hash
|
282
|
+
# hash example: { 'att1' => '/path/to/att1', 'att2' => '/path/to/att2' }
|
283
|
+
@locals[:attachments].each do |identifier, attachment|
|
284
|
+
add_attachment_to_multipart_message(multipart_message, attachment, identifier)
|
285
|
+
end
|
286
|
+
elsif @locals[:attachments].is_a? Array
|
287
|
+
# array example: [ '/path/to/att1', '/path/to/att2' ]
|
288
|
+
# array example: [ { filename: 'att1.xml', content: '<x/>' }, { filename: 'att2.xml', content: '<y/>' } ]
|
289
|
+
@locals[:attachments].each do |attachment|
|
290
|
+
add_attachment_to_multipart_message(multipart_message, attachment, attachment.is_a?(String) ? File.basename(attachment) : attachment[:filename])
|
291
|
+
end
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
def add_attachment_to_multipart_message(multipart_message, attachment, identifier)
|
296
|
+
multipart_message.add_file attachment.clone
|
297
|
+
multipart_message.parts.last.content_id = multipart_message.parts.last.content_location = identifier.to_s
|
298
|
+
end
|
230
299
|
end
|
231
300
|
end
|
data/lib/savon/client.rb
CHANGED
data/lib/savon/header.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require "akami"
|
2
3
|
require "gyoku"
|
3
4
|
require "securerandom"
|
@@ -61,12 +62,7 @@ module Savon
|
|
61
62
|
convert_to_xml({
|
62
63
|
'wsa:Action' => @locals[:soap_action],
|
63
64
|
'wsa:To' => @globals[:endpoint],
|
64
|
-
'wsa:MessageID' => "urn:uuid:#{SecureRandom.uuid}"
|
65
|
-
attributes!: {
|
66
|
-
'wsa:MessageID' => {
|
67
|
-
"xmlns:wsa" => "http://schemas.xmlsoap.org/ws/2004/08/addressing"
|
68
|
-
}
|
69
|
-
}
|
65
|
+
'wsa:MessageID' => "urn:uuid:#{SecureRandom.uuid}"
|
70
66
|
})
|
71
67
|
end
|
72
68
|
|
data/lib/savon/http_error.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Savon
|
4
4
|
class HTTPError < Error
|
@@ -14,9 +14,9 @@ module Savon
|
|
14
14
|
attr_reader :http
|
15
15
|
|
16
16
|
def to_s
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
String.new("HTTP error (#{@http.code})").tap do |str_error|
|
18
|
+
str_error << ": #{@http.body}" unless @http.body.empty?
|
19
|
+
end
|
20
20
|
end
|
21
21
|
|
22
22
|
def to_hash
|
data/lib/savon/log_message.rb
CHANGED
data/lib/savon/message.rb
CHANGED
data/lib/savon/mock.rb
CHANGED
data/lib/savon/model.rb
CHANGED
data/lib/savon/operation.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require "savon/options"
|
2
3
|
require "savon/block_interface"
|
3
4
|
require "savon/request"
|
@@ -5,10 +6,16 @@ require "savon/builder"
|
|
5
6
|
require "savon/response"
|
6
7
|
require "savon/request_logger"
|
7
8
|
require "savon/http_error"
|
9
|
+
require "mail"
|
8
10
|
|
9
11
|
module Savon
|
10
12
|
class Operation
|
11
13
|
|
14
|
+
SOAP_REQUEST_TYPE = {
|
15
|
+
1 => "text/xml",
|
16
|
+
2 => "application/soap+xml"
|
17
|
+
}
|
18
|
+
|
12
19
|
def self.create(operation_name, wsdl, globals)
|
13
20
|
if wsdl.document?
|
14
21
|
ensure_name_is_symbol! operation_name
|
@@ -66,21 +73,7 @@ module Savon
|
|
66
73
|
private
|
67
74
|
|
68
75
|
def create_response(response)
|
69
|
-
|
70
|
-
Multipart::Response.new(response, @globals, @locals)
|
71
|
-
else
|
72
|
-
Response.new(response, @globals, @locals)
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
def multipart_supported?
|
77
|
-
return false unless @globals[:multipart] || @locals[:multipart]
|
78
|
-
|
79
|
-
if Savon.const_defined? :Multipart
|
80
|
-
true
|
81
|
-
else
|
82
|
-
raise 'Unable to find Savon::Multipart. Make sure the savon-multipart gem is installed and loaded.'
|
83
|
-
end
|
76
|
+
Response.new(response, @globals, @locals)
|
84
77
|
end
|
85
78
|
|
86
79
|
def set_locals(locals, block)
|
@@ -107,6 +100,15 @@ module Savon
|
|
107
100
|
request.url = endpoint
|
108
101
|
request.body = builder.to_s
|
109
102
|
|
103
|
+
if builder.multipart
|
104
|
+
request.gzip
|
105
|
+
request.headers["Content-Type"] = ["multipart/related",
|
106
|
+
"type=\"#{SOAP_REQUEST_TYPE[@globals[:soap_version]]}\"",
|
107
|
+
"start=\"#{builder.multipart[:start]}\"",
|
108
|
+
"boundary=\"#{builder.multipart[:multipart_boundary]}\""].join("; ")
|
109
|
+
request.headers["MIME-Version"] = "1.0"
|
110
|
+
end
|
111
|
+
|
110
112
|
# TODO: could HTTPI do this automatically in case the header
|
111
113
|
# was not specified manually? [dh, 2013-01-04]
|
112
114
|
request.headers["Content-Length"] = request.body.bytesize.to_s
|
@@ -119,11 +121,11 @@ module Savon
|
|
119
121
|
return if @locals.include?(:soap_action) && !@locals[:soap_action]
|
120
122
|
|
121
123
|
# get the soap_action from local options
|
122
|
-
|
124
|
+
@locals[:soap_action] ||
|
123
125
|
# with no local option, but a wsdl, ask it for the soap_action
|
124
|
-
|
126
|
+
@wsdl.document? && @wsdl.soap_action(@name.to_sym) ||
|
125
127
|
# if there is no soap_action up to this point, fallback to a simple default
|
126
|
-
|
128
|
+
Gyoku.xml_tag(@name, :key_converter => @globals[:convert_request_keys_to])
|
127
129
|
end
|
128
130
|
|
129
131
|
def endpoint
|
data/lib/savon/options.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require "logger"
|
2
3
|
require "httpi"
|
3
4
|
|
@@ -78,6 +79,7 @@ module Savon
|
|
78
79
|
:namespaces => {},
|
79
80
|
:logger => Logger.new($stdout),
|
80
81
|
:log => false,
|
82
|
+
:log_headers => true,
|
81
83
|
:filters => [],
|
82
84
|
:pretty_print_xml => false,
|
83
85
|
:raise_errors => true,
|
@@ -155,6 +157,11 @@ module Savon
|
|
155
157
|
@options[:read_timeout] = read_timeout
|
156
158
|
end
|
157
159
|
|
160
|
+
# Write timeout in seconds.
|
161
|
+
def write_timeout(write_timeout)
|
162
|
+
@options[:write_timeout] = write_timeout
|
163
|
+
end
|
164
|
+
|
158
165
|
# The encoding to use. Defaults to "UTF-8".
|
159
166
|
def encoding(encoding)
|
160
167
|
@options[:encoding] = encoding
|
@@ -213,6 +220,11 @@ module Savon
|
|
213
220
|
@options[:logger].level = levels[level]
|
214
221
|
end
|
215
222
|
|
223
|
+
# To log headers or not.
|
224
|
+
def log_headers(log_headers)
|
225
|
+
@options[:log_headers] = log_headers
|
226
|
+
end
|
227
|
+
|
216
228
|
# A list of XML tags to filter from logged SOAP messages.
|
217
229
|
def filters(*filters)
|
218
230
|
@options[:filters] = filters.flatten
|
@@ -228,6 +240,16 @@ module Savon
|
|
228
240
|
@options[:ssl_version] = version
|
229
241
|
end
|
230
242
|
|
243
|
+
# Specifies the SSL version to use.
|
244
|
+
def ssl_min_version(version)
|
245
|
+
@options[:ssl_min_version] = version
|
246
|
+
end
|
247
|
+
|
248
|
+
# Specifies the SSL version to use.
|
249
|
+
def ssl_max_version(version)
|
250
|
+
@options[:ssl_max_version] = version
|
251
|
+
end
|
252
|
+
|
231
253
|
# Whether and how to to verify the connection.
|
232
254
|
def ssl_verify_mode(verify_mode)
|
233
255
|
@options[:ssl_verify_mode] = verify_mode
|
@@ -396,6 +418,40 @@ module Savon
|
|
396
418
|
@options[:attributes] = attributes
|
397
419
|
end
|
398
420
|
|
421
|
+
# Attachments for the SOAP message (https://www.w3.org/TR/SOAP-attachments)
|
422
|
+
#
|
423
|
+
# should pass an Array or a Hash; items should be path strings or
|
424
|
+
# { filename: 'file.name', content: 'content' } objects
|
425
|
+
# The Content-ID in multipart message sections will be the filename or the key if Hash is given
|
426
|
+
#
|
427
|
+
# usage examples:
|
428
|
+
#
|
429
|
+
# response = client.call :operation1 do
|
430
|
+
# message param1: 'value'
|
431
|
+
# attachments [
|
432
|
+
# { filename: 'x1.xml', content: '<xml>abc</xml>'},
|
433
|
+
# { filename: 'x2.xml', content: '<xml>abc</xml>'}
|
434
|
+
# ]
|
435
|
+
# end
|
436
|
+
# # Content-ID will be x1.xml and x2.xml
|
437
|
+
#
|
438
|
+
# response = client.call :operation1 do
|
439
|
+
# message param1: 'value'
|
440
|
+
# attachments 'x1.xml' => '/tmp/1281ab7d7d.xml', 'x2.xml' => '/tmp/4c5v8e833a.xml'
|
441
|
+
# end
|
442
|
+
# # Content-ID will be x1.xml and x2.xml
|
443
|
+
#
|
444
|
+
# response = client.call :operation1 do
|
445
|
+
# message param1: 'value'
|
446
|
+
# attachments [ '/tmp/1281ab7d7d.xml', '/tmp/4c5v8e833a.xml']
|
447
|
+
# end
|
448
|
+
# # Content-ID will be 1281ab7d7d.xml and 4c5v8e833a.xml
|
449
|
+
#
|
450
|
+
# The Content-ID is important if you want to refer to the attachments from the SOAP request
|
451
|
+
def attachments(attachments)
|
452
|
+
@options[:attachments] = attachments
|
453
|
+
end
|
454
|
+
|
399
455
|
# Value of the SOAPAction HTTP header.
|
400
456
|
def soap_action(soap_action)
|
401
457
|
@options[:soap_action] = soap_action
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require "gyoku"
|
2
3
|
|
3
4
|
module Savon
|
@@ -26,7 +27,7 @@ module Savon
|
|
26
27
|
translated_key = translate_tag(key)
|
27
28
|
newkey = add_namespaces_to_values(key, path).first
|
28
29
|
newpath = path + [translated_key]
|
29
|
-
newhash[newkey] = to_hash(value, newpath)
|
30
|
+
newhash[newkey] = to_hash(value, @types[newpath] ? [@types[newpath]] : newpath)
|
30
31
|
end
|
31
32
|
end
|
32
33
|
newhash
|
@@ -43,8 +44,8 @@ module Savon
|
|
43
44
|
Array(values).collect do |value|
|
44
45
|
translated_value = translate_tag(value)
|
45
46
|
namespace_path = path + [translated_value]
|
46
|
-
namespace = @used_namespaces[namespace_path]
|
47
|
-
namespace.
|
47
|
+
namespace = @used_namespaces[namespace_path] || ''
|
48
|
+
namespace.empty? ? value : "#{namespace}:#{translated_value}"
|
48
49
|
end
|
49
50
|
end
|
50
51
|
end
|
data/lib/savon/request.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require "httpi"
|
2
3
|
|
3
4
|
module Savon
|
@@ -21,10 +22,14 @@ module Savon
|
|
21
22
|
def configure_timeouts
|
22
23
|
@http_request.open_timeout = @globals[:open_timeout] if @globals.include? :open_timeout
|
23
24
|
@http_request.read_timeout = @globals[:read_timeout] if @globals.include? :read_timeout
|
25
|
+
@http_request.write_timeout = @globals[:write_timeout] if @globals.include? :write_timeout
|
24
26
|
end
|
25
27
|
|
26
28
|
def configure_ssl
|
27
29
|
@http_request.auth.ssl.ssl_version = @globals[:ssl_version] if @globals.include? :ssl_version
|
30
|
+
@http_request.auth.ssl.min_version = @globals[:ssl_min_version] if @globals.include? :ssl_min_version
|
31
|
+
@http_request.auth.ssl.max_version = @globals[:ssl_max_version] if @globals.include? :ssl_max_version
|
32
|
+
|
28
33
|
@http_request.auth.ssl.verify_mode = @globals[:ssl_verify_mode] if @globals.include? :ssl_verify_mode
|
29
34
|
@http_request.auth.ssl.ciphers = @globals[:ssl_ciphers] if @globals.include? :ssl_ciphers
|
30
35
|
|
data/lib/savon/request_logger.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require "savon/log_message"
|
2
3
|
|
3
4
|
module Savon
|
@@ -23,21 +24,26 @@ module Savon
|
|
23
24
|
@globals[:log]
|
24
25
|
end
|
25
26
|
|
27
|
+
def log_headers?
|
28
|
+
@globals[:log_headers]
|
29
|
+
end
|
30
|
+
|
26
31
|
private
|
27
32
|
|
28
33
|
def log_request(request)
|
29
34
|
logger.info { "SOAP request: #{request.url}" }
|
30
|
-
logger.info { headers_to_log(request.headers) }
|
35
|
+
logger.info { headers_to_log(request.headers) } if log_headers?
|
31
36
|
logger.debug { body_to_log(request.body) }
|
32
37
|
end
|
33
38
|
|
34
39
|
def log_response(response)
|
35
40
|
logger.info { "SOAP response (status #{response.code})" }
|
41
|
+
logger.debug { headers_to_log(response.headers) } if log_headers?
|
36
42
|
logger.debug { body_to_log(response.body) }
|
37
43
|
end
|
38
44
|
|
39
45
|
def headers_to_log(headers)
|
40
|
-
headers.map { |key, value| "#{key}: #{value}" }.join("
|
46
|
+
headers.map { |key, value| "#{key}: #{value}" }.join("\n")
|
41
47
|
end
|
42
48
|
|
43
49
|
def body_to_log(body)
|