savon 2.12.1 → 2.15.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 +4 -4
- data/CHANGELOG.md +108 -74
- data/README.md +23 -21
- data/lib/savon/block_interface.rb +1 -0
- data/lib/savon/builder.rb +100 -30
- data/lib/savon/client.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 +4 -3
- data/lib/savon/operation.rb +20 -18
- data/lib/savon/options.rb +57 -1
- data/lib/savon/qualified_message.rb +3 -2
- data/lib/savon/request.rb +5 -0
- data/lib/savon/request_logger.rb +8 -2
- data/lib/savon/response.rb +51 -4
- data/lib/savon/soap_fault.rb +1 -0
- data/lib/savon/string_utils.rb +17 -0
- data/lib/savon/version.rb +2 -1
- data/lib/savon.rb +2 -0
- metadata +78 -102
- data/.gitignore +0 -14
- data/.travis.yml +0 -26
- data/.yardopts +0 -6
- data/CONTRIBUTING.md +0 -46
- data/Gemfile +0 -13
- data/donate.png +0 -0
- data/lib/savon/core_ext/string.rb +0 -29
- data/savon.gemspec +0 -46
- data/spec/fixtures/gzip/message.gz +0 -0
- data/spec/fixtures/response/another_soap_fault.xml +0 -14
- data/spec/fixtures/response/authentication.xml +0 -14
- data/spec/fixtures/response/empty_soap_fault.xml +0 -13
- data/spec/fixtures/response/f5.xml +0 -39
- data/spec/fixtures/response/header.xml +0 -13
- data/spec/fixtures/response/list.xml +0 -18
- data/spec/fixtures/response/multi_ref.xml +0 -39
- data/spec/fixtures/response/no_body.xml +0 -1
- data/spec/fixtures/response/soap_fault.xml +0 -8
- data/spec/fixtures/response/soap_fault12.xml +0 -18
- data/spec/fixtures/response/soap_fault_funky.xml +0 -8
- data/spec/fixtures/response/taxcloud.xml +0 -1
- data/spec/fixtures/ssl/client_cert.pem +0 -16
- data/spec/fixtures/ssl/client_encrypted_key.pem +0 -30
- data/spec/fixtures/ssl/client_encrypted_key_cert.pem +0 -24
- data/spec/fixtures/ssl/client_key.pem +0 -15
- data/spec/fixtures/wsdl/authentication.xml +0 -63
- data/spec/fixtures/wsdl/betfair.xml +0 -2981
- data/spec/fixtures/wsdl/brand.xml +0 -624
- data/spec/fixtures/wsdl/edialog.xml +0 -15416
- data/spec/fixtures/wsdl/elements_in_types.xml +0 -43
- data/spec/fixtures/wsdl/interhome.xml +0 -2137
- data/spec/fixtures/wsdl/lower_camel.xml +0 -52
- data/spec/fixtures/wsdl/multiple_namespaces.xml +0 -92
- data/spec/fixtures/wsdl/multiple_types.xml +0 -60
- data/spec/fixtures/wsdl/no_message_tag.xml +0 -1267
- data/spec/fixtures/wsdl/taxcloud.xml +0 -934
- data/spec/fixtures/wsdl/team_software.xml +0 -1
- data/spec/fixtures/wsdl/vies.xml +0 -176
- data/spec/fixtures/wsdl/wasmuth.xml +0 -153
- 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/support/application.rb +0 -82
- data/spec/integration/support/server.rb +0 -84
- data/spec/integration/temperature_example_spec.rb +0 -46
- data/spec/integration/zipcode_example_spec.rb +0 -42
- data/spec/savon/builder_spec.rb +0 -137
- data/spec/savon/client_spec.rb +0 -271
- data/spec/savon/core_ext/string_spec.rb +0 -37
- data/spec/savon/features/message_tag_spec.rb +0 -61
- data/spec/savon/http_error_spec.rb +0 -49
- data/spec/savon/log_message_spec.rb +0 -50
- data/spec/savon/message_spec.rb +0 -70
- data/spec/savon/mock_spec.rb +0 -174
- data/spec/savon/model_spec.rb +0 -182
- data/spec/savon/observers_spec.rb +0 -92
- data/spec/savon/operation_spec.rb +0 -230
- data/spec/savon/options_spec.rb +0 -1115
- data/spec/savon/qualified_message_spec.rb +0 -101
- data/spec/savon/request_logger_spec.rb +0 -37
- data/spec/savon/request_spec.rb +0 -540
- data/spec/savon/response_spec.rb +0 -275
- data/spec/savon/soap_fault_spec.rb +0 -146
- data/spec/savon/softlayer_spec.rb +0 -27
- data/spec/spec_helper.rb +0 -30
- data/spec/support/adapters.rb +0 -48
- data/spec/support/endpoint.rb +0 -25
- data/spec/support/fixture.rb +0 -39
- data/spec/support/integration.rb +0 -9
- data/spec/support/stdout.rb +0 -25
data/README.md
CHANGED
@@ -2,18 +2,18 @@
|
|
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
|
-
[](https://github.com/savonrb/savon/actions/workflows/ci.yml)
|
9
9
|
[](http://badge.fury.io/rb/savon)
|
10
10
|
[](https://codeclimate.com/github/savonrb/savon)
|
11
|
-
[](https://coveralls.io/r/savonrb/savon)
|
12
12
|
|
13
13
|
|
14
|
-
##
|
14
|
+
## Installation
|
15
15
|
|
16
|
-
Savon
|
16
|
+
Savon is available through [Rubygems](http://rubygems.org/gems/savon) and can be installed via:
|
17
17
|
|
18
18
|
```
|
19
19
|
$ gem install savon
|
@@ -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.15.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,18 @@ 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
|
+
Every savon release is tested with contemporary supported versions of ruby. Historical compatibility information:
|
58
|
+
|
59
|
+
* `main` - same support as Ruby
|
60
|
+
* 2.15.x - MRI 3.0, 3.1, 3.2, 3.3
|
61
|
+
* 2.13.x, 2.14.x - MRI 2.7, 3.0, 3.1
|
62
|
+
* 2.12.x - MRI 2.2, 2.3, 2.4, 2.5
|
51
63
|
* 2.11.x - MRI 2.0, 2.1, 2.2, and 2.3
|
52
64
|
|
53
|
-
If you are running MRI 1.8.7, try
|
65
|
+
If you are running MRI 1.8.7, try a 2.6.x release.
|
66
|
+
|
67
|
+
Most changes are not backported to older versions of savon, or unsupported ruby versions.
|
54
68
|
|
55
69
|
## Running tests
|
56
70
|
|
@@ -64,19 +78,7 @@ $ bundle exec rspec
|
|
64
78
|
* 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
79
|
- See https://github.com/savonrb/savon/issues/488 for more info
|
66
80
|
|
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
|
-
[](https://www.gittip.com/tjarratt/)
|
73
|
-
|
74
81
|
|
75
82
|
## Documentation
|
76
83
|
|
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).
|
84
|
+
Please be sure to [read the documentation](https://www.rubydoc.info/github/savonrb/savon/).
|
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,25 @@ 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
|
+
next if identifier == 'xmlns' # Do not include xmlns namespace as this causes issues for some servers (https://github.com/savonrb/savon/issues/986)
|
125
125
|
|
126
|
-
|
127
|
-
|
128
|
-
|
126
|
+
prefixed_identifier = "xmlns:#{identifier}"
|
127
|
+
|
128
|
+
next if namespaces.key?(prefixed_identifier)
|
129
|
+
|
130
|
+
namespaces[prefixed_identifier] = path
|
131
|
+
end
|
132
|
+
end
|
129
133
|
|
130
134
|
namespaces
|
131
135
|
end
|
@@ -160,20 +164,22 @@ module Savon
|
|
160
164
|
message_tag = serialized_message_tag[1]
|
161
165
|
@wsdl.soap_input(@operation_name.to_sym)[message_tag].each_pair do |message, type|
|
162
166
|
break if @locals[:message].nil?
|
163
|
-
message_locals = @locals[:message][
|
167
|
+
message_locals = @locals[:message][StringUtils.snakecase(message).to_sym]
|
164
168
|
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
|
169
|
+
messages += "<#{message} xsi:type=\"#{type.join(':')}\">#{message_content}</#{message}>"
|
166
170
|
end
|
167
171
|
messages
|
168
172
|
end
|
169
173
|
|
170
174
|
def message_tag
|
171
|
-
|
175
|
+
wsdl_tag_name = @wsdl.document? && @wsdl.soap_input(@operation_name.to_sym)
|
176
|
+
|
177
|
+
message_tag = wsdl_tag_name.keys.first if wsdl_tag_name.is_a?(Hash)
|
172
178
|
message_tag ||= @locals[:message_tag]
|
173
|
-
message_tag ||=
|
179
|
+
message_tag ||= wsdl_tag_name
|
174
180
|
message_tag ||= Gyoku.xml_tag(@operation_name, :key_converter => @globals[:convert_request_keys_to])
|
175
181
|
|
176
|
-
|
182
|
+
message_tag.to_sym
|
177
183
|
end
|
178
184
|
|
179
185
|
def message_attributes
|
@@ -227,5 +233,69 @@ module Savon
|
|
227
233
|
end
|
228
234
|
end
|
229
235
|
|
236
|
+
def build_xml
|
237
|
+
tag(builder, :Envelope, namespaces_with_globals) do |xml|
|
238
|
+
tag(xml, :Header, header_attributes) { xml << header.to_s } unless header.empty?
|
239
|
+
tag(xml, :Body, body_attributes) do
|
240
|
+
if @globals[:no_message_tag]
|
241
|
+
xml << message.to_s
|
242
|
+
else
|
243
|
+
xml.tag!(*namespaced_message_tag) { xml << body_message }
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
def build_multipart_message(message_xml)
|
250
|
+
multipart_message = init_multipart_message(message_xml)
|
251
|
+
add_attachments_to_multipart_message(multipart_message)
|
252
|
+
|
253
|
+
multipart_message.ready_to_send!
|
254
|
+
|
255
|
+
# the mail.body.encoded algorithm reorders the parts, default order is [ "text/plain", "text/enriched", "text/html" ]
|
256
|
+
# should redefine the sort order, because the soap request xml should be the first
|
257
|
+
multipart_message.body.set_sort_order [ "text/xml" ]
|
258
|
+
|
259
|
+
multipart_message.body.encoded(multipart_message.content_transfer_encoding)
|
260
|
+
end
|
261
|
+
|
262
|
+
def init_multipart_message(message_xml)
|
263
|
+
multipart_message = Mail.new
|
264
|
+
xml_part = Mail::Part.new do
|
265
|
+
content_type 'text/xml'
|
266
|
+
body message_xml
|
267
|
+
# in Content-Type the start parameter is recommended (RFC 2387)
|
268
|
+
content_id '<soap-request-body@soap>'
|
269
|
+
end
|
270
|
+
multipart_message.add_part xml_part
|
271
|
+
|
272
|
+
#request.headers["Content-Type"] = "multipart/related; boundary=\"#{multipart_message.body.boundary}\"; type=\"text/xml\"; start=\"#{xml_part.content_id}\""
|
273
|
+
@multipart = {
|
274
|
+
multipart_boundary: multipart_message.body.boundary,
|
275
|
+
start: xml_part.content_id,
|
276
|
+
}
|
277
|
+
|
278
|
+
multipart_message
|
279
|
+
end
|
280
|
+
|
281
|
+
def add_attachments_to_multipart_message(multipart_message)
|
282
|
+
if @locals[:attachments].is_a? Hash
|
283
|
+
# hash example: { 'att1' => '/path/to/att1', 'att2' => '/path/to/att2' }
|
284
|
+
@locals[:attachments].each do |identifier, attachment|
|
285
|
+
add_attachment_to_multipart_message(multipart_message, attachment, identifier)
|
286
|
+
end
|
287
|
+
elsif @locals[:attachments].is_a? Array
|
288
|
+
# array example: [ '/path/to/att1', '/path/to/att2' ]
|
289
|
+
# array example: [ { filename: 'att1.xml', content: '<x/>' }, { filename: 'att2.xml', content: '<y/>' } ]
|
290
|
+
@locals[:attachments].each do |attachment|
|
291
|
+
add_attachment_to_multipart_message(multipart_message, attachment, attachment.is_a?(String) ? File.basename(attachment) : attachment[:filename])
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
def add_attachment_to_multipart_message(multipart_message, attachment, identifier)
|
297
|
+
multipart_message.add_file attachment.clone
|
298
|
+
multipart_message.parts.last.content_id = multipart_message.parts.last.content_location = identifier.to_s
|
299
|
+
end
|
230
300
|
end
|
231
301
|
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
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module Savon
|
2
3
|
module Model
|
3
4
|
|
@@ -28,7 +29,7 @@ module Savon
|
|
28
29
|
# Defines a class-level SOAP operation.
|
29
30
|
def define_class_operation(operation)
|
30
31
|
class_operation_module.module_eval %{
|
31
|
-
def #{operation.to_s
|
32
|
+
def #{StringUtils.snakecase(operation.to_s)}(locals = {})
|
32
33
|
client.call #{operation.inspect}, locals
|
33
34
|
end
|
34
35
|
}
|
@@ -37,8 +38,8 @@ module Savon
|
|
37
38
|
# Defines an instance-level SOAP operation.
|
38
39
|
def define_instance_operation(operation)
|
39
40
|
instance_operation_module.module_eval %{
|
40
|
-
def #{operation.to_s
|
41
|
-
self.class.#{operation.to_s
|
41
|
+
def #{StringUtils.snakecase(operation.to_s)}(locals = {})
|
42
|
+
self.class.#{StringUtils.snakecase(operation.to_s)} locals
|
42
43
|
end
|
43
44
|
}
|
44
45
|
end
|
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,12 +79,13 @@ 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,
|
84
86
|
:strip_namespaces => true,
|
85
87
|
:delete_namespace_attributes => false,
|
86
|
-
:convert_response_tags_to => lambda { |tag|
|
88
|
+
:convert_response_tags_to => lambda { |tag| StringUtils.snakecase(tag).to_sym},
|
87
89
|
:convert_attributes_to => lambda { |k,v| [k,v] },
|
88
90
|
:multipart => false,
|
89
91
|
:adapter => nil,
|
@@ -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
|
@@ -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)
|