savon 2.12.1 → 2.13.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 +4 -4
- data/.gitignore +2 -0
- data/CHANGELOG.md +87 -73
- data/CONTRIBUTING.md +15 -19
- data/Gemfile +2 -7
- data/README.md +14 -16
- data/coverage/assets/0.12.3/DataTables-1.10.20/images/sort_asc.png +0 -0
- data/coverage/assets/0.12.3/DataTables-1.10.20/images/sort_asc_disabled.png +0 -0
- data/coverage/assets/0.12.3/DataTables-1.10.20/images/sort_both.png +0 -0
- data/coverage/assets/0.12.3/DataTables-1.10.20/images/sort_desc.png +0 -0
- data/coverage/assets/0.12.3/DataTables-1.10.20/images/sort_desc_disabled.png +0 -0
- data/coverage/assets/0.12.3/application.css +1 -0
- data/coverage/assets/0.12.3/application.js +7 -0
- data/coverage/assets/0.12.3/colorbox/border.png +0 -0
- data/coverage/assets/0.12.3/colorbox/controls.png +0 -0
- data/coverage/assets/0.12.3/colorbox/loading.gif +0 -0
- data/coverage/assets/0.12.3/colorbox/loading_background.png +0 -0
- data/coverage/assets/0.12.3/favicon_green.png +0 -0
- data/coverage/assets/0.12.3/favicon_red.png +0 -0
- data/coverage/assets/0.12.3/favicon_yellow.png +0 -0
- data/coverage/assets/0.12.3/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/coverage/assets/0.12.3/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/coverage/assets/0.12.3/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/coverage/assets/0.12.3/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/coverage/assets/0.12.3/images/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/coverage/assets/0.12.3/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/coverage/assets/0.12.3/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- data/coverage/assets/0.12.3/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/coverage/assets/0.12.3/images/ui-icons_222222_256x240.png +0 -0
- data/coverage/assets/0.12.3/images/ui-icons_2e83ff_256x240.png +0 -0
- data/coverage/assets/0.12.3/images/ui-icons_454545_256x240.png +0 -0
- data/coverage/assets/0.12.3/images/ui-icons_888888_256x240.png +0 -0
- data/coverage/assets/0.12.3/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/coverage/assets/0.12.3/loading.gif +0 -0
- data/coverage/assets/0.12.3/magnify.png +0 -0
- data/coverage/index.html +21518 -0
- data/lib/savon/block_interface.rb +1 -0
- data/lib/savon/builder.rb +95 -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 +3 -2
- 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 +1 -0
- data/lib/savon/version.rb +2 -1
- data/lib/savon.rb +1 -0
- data/savon.gemspec +9 -8
- 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 +40 -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 +2 -1
- data/spec/savon/softlayer_spec.rb +3 -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 +81 -30
- data/.travis.yml +0 -26
- 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/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,21 @@ module Savon
|
|
117
111
|
@namespaces ||= begin
|
118
112
|
namespaces = SCHEMA_TYPES.dup
|
119
113
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
namespaces["xmlns:#{namespace_identifier}"] = @globals[:namespace] || @wsdl.namespace
|
124
|
-
end
|
114
|
+
# check namespace_identifier
|
115
|
+
namespaces["xmlns#{namespace_identifier.nil? ? '' : ":#{namespace_identifier}"}"] =
|
116
|
+
@globals[:namespace] || @wsdl.namespace
|
125
117
|
|
126
|
-
|
127
|
-
|
128
|
-
|
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 namespaces.key?("xmlns:#{identifier}")
|
125
|
+
|
126
|
+
namespaces["xmlns:#{identifier}"] = path
|
127
|
+
end
|
128
|
+
end
|
129
129
|
|
130
130
|
namespaces
|
131
131
|
end
|
@@ -162,18 +162,20 @@ module Savon
|
|
162
162
|
break if @locals[:message].nil?
|
163
163
|
message_locals = @locals[:message][message.snakecase.to_sym]
|
164
164
|
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
|
165
|
+
messages += "<#{message} xsi:type=\"#{type.join(':')}\">#{message_content}</#{message}>"
|
166
166
|
end
|
167
167
|
messages
|
168
168
|
end
|
169
169
|
|
170
170
|
def message_tag
|
171
|
-
|
171
|
+
wsdl_tag_name = @wsdl.document? && @wsdl.soap_input(@operation_name.to_sym)
|
172
|
+
|
173
|
+
message_tag = wsdl_tag_name.keys.first if wsdl_tag_name.is_a?(Hash)
|
172
174
|
message_tag ||= @locals[:message_tag]
|
173
|
-
message_tag ||=
|
175
|
+
message_tag ||= wsdl_tag_name
|
174
176
|
message_tag ||= Gyoku.xml_tag(@operation_name, :key_converter => @globals[:convert_request_keys_to])
|
175
177
|
|
176
|
-
|
178
|
+
message_tag.to_sym
|
177
179
|
end
|
178
180
|
|
179
181
|
def message_attributes
|
@@ -227,5 +229,69 @@ module Savon
|
|
227
229
|
end
|
228
230
|
end
|
229
231
|
|
232
|
+
def build_xml
|
233
|
+
tag(builder, :Envelope, namespaces_with_globals) do |xml|
|
234
|
+
tag(xml, :Header, header_attributes) { xml << header.to_s } unless header.empty?
|
235
|
+
tag(xml, :Body, body_attributes) do
|
236
|
+
if @globals[:no_message_tag]
|
237
|
+
xml << message.to_s
|
238
|
+
else
|
239
|
+
xml.tag!(*namespaced_message_tag) { xml << body_message }
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
def build_multipart_message(message_xml)
|
246
|
+
multipart_message = init_multipart_message(message_xml)
|
247
|
+
add_attachments_to_multipart_message(multipart_message)
|
248
|
+
|
249
|
+
multipart_message.ready_to_send!
|
250
|
+
|
251
|
+
# the mail.body.encoded algorithm reorders the parts, default order is [ "text/plain", "text/enriched", "text/html" ]
|
252
|
+
# should redefine the sort order, because the soap request xml should be the first
|
253
|
+
multipart_message.body.set_sort_order [ "text/xml" ]
|
254
|
+
|
255
|
+
multipart_message.body.encoded(multipart_message.content_transfer_encoding)
|
256
|
+
end
|
257
|
+
|
258
|
+
def init_multipart_message(message_xml)
|
259
|
+
multipart_message = Mail.new
|
260
|
+
xml_part = Mail::Part.new do
|
261
|
+
content_type 'text/xml'
|
262
|
+
body message_xml
|
263
|
+
# in Content-Type the start parameter is recommended (RFC 2387)
|
264
|
+
content_id '<soap-request-body@soap>'
|
265
|
+
end
|
266
|
+
multipart_message.add_part xml_part
|
267
|
+
|
268
|
+
#request.headers["Content-Type"] = "multipart/related; boundary=\"#{multipart_message.body.boundary}\"; type=\"text/xml\"; start=\"#{xml_part.content_id}\""
|
269
|
+
@multipart = {
|
270
|
+
multipart_boundary: multipart_message.body.boundary,
|
271
|
+
start: xml_part.content_id,
|
272
|
+
}
|
273
|
+
|
274
|
+
multipart_message
|
275
|
+
end
|
276
|
+
|
277
|
+
def add_attachments_to_multipart_message(multipart_message)
|
278
|
+
if @locals[:attachments].is_a? Hash
|
279
|
+
# hash example: { 'att1' => '/path/to/att1', 'att2' => '/path/to/att2' }
|
280
|
+
@locals[:attachments].each do |identifier, attachment|
|
281
|
+
add_attachment_to_multipart_message(multipart_message, attachment, identifier)
|
282
|
+
end
|
283
|
+
elsif @locals[:attachments].is_a? Array
|
284
|
+
# array example: [ '/path/to/att1', '/path/to/att2' ]
|
285
|
+
# array example: [ { filename: 'att1.xml', content: '<x/>' }, { filename: 'att2.xml', content: '<y/>' } ]
|
286
|
+
@locals[:attachments].each do |attachment|
|
287
|
+
add_attachment_to_multipart_message(multipart_message, attachment, attachment.is_a?(String) ? File.basename(attachment) : attachment[:filename])
|
288
|
+
end
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
def add_attachment_to_multipart_message(multipart_message, attachment, identifier)
|
293
|
+
multipart_message.add_file attachment.clone
|
294
|
+
multipart_message.parts.last.content_id = multipart_message.parts.last.content_location = identifier.to_s
|
295
|
+
end
|
230
296
|
end
|
231
297
|
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
|
@@ -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)
|
data/lib/savon/response.rb
CHANGED
@@ -1,14 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require "nori"
|
2
3
|
require "savon/soap_fault"
|
3
4
|
require "savon/http_error"
|
4
5
|
|
5
6
|
module Savon
|
6
7
|
class Response
|
8
|
+
CRLF = /\r\n/
|
9
|
+
WSP = /[#{%Q|\x9\x20|}]/
|
7
10
|
|
8
11
|
def initialize(http, globals, locals)
|
9
12
|
@http = http
|
10
13
|
@globals = globals
|
11
14
|
@locals = locals
|
15
|
+
@attachments = []
|
16
|
+
@xml = ''
|
17
|
+
@has_parsed_body = false
|
12
18
|
|
13
19
|
build_soap_and_http_errors!
|
14
20
|
raise_soap_and_http_errors! if @globals[:raise_errors]
|
@@ -53,7 +59,12 @@ module Savon
|
|
53
59
|
end
|
54
60
|
|
55
61
|
def xml
|
56
|
-
|
62
|
+
if multipart?
|
63
|
+
parse_body unless @has_parsed_body
|
64
|
+
@xml
|
65
|
+
else
|
66
|
+
@http.body
|
67
|
+
end
|
57
68
|
end
|
58
69
|
|
59
70
|
alias_method :to_xml, :xml
|
@@ -74,8 +85,44 @@ module Savon
|
|
74
85
|
nori.find(envelope, *path)
|
75
86
|
end
|
76
87
|
|
88
|
+
def attachments
|
89
|
+
if multipart?
|
90
|
+
parse_body unless @has_parsed_body
|
91
|
+
@attachments
|
92
|
+
else
|
93
|
+
[]
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def multipart?
|
98
|
+
!(http.headers['content-type'] =~ /^multipart/im).nil?
|
99
|
+
end
|
100
|
+
|
77
101
|
private
|
78
102
|
|
103
|
+
def boundary
|
104
|
+
return unless multipart?
|
105
|
+
Mail::Field.new('content-type', http.headers['content-type']).parameters['boundary']
|
106
|
+
end
|
107
|
+
|
108
|
+
def parse_body
|
109
|
+
http.body.force_encoding Encoding::ASCII_8BIT
|
110
|
+
parts = http.body.split(/(?:\A|\r\n)--#{Regexp.escape(boundary)}(?=(?:--)?\s*$)/)
|
111
|
+
parts[1..-1].to_a.each_with_index do |part, index|
|
112
|
+
header_part, body_part = part.lstrip.split(/#{CRLF}#{CRLF}|#{CRLF}#{WSP}*#{CRLF}(?!#{WSP})/m, 2)
|
113
|
+
section = Mail::Part.new(
|
114
|
+
body: body_part
|
115
|
+
)
|
116
|
+
section.header = header_part
|
117
|
+
if index == 0
|
118
|
+
@xml = section.body.to_s
|
119
|
+
else
|
120
|
+
@attachments << section
|
121
|
+
end
|
122
|
+
end
|
123
|
+
@has_parsed_body = true
|
124
|
+
end
|
125
|
+
|
79
126
|
def build_soap_and_http_errors!
|
80
127
|
@soap_fault = SOAPFault.new(@http, nori, xml) if soap_fault?
|
81
128
|
@http_error = HTTPError.new(@http) if http_error?
|
data/lib/savon/soap_fault.rb
CHANGED
data/lib/savon/version.rb
CHANGED
data/lib/savon.rb
CHANGED
data/savon.gemspec
CHANGED
@@ -12,29 +12,30 @@ Gem::Specification.new do |s|
|
|
12
12
|
s.homepage = "http://savonrb.com"
|
13
13
|
s.summary = "Heavy metal SOAP client"
|
14
14
|
s.description = s.summary
|
15
|
-
s.required_ruby_version = '>=
|
15
|
+
s.required_ruby_version = '>= 2.5.0'
|
16
16
|
|
17
|
-
s.rubyforge_project = s.name
|
18
17
|
s.license = 'MIT'
|
19
18
|
|
20
19
|
s.add_dependency "nori", "~> 2.4"
|
21
|
-
s.add_dependency "httpi", "
|
20
|
+
s.add_dependency "httpi", ">= 2.4.5"
|
22
21
|
s.add_dependency "wasabi", "~> 3.4"
|
23
22
|
s.add_dependency "akami", "~> 1.2"
|
24
23
|
s.add_dependency "gyoku", "~> 1.2"
|
25
24
|
s.add_dependency "builder", ">= 2.1.2"
|
26
25
|
s.add_dependency "nokogiri", ">= 1.8.1"
|
26
|
+
s.add_dependency "mail", "~> 2.5"
|
27
27
|
|
28
28
|
s.add_development_dependency "rack"
|
29
|
-
s.add_development_dependency "puma", "
|
29
|
+
s.add_development_dependency "puma", ">= 4.3.8"
|
30
30
|
|
31
|
-
s.add_development_dependency "
|
32
|
-
s.add_development_dependency "
|
31
|
+
s.add_development_dependency "byebug"
|
32
|
+
s.add_development_dependency "rake", ">= 12.3.3"
|
33
|
+
s.add_development_dependency "rspec", "~> 3.9"
|
33
34
|
s.add_development_dependency "mocha", "~> 0.14"
|
34
|
-
s.add_development_dependency "json", "
|
35
|
+
s.add_development_dependency "json", ">= 2.3.0"
|
35
36
|
|
36
37
|
ignores = File.readlines(".gitignore").grep(/\S+/).map(&:chomp)
|
37
|
-
dotfiles = %w[.gitignore .
|
38
|
+
dotfiles = %w[.gitignore .yardopts]
|
38
39
|
|
39
40
|
all_files_without_ignores = Dir["**/*"].reject { |f|
|
40
41
|
File.directory?(f) || ignores.any? { |i| File.fnmatch(i, f) }
|