savon 2.17.3 → 3.0.0.rc1

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.
@@ -1,138 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "httpi"
4
- require "savon/transport/logging"
5
- require "savon/transport/response"
6
-
7
- module Savon
8
- module Transport
9
- # HTTPI-backed HTTP transport for the default HTTP path.
10
- #
11
- # Encapsulates everything HTTPI-specific:
12
- # * header assembly
13
- # * proxy/SSL/auth/timeout configuration
14
- # * request execution
15
- class HTTPI
16
- include Logging
17
-
18
- # @param globals [Savon::GlobalOptions] the client-level options
19
- def initialize(globals)
20
- @globals = globals
21
- end
22
-
23
- # Assembles and executes a SOAP request, returning a Transport::Response.
24
- # Logs the outbound request and inbound response when logging is enabled.
25
- #
26
- # @param url [String] the SOAP endpoint URL
27
- # @param soap_headers [Hash] SOAP-level headers
28
- # @param body [String] the serialized SOAP envelope
29
- # @param locals [Savon::LocalOptions] per-request options
30
- # @return [Transport::Response]
31
- def post(url, soap_headers, body, locals)
32
- http_request = to_httpi_request(url, soap_headers, body, locals)
33
-
34
- log_request(http_request.url, http_request.headers, http_request.body) if log?
35
-
36
- httpi_response = ::HTTPI.post(http_request, @globals[:adapter])
37
- response = normalize_response(httpi_response)
38
-
39
- log_response(response) if log?
40
- response
41
- end
42
-
43
- # Normalizes a native HTTPI::Response into a Transport::Response.
44
- #
45
- # @param httpi_response [HTTPI::Response]
46
- # @return [Transport::Response]
47
- def normalize_response(httpi_response)
48
- Response.from_httpi(httpi_response)
49
- end
50
-
51
- # Builds a fully-configured HTTPI::Request.
52
- #
53
- # @param url [String] the SOAP endpoint URL
54
- # @param soap_headers [Hash] SOAP-level headers
55
- # @param body [String] the serialized SOAP envelope
56
- # @param locals [Savon::LocalOptions] per-request options
57
- # @return [HTTPI::Request]
58
- def to_httpi_request(url, soap_headers, body, locals)
59
- headers = {}
60
- headers.merge!(@globals[:headers]) if @globals.include?(:headers)
61
- headers.merge!(locals[:headers]) if locals.include?(:headers)
62
-
63
- # soap_headers are lowest priority
64
- soap_headers.each do |k, v| headers[k] ||= v end
65
-
66
- http_request = ::HTTPI::Request.new
67
- http_request.url = url
68
- http_request.body = body
69
- http_request.headers = headers
70
- http_request.set_cookies(locals[:cookies]) if locals[:cookies]
71
- configure_http_request(http_request)
72
- http_request
73
- end
74
-
75
- # Returns a configured HTTPI::Request for Wasabi's WSDL resolver.
76
- # Applies global headers and all transport-level options and
77
- # leaves the rest to Wasabi.
78
- #
79
- # @return [HTTPI::Request]
80
- def wsdl_request
81
- http_request = ::HTTPI::Request.new
82
- http_request.headers = @globals[:headers].dup if @globals.include?(:headers)
83
- configure_http_request(http_request)
84
- http_request
85
- end
86
-
87
- private
88
-
89
- def configure_http_request(http_request)
90
- configure_proxy(http_request)
91
- configure_timeouts(http_request)
92
- configure_ssl(http_request)
93
- configure_auth(http_request)
94
- configure_redirect_handling(http_request)
95
- end
96
-
97
- def configure_proxy(http_request)
98
- http_request.proxy = @globals[:proxy] if @globals.include?(:proxy)
99
- end
100
-
101
- def configure_timeouts(http_request)
102
- http_request.open_timeout = @globals[:open_timeout] if @globals.include?(:open_timeout)
103
- http_request.read_timeout = @globals[:read_timeout] if @globals.include?(:read_timeout)
104
- http_request.write_timeout = @globals[:write_timeout] if @globals.include?(:write_timeout)
105
- end
106
-
107
- # Configures SSL on the HTTPI::Request from all ssl globals.
108
- # SSL option reference: https://github.com/savonrb/httpi/blob/main/lib/httpi/auth/ssl.rb
109
- def configure_ssl(http_request)
110
- ssl = http_request.auth.ssl
111
- ssl.ssl_version = @globals[:ssl_version] if @globals.include?(:ssl_version)
112
- ssl.min_version = @globals[:ssl_min_version] if @globals.include?(:ssl_min_version)
113
- ssl.max_version = @globals[:ssl_max_version] if @globals.include?(:ssl_max_version)
114
- ssl.verify_mode = @globals[:ssl_verify_mode] if @globals.include?(:ssl_verify_mode)
115
- ssl.ciphers = @globals[:ssl_ciphers] if @globals.include?(:ssl_ciphers)
116
- ssl.cert_key_file = @globals[:ssl_cert_key_file] if @globals.include?(:ssl_cert_key_file)
117
- ssl.cert_key = @globals[:ssl_cert_key] if @globals.include?(:ssl_cert_key)
118
- ssl.cert_file = @globals[:ssl_cert_file] if @globals.include?(:ssl_cert_file)
119
- ssl.cert = @globals[:ssl_cert] if @globals.include?(:ssl_cert)
120
- ssl.ca_cert_file = @globals[:ssl_ca_cert_file] if @globals.include?(:ssl_ca_cert_file)
121
- ssl.ca_cert_path = @globals[:ssl_ca_cert_path] if @globals.include?(:ssl_ca_cert_path)
122
- ssl.ca_cert = @globals[:ssl_ca_cert] if @globals.include?(:ssl_ca_cert)
123
- ssl.cert_store = @globals[:ssl_cert_store] if @globals.include?(:ssl_cert_store)
124
- ssl.cert_key_password = @globals[:ssl_cert_key_password] if @globals.include?(:ssl_cert_key_password)
125
- end
126
-
127
- def configure_auth(http_request)
128
- http_request.auth.basic(*@globals[:basic_auth]) if @globals.include?(:basic_auth)
129
- http_request.auth.digest(*@globals[:digest_auth]) if @globals.include?(:digest_auth)
130
- http_request.auth.ntlm(*@globals[:ntlm]) if @globals.include?(:ntlm)
131
- end
132
-
133
- def configure_redirect_handling(http_request)
134
- http_request.follow_redirect = @globals[:follow_redirects] if @globals.include?(:follow_redirects)
135
- end
136
- end
137
- end
138
- end
@@ -1,60 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "savon/log_message"
4
-
5
- module Savon
6
- module Transport
7
- # Shared logging behaviour for HTTP transports.
8
- #
9
- # Expects the including class to expose @globals (Savon::GlobalOptions)
10
- # so that log level, filters, and pretty-print settings can be accessed.
11
- #
12
- # log_request and log_response are intentionally private so that each
13
- # transport drives them from its own post method.
14
- module Logging
15
- private
16
-
17
- def log?
18
- @globals[:log]
19
- end
20
-
21
- def log_headers?
22
- @globals[:log_headers]
23
- end
24
-
25
- def logger
26
- @globals[:logger]
27
- end
28
-
29
- # Logs the outbound request at INFO (URL and optional headers)
30
- # and DEBUG (filtered/pretty-printed body).
31
- #
32
- # @param url [String] the SOAP endpoint URL
33
- # @param headers [Hash] request headers
34
- # @param body [String] the serialized SOAP envelope
35
- def log_request(url, headers, body)
36
- logger.info do "SOAP request: #{url}" end
37
- logger.info { headers_to_log(headers) } if log_headers?
38
- logger.debug { body_to_log(body) }
39
- end
40
-
41
- # Logs the inbound response at INFO (status line)
42
- # and DEBUG (headers and filtered/pretty-printed body).
43
- #
44
- # @param response [Transport::Response]
45
- def log_response(response)
46
- logger.info do "SOAP response (status #{response.code})" end
47
- logger.debug { headers_to_log(response.headers) } if log_headers?
48
- logger.debug { body_to_log(response.body) }
49
- end
50
-
51
- def headers_to_log(headers)
52
- headers.map { |key, value| "#{key}: #{value}" }.join("\n")
53
- end
54
-
55
- def body_to_log(body)
56
- LogMessage.new(body, @globals[:filters], @globals[:pretty_print_xml]).to_s
57
- end
58
- end
59
- end
60
- end
@@ -1,73 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "httpi"
4
- require "savon/transport/faraday"
5
-
6
- module Savon
7
- module Transport
8
- # Transport-agnostic HTTP response value object.
9
- #
10
- # Every transport produces a Transport::Response so that higher-level code
11
- # never depends on transport-specific code. Immutable once constructed.
12
- #
13
- # The shape of #cookies is transport-specific: HTTPI responses expose an
14
- # Array of HTTPI::Cookie, while Faraday responses expose a plain Hash so
15
- # Faraday users do not depend on HTTPI types.
16
- class Response
17
- # Builds a Response from an HTTPI::Response.
18
- #
19
- # @param httpi_response [HTTPI::Response]
20
- # @return [Transport::Response]
21
- def self.from_httpi(httpi_response)
22
- new(
23
- httpi_response.code,
24
- httpi_response.headers,
25
- httpi_response.body,
26
- cookies: ::HTTPI::Cookie.list_from_headers(httpi_response.headers)
27
- )
28
- end
29
-
30
- # Builds a Response from a Faraday::Response.
31
- #
32
- # @param faraday_response [Faraday::Response]
33
- # @return [Transport::Response]
34
- def self.from_faraday(faraday_response)
35
- new(
36
- faraday_response.status,
37
- faraday_response.headers.to_h,
38
- faraday_response.body,
39
- cookies: Savon::Transport::Faraday.parse_cookies(faraday_response.headers)
40
- )
41
- end
42
-
43
- # @param code [Integer] HTTP status code
44
- # @param headers [Hash] response headers
45
- # @param body [String] response body
46
- # @param cookies [Object] parsed cookies in a transport-specific shape
47
- def initialize(code, headers, body, cookies: nil)
48
- @code = code
49
- @headers = headers
50
- @body = body
51
- @cookies = cookies
52
- end
53
-
54
- # Returns the HTTP status code.
55
- attr_reader :code
56
-
57
- # Returns the response headers hash.
58
- attr_reader :headers
59
-
60
- # Returns the response body string.
61
- attr_reader :body
62
-
63
- # Returns the parsed cookies in a transport-specific shape.
64
- # See class-level docs.
65
- attr_reader :cookies
66
-
67
- # Returns true when the HTTP status code indicates an error (>= 300).
68
- def error?
69
- @code >= 300
70
- end
71
- end
72
- end
73
- end