savon 2.11.2 → 2.14.0
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 +5 -5
- data/CHANGELOG.md +112 -73
- data/README.md +25 -16
- 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 +71 -2
- data/lib/savon/qualified_message.rb +5 -4
- data/lib/savon/request.rb +18 -3
- data/lib/savon/request_logger.rb +8 -2
- data/lib/savon/response.rb +52 -5
- data/lib/savon/soap_fault.rb +2 -3
- data/lib/savon/string_utils.rb +17 -0
- data/lib/savon/version.rb +2 -1
- data/lib/savon.rb +2 -0
- metadata +62 -97
- data/.gitignore +0 -14
- data/.travis.yml +0 -19
- 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/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/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/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 -66
- 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 -1075
- data/spec/savon/qualified_message_spec.rb +0 -68
- data/spec/savon/request_logger_spec.rb +0 -37
- data/spec/savon/request_spec.rb +0 -496
- data/spec/savon/response_spec.rb +0 -270
- data/spec/savon/soap_fault_spec.rb +0 -136
- 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/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,
|
@@ -137,7 +139,7 @@ module Savon
|
|
137
139
|
|
138
140
|
# Proxy server to use for all requests.
|
139
141
|
def proxy(proxy)
|
140
|
-
@options[:proxy] = proxy
|
142
|
+
@options[:proxy] = proxy unless proxy.nil?
|
141
143
|
end
|
142
144
|
|
143
145
|
# A Hash of HTTP headers.
|
@@ -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
|
@@ -268,6 +290,19 @@ module Savon
|
|
268
290
|
@options[:ssl_ca_cert] = cert
|
269
291
|
end
|
270
292
|
|
293
|
+
def ssl_ciphers(ciphers)
|
294
|
+
@options[:ssl_ciphers] = ciphers
|
295
|
+
end
|
296
|
+
|
297
|
+
# Sets the ca cert path.
|
298
|
+
def ssl_ca_cert_path(path)
|
299
|
+
@options[:ssl_ca_cert_path] = path
|
300
|
+
end
|
301
|
+
|
302
|
+
# Sets the ssl cert store.
|
303
|
+
def ssl_cert_store(store)
|
304
|
+
@options[:ssl_cert_store] = store
|
305
|
+
end
|
271
306
|
|
272
307
|
# HTTP basic auth credentials.
|
273
308
|
def basic_auth(*credentials)
|
@@ -383,6 +418,40 @@ module Savon
|
|
383
418
|
@options[:attributes] = attributes
|
384
419
|
end
|
385
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
|
+
|
386
455
|
# Value of the SOAPAction HTTP header.
|
387
456
|
def soap_action(soap_action)
|
388
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
|
@@ -9,7 +10,7 @@ module Savon
|
|
9
10
|
end
|
10
11
|
|
11
12
|
def to_hash(hash, path)
|
12
|
-
return unless hash
|
13
|
+
return hash unless hash
|
13
14
|
return hash.map { |value| to_hash(value, path) } if hash.is_a?(Array)
|
14
15
|
return hash.to_s unless hash.is_a?(Hash)
|
15
16
|
|
@@ -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,18 +22,25 @@ 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
|
34
|
+
@http_request.auth.ssl.ciphers = @globals[:ssl_ciphers] if @globals.include? :ssl_ciphers
|
29
35
|
|
30
36
|
@http_request.auth.ssl.cert_key_file = @globals[:ssl_cert_key_file] if @globals.include? :ssl_cert_key_file
|
31
|
-
@http_request.auth.ssl.cert_key = @globals[:ssl_cert_key]
|
37
|
+
@http_request.auth.ssl.cert_key = @globals[:ssl_cert_key] if @globals.include? :ssl_cert_key
|
32
38
|
@http_request.auth.ssl.cert_file = @globals[:ssl_cert_file] if @globals.include? :ssl_cert_file
|
33
|
-
@http_request.auth.ssl.cert = @globals[:ssl_cert]
|
39
|
+
@http_request.auth.ssl.cert = @globals[:ssl_cert] if @globals.include? :ssl_cert
|
34
40
|
@http_request.auth.ssl.ca_cert_file = @globals[:ssl_ca_cert_file] if @globals.include? :ssl_ca_cert_file
|
35
|
-
@http_request.auth.ssl.
|
41
|
+
@http_request.auth.ssl.ca_cert_path = @globals[:ssl_ca_cert_path] if @globals.include? :ssl_ca_cert_path
|
42
|
+
@http_request.auth.ssl.ca_cert = @globals[:ssl_ca_cert] if @globals.include? :ssl_ca_cert
|
43
|
+
@http_request.auth.ssl.cert_store = @globals[:ssl_cert_store] if @globals.include? :ssl_cert_store
|
36
44
|
|
37
45
|
@http_request.auth.ssl.cert_key_password = @globals[:ssl_cert_key_password] if @globals.include? :ssl_cert_key_password
|
38
46
|
end
|
@@ -55,12 +63,19 @@ module Savon
|
|
55
63
|
def build
|
56
64
|
configure_proxy
|
57
65
|
configure_timeouts
|
66
|
+
configure_headers
|
58
67
|
configure_ssl
|
59
68
|
configure_auth
|
60
69
|
configure_redirect_handling
|
61
70
|
|
62
71
|
@http_request
|
63
72
|
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def configure_headers
|
77
|
+
@http_request.headers = @globals[:headers] if @globals.include? :headers
|
78
|
+
end
|
64
79
|
end
|
65
80
|
|
66
81
|
class SOAPRequest < HTTPRequest
|
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)
|