From 725855d6f384bae65bd5e3ce36cc5d1456e9212d Mon Sep 17 00:00:00 2001
From: Lucas Ridge <74679969+LukeIGS@users.noreply.github.com>
Date: Wed, 10 Jul 2024 10:48:28 -0400
Subject: [PATCH] Faraday (#998)
* replace httpi with faraday, pull in rubyntlm since it'll be needed for ntlm auth handshakes
---
CHANGELOG.md | 12 +
lib/savon.rb | 8 +
lib/savon/http_error.rb | 6 +-
lib/savon/mock/expectation.rb | 8 +-
lib/savon/operation.rb | 70 +++--
lib/savon/options.rb | 30 ++-
lib/savon/request.rb | 128 +++++----
lib/savon/request_logger.rb | 13 +-
lib/savon/soap_fault.rb | 2 +-
savon.gemspec | 10 +-
spec/integration/zipcode_example_spec.rb | 30 +--
spec/savon/client_spec.rb | 7 +-
spec/savon/http_error_spec.rb | 6 +-
spec/savon/mock_spec.rb | 2 +-
spec/savon/observers_spec.rb | 6 +-
spec/savon/operation_spec.rb | 35 ++-
spec/savon/options_spec.rb | 175 ++++++------
spec/savon/request_spec.rb | 324 +++++------------------
spec/savon/response_spec.rb | 9 +-
spec/savon/soap_fault_spec.rb | 14 +-
spec/spec_helper.rb | 1 -
spec/support/adapters.rb | 49 ----
spec/support/responses.rb | 8 +
23 files changed, 398 insertions(+), 555 deletions(-)
delete mode 100644 spec/support/adapters.rb
create mode 100644 spec/support/responses.rb
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c2c0b9d7..bd4021fa 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,18 @@
# Savon changelog
## Unreleased
+* Changes to utilize faraday instead of http
+* BC BREAKING Cookies are handled differently now
+* BC BREAKING Multiple pieces of functionality will rely on faraday libraries to be provided by the consuming codebase
+* BC BREAKING Adapter overrides now utilize the faraday model
+* BC BREAKING Multiple hard deprecations due to a lack of feature parity between Faraday and HTTPI
+ * Deprecates digest auth
+ * Deprecates ssl_cert_key_file auth, upgrade path is to read the key
+ in and provide it
+ * Deprecates encrypted ssl keys, upgrade path is to
+ decrypt the key and pass it to faraday in code
+ * Deprecates providing a ca cert, upgrade path is to provide a ca cert file
+ * deprecates overriding ssl ciphers, as faraday does not support this
* Add your PR changelog line here
## 2.15.1 (2024-07-08)
diff --git a/lib/savon.rb b/lib/savon.rb
index b8ece228..00508a03 100644
--- a/lib/savon.rb
+++ b/lib/savon.rb
@@ -7,6 +7,14 @@ module Savon
UnknownOperationError = Class.new(Error)
InvalidResponseError = Class.new(Error)
+ class DeprecatedOptionError < Error
+ attr_accessor :option
+ def initialize(option)
+ @option = option
+ super("#{option} is deprecated as it is not supported in Faraday")
+ end
+ end
+
def self.client(globals = {}, &block)
Client.new(globals, &block)
end
diff --git a/lib/savon/http_error.rb b/lib/savon/http_error.rb
index 484d4bd0..b742c061 100644
--- a/lib/savon/http_error.rb
+++ b/lib/savon/http_error.rb
@@ -4,7 +4,7 @@ module Savon
class HTTPError < Error
def self.present?(http)
- http.error?
+ !http.success?
end
def initialize(http)
@@ -14,13 +14,13 @@ def initialize(http)
attr_reader :http
def to_s
- String.new("HTTP error (#{@http.code})").tap do |str_error|
+ String.new("HTTP error (#{@http.status})").tap do |str_error|
str_error << ": #{@http.body}" unless @http.body.empty?
end
end
def to_hash
- { :code => @http.code, :headers => @http.headers, :body => @http.body }
+ { :code => @http.status, :headers => @http.headers, :body => @http.body }
end
end
diff --git a/lib/savon/mock/expectation.rb b/lib/savon/mock/expectation.rb
index f9623161..b821a09a 100644
--- a/lib/savon/mock/expectation.rb
+++ b/lib/savon/mock/expectation.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require "httpi"
+require "faraday"
module Savon
class MockExpectation
@@ -41,8 +41,8 @@ def response!
unless @response
raise ExpectationError, "This expectation was not set up with a response."
end
-
- HTTPI::Response.new(@response[:code], @response[:headers], @response[:body])
+ env = Faraday::Env.from(status: @response[:code], response_headers: @response[:headers], response_body: @response[:body])
+ Faraday::Response.new(env)
end
private
@@ -75,7 +75,7 @@ def equals_except_any(msg_expected, msg_real)
next if (expected_value == :any && msg_real.include?(key))
return false if expected_value != msg_real[key]
end
- return true
+ true
end
end
end
diff --git a/lib/savon/operation.rb b/lib/savon/operation.rb
index 00e40d87..9a6d3c7f 100644
--- a/lib/savon/operation.rb
+++ b/lib/savon/operation.rb
@@ -7,6 +7,8 @@
require "savon/request_logger"
require "savon/http_error"
require "mail"
+require 'faraday/gzip'
+
module Savon
class Operation
@@ -58,16 +60,20 @@ def call(locals = {}, &block)
builder = build(locals, &block)
response = Savon.notify_observers(@name, builder, @globals, @locals)
- response ||= call_with_logging build_request(builder)
+ response ||= call_with_logging build_connection(builder)
- raise_expected_httpi_response! unless response.kind_of?(HTTPI::Response)
+ raise_expected_faraday_response! unless response.kind_of?(Faraday::Response)
create_response(response)
end
def request(locals = {}, &block)
builder = build(locals, &block)
- build_request(builder)
+ connection = build_connection(builder)
+ connection.build_request(:post) do |req|
+ req.url(@globals[:endpoint])
+ req.body = @locals[:body]
+ end
end
private
@@ -83,37 +89,47 @@ def set_locals(locals, block)
@locals = locals
end
- def call_with_logging(request)
- @logger.log(request) { HTTPI.post(request, @globals[:adapter]) }
+ def call_with_logging(connection)
+ ntlm_auth = handle_ntlm(connection) if @globals.include?(:ntlm)
+ @logger.log_response(connection.post(@globals[:endpoint]) { |request|
+ request.body = @locals[:body]
+ request.headers['Authorization'] = "NTLM #{auth.encode64}" if ntlm_auth
+ @logger.log_request(request)
+ })
end
- def build_request(builder)
- @locals[:soap_action] ||= soap_action
- @globals[:endpoint] ||= endpoint
+ def handle_ntlm(connection)
+ ntlm_message = Net::NTLM::Message
+ response = connection.get(@globals[:endpoint]) do |request|
+ request.headers['Authorization'] = 'NTLM ' + ntlm_message::Type1.new.encode64
+ end
+ challenge = response.headers['www-authenticate'][/(?:NTLM|Negotiate) (.*)$/, 1]
+ message = ntlm_message::Type2.decode64(challenge)
+ message.response([:user, :password, :domain].zip(@globals[:ntlm]).to_h)
+ end
- request = SOAPRequest.new(@globals).build(
+ def build_connection(builder)
+ @globals[:endpoint] ||= endpoint
+ @locals[:soap_action] ||= soap_action
+ @locals[:body] = builder.to_s
+ @connection = SOAPRequest.new(@globals).build(
:soap_action => soap_action,
:cookies => @locals[:cookies],
:headers => @locals[:headers]
- )
-
- request.url = endpoint
- request.body = builder.to_s
-
- if builder.multipart
- request.gzip
- request.headers["Content-Type"] = ["multipart/related",
- "type=\"#{SOAP_REQUEST_TYPE[@globals[:soap_version]]}\"",
- "start=\"#{builder.multipart[:start]}\"",
- "boundary=\"#{builder.multipart[:multipart_boundary]}\""].join("; ")
- request.headers["MIME-Version"] = "1.0"
+ ) do |connection|
+ if builder.multipart
+ connection.request :gzip
+ connection.headers["Content-Type"] = %W[multipart/related
+ type="#{SOAP_REQUEST_TYPE[@globals[:soap_version]]}",
+ start="#{builder.multipart[:start]}",
+ boundary="#{builder.multipart[:multipart_boundary]}"].join("; ")
+ connection.headers["MIME-Version"] = "1.0"
+ end
+
+ connection.headers["Content-Length"] = @locals[:body].bytesize.to_s
end
- # TODO: could HTTPI do this automatically in case the header
- # was not specified manually? [dh, 2013-01-04]
- request.headers["Content-Length"] = request.body.bytesize.to_s
- request
end
def soap_action
@@ -138,8 +154,8 @@ def endpoint
end
end
- def raise_expected_httpi_response!
- raise Error, "Observers need to return an HTTPI::Response to mock " \
+ def raise_expected_faraday_response!
+ raise Error, "Observers need to return an Faraday::Response to mock " \
"the request or nil to execute the request."
end
diff --git a/lib/savon/options.rb b/lib/savon/options.rb
index 71d78666..974c12e6 100644
--- a/lib/savon/options.rb
+++ b/lib/savon/options.rb
@@ -1,6 +1,5 @@
# frozen_string_literal: true
require "logger"
-require "httpi"
module Savon
class Options
@@ -10,6 +9,10 @@ def initialize(options = {})
assign options
end
+ def deprecate(option)
+ raise DeprecatedOptionError.new(option)
+ end
+
attr_reader :option_type
def [](option)
@@ -127,7 +130,7 @@ def namespace(namespace)
@options[:namespace] = namespace
end
- # The namespace identifer.
+ # The namespace identifier.
def namespace_identifier(identifier)
@options[:namespace_identifier] = identifier
end
@@ -198,13 +201,11 @@ def raise_errors(raise_errors)
# Whether or not to log.
def log(log)
- HTTPI.log = log
@options[:log] = log
end
# The logger to use. Defaults to a Savon::Logger instance.
def logger(logger)
- HTTPI.logger = logger
@options[:logger] = logger
end
@@ -257,6 +258,7 @@ def ssl_verify_mode(verify_mode)
# Sets the cert key file to use.
def ssl_cert_key_file(file)
+ deprecate('ssl_cert_key_file')
@options[:ssl_cert_key_file] = file
end
@@ -267,11 +269,13 @@ def ssl_cert_key(key)
# Sets the cert key password to use.
def ssl_cert_key_password(password)
+ deprecate('ssl_cert_key_password')
@options[:ssl_cert_key_password] = password
end
# Sets the cert file to use.
def ssl_cert_file(file)
+ deprecate('ssl_cert_file')
@options[:ssl_cert_file] = file
end
@@ -287,10 +291,12 @@ def ssl_ca_cert_file(file)
# Sets the ca cert to use.
def ssl_ca_cert(cert)
+ deprecate('ssl_ca_cert')
@options[:ssl_ca_cert] = cert
end
def ssl_ciphers(ciphers)
+ deprecate('ssl_ciphers')
@options[:ssl_ciphers] = ciphers
end
@@ -311,6 +317,7 @@ def basic_auth(*credentials)
# HTTP digest auth credentials.
def digest_auth(*credentials)
+ deprecate('digest_auth')
@options[:digest_auth] = credentials.flatten
end
@@ -389,7 +396,8 @@ def initialize(options = {})
defaults = {
:advanced_typecasting => true,
:response_parser => :nokogiri,
- :multipart => false
+ :multipart => false,
+ :body => false
}
super defaults.merge(options)
@@ -397,7 +405,7 @@ def initialize(options = {})
# The local SOAP header. Expected to be a Hash or respond to #to_s.
# Will be merged with the global SOAP header if both are Hashes.
- # Otherwise the local option will be prefered.
+ # Otherwise the local option will be preferred.
def soap_header(header)
@options[:soap_header] = header
end
@@ -457,7 +465,11 @@ def soap_action(soap_action)
@options[:soap_action] = soap_action
end
- # Cookies to be used for the next request.
+ # Cookies to be used for the next request
+ # @param [Hash] cookies cookies associated to nil will be appended as array cookies, if you need a cookie equal to
+ # and empty string, set it to ""
+ # @example cookies({accept: 'application/json', some-cookie: 'foo', "empty-cookie": "", HttpOnly: nil})
+ # # => "accept=application/json; some-cookie=foo; empty-cookie=; HttpOnly"
def cookies(cookies)
@options[:cookies] = cookies
end
@@ -485,5 +497,9 @@ def multipart(multipart)
def headers(headers)
@options[:headers] = headers
end
+
+ def body(body)
+ @options[:body] = body
+ end
end
end
diff --git a/lib/savon/request.rb b/lib/savon/request.rb
index 48c916a0..5197019e 100644
--- a/lib/savon/request.rb
+++ b/lib/savon/request.rb
@@ -1,61 +1,87 @@
# frozen_string_literal: true
-require "httpi"
+require "faraday"
module Savon
class HTTPRequest
- def initialize(globals, http_request = nil)
+ def initialize(globals, connection = nil)
@globals = globals
- @http_request = http_request || HTTPI::Request.new
- end
-
- def build
- @http_request
+ @connection = connection || Faraday::Connection.new
end
private
def configure_proxy
- @http_request.proxy = @globals[:proxy] if @globals.include? :proxy
+ connection.proxy = @globals[:proxy] if @globals.include? :proxy
end
def configure_timeouts
- @http_request.open_timeout = @globals[:open_timeout] if @globals.include? :open_timeout
- @http_request.read_timeout = @globals[:read_timeout] if @globals.include? :read_timeout
- @http_request.write_timeout = @globals[:write_timeout] if @globals.include? :write_timeout
+ connection.options.open_timeout = @globals[:open_timeout] if @globals.include? :open_timeout
+ connection.options.read_timeout = @globals[:read_timeout] if @globals.include? :read_timeout
+ connection.options.write_timeout = @globals[:write_timeout] if @globals.include? :write_timeout
end
def configure_ssl
- @http_request.auth.ssl.ssl_version = @globals[:ssl_version] if @globals.include? :ssl_version
- @http_request.auth.ssl.min_version = @globals[:ssl_min_version] if @globals.include? :ssl_min_version
- @http_request.auth.ssl.max_version = @globals[:ssl_max_version] if @globals.include? :ssl_max_version
-
- @http_request.auth.ssl.verify_mode = @globals[:ssl_verify_mode] if @globals.include? :ssl_verify_mode
- @http_request.auth.ssl.ciphers = @globals[:ssl_ciphers] if @globals.include? :ssl_ciphers
-
- @http_request.auth.ssl.cert_key_file = @globals[:ssl_cert_key_file] if @globals.include? :ssl_cert_key_file
- @http_request.auth.ssl.cert_key = @globals[:ssl_cert_key] if @globals.include? :ssl_cert_key
- @http_request.auth.ssl.cert_file = @globals[:ssl_cert_file] if @globals.include? :ssl_cert_file
- @http_request.auth.ssl.cert = @globals[:ssl_cert] if @globals.include? :ssl_cert
- @http_request.auth.ssl.ca_cert_file = @globals[:ssl_ca_cert_file] if @globals.include? :ssl_ca_cert_file
- @http_request.auth.ssl.ca_cert_path = @globals[:ssl_ca_cert_path] if @globals.include? :ssl_ca_cert_path
- @http_request.auth.ssl.ca_cert = @globals[:ssl_ca_cert] if @globals.include? :ssl_ca_cert
- @http_request.auth.ssl.cert_store = @globals[:ssl_cert_store] if @globals.include? :ssl_cert_store
-
- @http_request.auth.ssl.cert_key_password = @globals[:ssl_cert_key_password] if @globals.include? :ssl_cert_key_password
+ connection.ssl.verify = @globals[:ssl_verify] if @globals.include? :ssl_verify
+ connection.ssl.ca_file = @globals[:ssl_ca_cert_file] if @globals.include? :ssl_ca_cert_file
+ connection.ssl.verify_hostname = @globals[:verify_hostname] if @globals.include? :verify_hostname
+ connection.ssl.ca_path = @globals[:ssl_ca_cert_path] if @globals.include? :ssl_ca_cert_path
+ connection.ssl.verify_mode = @globals[:ssl_verify_mode] if @globals.include? :ssl_verify_mode
+ connection.ssl.cert_store = @globals[:ssl_cert_store] if @globals.include? :ssl_cert_store
+ connection.ssl.client_cert = @globals[:ssl_cert] if @globals.include? :ssl_cert
+ connection.ssl.client_key = @globals[:ssl_cert_key] if @globals.include? :ssl_cert_key
+ connection.ssl.certificate = @globals[:ssl_certificate] if @globals.include? :ssl_certificate
+ connection.ssl.private_key = @globals[:ssl_private_key] if @globals.include? :ssl_private_key
+ connection.ssl.verify_depth = @globals[:verify_depth] if @globals.include? :verify_depth
+ connection.ssl.version = @globals[:ssl_version] if @globals.include? :ssl_version
+ connection.ssl.min_version = @globals[:ssl_min_version] if @globals.include? :ssl_min_version
+ connection.ssl.max_version = @globals[:ssl_max_version] if @globals.include? :ssl_max_version
+
+ # No Faraday Equivalent out of box, see: https://lostisland.github.io/faraday/#/customization/ssl-options
+ # connection.ssl.cert_file = @globals[:ssl_cert_file] if @globals.include? :ssl_cert_file
+ # connection.ssl.cert_key_file = @globals[:ssl_cert_key_file] if @globals.include? :ssl_cert_key_file
+ # connection.ssl.ca_cert = @globals[:ssl_ca_cert] if @globals.include? :ssl_ca_cert
+ # connection.ssl.ciphers = @globals[:ssl_ciphers] if @globals.include? :ssl_ciphers
+ # connection.ssl.cert_key_password = @globals[:ssl_cert_key_password] if @globals.include? :ssl_cert_key_password
+
end
def configure_auth
- @http_request.auth.basic(*@globals[:basic_auth]) if @globals.include? :basic_auth
- @http_request.auth.digest(*@globals[:digest_auth]) if @globals.include? :digest_auth
- @http_request.auth.ntlm(*@globals[:ntlm]) if @globals.include? :ntlm
+ basic_auth if @globals.include?(:basic_auth)
+ ntlm_auth if @globals.include?(:ntlm)
+ end
+
+ def basic_auth
+ connection.request(:authorization, :basic, *@globals[:basic_auth])
+ end
+
+ def ntlm_auth
+ begin
+ require 'rubyntlm'
+ require 'faraday/net_http_persistent'
+ connection.adapter :net_http_persistent, pool_size: 5
+ rescue LoadError
+ raise LoadError, 'Using NTLM Auth requires both `rubyntlm` and `faraday-net_http_persistent` to be installed.'
+ end
end
def configure_redirect_handling
- if @globals.include? :follow_redirects
- @http_request.follow_redirect = @globals[:follow_redirects]
+ if @globals[:follow_redirects]
+ require 'faraday/follow_redirects'
+ connection.response :follow_redirects
end
end
+
+ def configure_adapter
+ connection.adapter(*@globals[:adapter]) unless @globals[:adapter].nil?
+ end
+
+ def configure_logging
+ connection.response(:logger, nil, headers: @globals[:log_headers], level: @globals[:logger].level) if @globals[:log]
+ end
+
+ protected
+ attr_reader :connection
end
class WSDLRequest < HTTPRequest
@@ -63,18 +89,18 @@ class WSDLRequest < HTTPRequest
def build
configure_proxy
configure_timeouts
- configure_headers
configure_ssl
configure_auth
- configure_redirect_handling
-
- @http_request
+ configure_adapter
+ configure_logging
+ configure_headers
+ connection
end
private
def configure_headers
- @http_request.headers = @globals[:headers] if @globals.include? :headers
+ connection.headers = @globals[:headers] if @globals.include? :headers
end
end
@@ -88,26 +114,34 @@ class SOAPRequest < HTTPRequest
def build(options = {})
configure_proxy
configure_timeouts
- configure_headers options[:soap_action], options[:headers]
- configure_cookies options[:cookies]
configure_ssl
configure_auth
+ configure_headers(options[:soap_action], options[:headers])
+ configure_cookies(options[:cookies])
+ configure_adapter
+ configure_logging
configure_redirect_handling
-
- @http_request
+ yield(connection) if block_given?
+ connection
end
private
def configure_cookies(cookies)
- @http_request.set_cookies(cookies) if cookies
+ connection.headers['Cookie'] = cookies.map do |key, value|
+ if value.nil?
+ key
+ else
+ "#{key}=#{value}"
+ end
+ end.join('; ') if cookies
end
def configure_headers(soap_action, headers)
- @http_request.headers = @globals[:headers] if @globals.include? :headers
- @http_request.headers.merge!(headers) if headers
- @http_request.headers["SOAPAction"] ||= %{"#{soap_action}"} if soap_action
- @http_request.headers["Content-Type"] ||= CONTENT_TYPE[@globals[:soap_version]] % @globals[:encoding]
+ connection.headers = @globals[:headers] if @globals.include? :headers
+ connection.headers.merge!(headers) if headers
+ connection.headers["SOAPAction"] ||= %{"#{soap_action}"} if soap_action
+ connection.headers["Content-Type"] ||= CONTENT_TYPE[@globals[:soap_version]] % @globals[:encoding]
end
end
end
diff --git a/lib/savon/request_logger.rb b/lib/savon/request_logger.rb
index 079449ff..e143d871 100644
--- a/lib/savon/request_logger.rb
+++ b/lib/savon/request_logger.rb
@@ -27,21 +27,24 @@ def log?
def log_headers?
@globals[:log_headers]
end
-
- private
-
def log_request(request)
- logger.info { "SOAP request: #{request.url}" }
+ return unless log?
+ logger.info { "SOAP request: #{request.path}" }
logger.info { headers_to_log(request.headers) } if log_headers?
logger.debug { body_to_log(request.body) }
end
def log_response(response)
- logger.info { "SOAP response (status #{response.code})" }
+ return response unless log?
+ logger.info { "SOAP response (status #{response.status})" }
logger.debug { headers_to_log(response.headers) } if log_headers?
logger.debug { body_to_log(response.body) }
+ response
end
+ private
+
+
def headers_to_log(headers)
headers.map { |key, value| "#{key}: #{value}" }.join("\n")
end
diff --git a/lib/savon/soap_fault.rb b/lib/savon/soap_fault.rb
index 69beafce..40e61dfc 100644
--- a/lib/savon/soap_fault.rb
+++ b/lib/savon/soap_fault.rb
@@ -5,7 +5,7 @@ class SOAPFault < Error
def self.present?(http, xml = nil)
xml ||= http.body
fault_node = xml.include?("Fault>")
- soap1_fault = xml.match(/faultcode\/?\>/) && xml.match(/faultstring\/?\>/)
+ soap1_fault = xml.match(/faultcode\/?>/) && xml.match(/faultstring\/?>/)
soap2_fault = xml.include?("Code>") && xml.include?("Reason>")
fault_node && (soap1_fault || soap2_fault)
diff --git a/savon.gemspec b/savon.gemspec
index 0b9c9217..0142bf15 100644
--- a/savon.gemspec
+++ b/savon.gemspec
@@ -17,16 +17,22 @@ Gem::Specification.new do |s|
s.license = 'MIT'
s.add_dependency "nori", "~> 2.4"
- s.add_dependency "httpi", ">= 4", " < 5"
- s.add_dependency "wasabi", ">= 3.7", " < 6"
+ s.add_dependency "faraday", "~> 2.8"
+ s.add_dependency "faraday-gzip", "~> 2.0"
+ s.add_dependency "faraday-follow_redirects", "~> 0.3"
+ s.add_dependency "wasabi", " > 5"
s.add_dependency "akami", "~> 1.2"
s.add_dependency "gyoku", "~> 1.2"
s.add_dependency "builder", ">= 2.1.2"
s.add_dependency "nokogiri", ">= 1.8.1"
s.add_dependency "mail", "~> 2.5"
+ s.add_development_dependency "faraday-net_http_persistent", "~> 2.1"
+ s.add_development_dependency "rubyntlm", ">= 0.6"
s.add_development_dependency "rack", " < 4"
s.add_development_dependency "puma", ">= 4.3.8", "< 7"
+ s.add_development_dependency "httpclient"
+ s.add_development_dependency "mutex_m"
s.add_development_dependency "byebug"
s.add_development_dependency "rake", ">= 12.3.3"
diff --git a/spec/integration/zipcode_example_spec.rb b/spec/integration/zipcode_example_spec.rb
index cedeca04..b434fa45 100644
--- a/spec/integration/zipcode_example_spec.rb
+++ b/spec/integration/zipcode_example_spec.rb
@@ -2,26 +2,26 @@
require "spec_helper"
RSpec.describe "ZIP code example" do
- it "supports threads making requests simultaneously" do
- client = Savon.client(
- :wsdl => "http://www.thomas-bayer.com/axis2/services/BLZService?wsdl",
+ let(:expected) { ["seventy million seventy thousand ten ", "twenty four million fifty thousand one hundred and ten ", "twenty million fifty thousand five hundred and fifty "] }
+ let(:request_data) { [70070010, 24050110, 20050550] }
+ let(:client) {
+ Savon.client(
+ wsdl: "https://www.dataaccess.com/webservicesserver/NumberConversion.wso?wsdl",
+ ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE,
# Lower timeouts so these specs don't take forever when the service is not available.
- :open_timeout => 10,
- :read_timeout => 10,
-
- :log => false # Disable logging for cleaner spec output.
+ open_timeout: 10,
+ read_timeout: 10,
+ log: false # Disable logging for cleaner spec output.
)
-
- mutex = Mutex.new
-
- request_data = [70070010, 24050110, 20050550]
+ }
+ let(:mutex) { Mutex.new }
+ it "supports threads making requests simultaneously" do
threads_waiting = request_data.size
-
threads = request_data.map do |blz|
thread = Thread.new do
- response = call_and_fail_gracefully(client, :get_bank, :message => { :blz => blz })
- Thread.current[:value] = response.body[:get_bank_response][:details]
+ response = call_and_fail_gracefully(client, :number_to_words, :message => { :ubi_num => blz })
+ Thread.current[:value] = response.body[:number_to_words_response][:number_to_words_result]
mutex.synchronize { threads_waiting -= 1 }
end
@@ -34,6 +34,6 @@
threads.each(&:kill)
values = threads.map { |thr| thr[:value] }.compact
- expect(values.uniq.size).to eq(values.size)
+ expect(values).to match_array(expected)
end
end
diff --git a/spec/savon/client_spec.rb b/spec/savon/client_spec.rb
index 4ba9d834..869f5b14 100644
--- a/spec/savon/client_spec.rb
+++ b/spec/savon/client_spec.rb
@@ -103,7 +103,7 @@
end
end
- describe "#call" do
+ describe "call" do
it "calls a new SOAP operation" do
locals = { :message => { :symbol => "AAPL" } }
soap_response = new_soap_response
@@ -171,7 +171,7 @@
end
end
- describe "#build_request" do
+ describe "build_request" do
it "returns the request without making an actual call" do
expected_request = mock('request')
wsdl = Wasabi::Document.new('http://example.com')
@@ -247,8 +247,7 @@
def new_http_response(options = {})
defaults = { :code => 200, :headers => {}, :body => Fixture.response(:authentication) }
response = defaults.merge options
-
- HTTPI::Response.new response[:code], response[:headers], response[:body]
+ Responses.mock_faraday(response[:code], response[:headers], response[:body])
end
def new_soap_response(options = {})
diff --git a/spec/savon/http_error_spec.rb b/spec/savon/http_error_spec.rb
index fc107b81..fdce475d 100644
--- a/spec/savon/http_error_spec.rb
+++ b/spec/savon/http_error_spec.rb
@@ -22,8 +22,8 @@
end
describe "#http" do
- it "returns the HTTPI::Response" do
- expect(http_error.http).to be_a(HTTPI::Response)
+ it "returns the Faraday::Response" do
+ expect(http_error.http).to be_a(Faraday::Response)
end
end
@@ -51,7 +51,7 @@ def new_response(options = {})
defaults = { :code => 200, :headers => {}, :body => Fixture.response(:authentication) }
response = defaults.merge options
- HTTPI::Response.new response[:code], response[:headers], response[:body]
+ Responses.mock_faraday(response[:code], response[:headers], response[:body])
end
end
diff --git a/spec/savon/mock_spec.rb b/spec/savon/mock_spec.rb
index d9becd63..e2330978 100644
--- a/spec/savon/mock_spec.rb
+++ b/spec/savon/mock_spec.rb
@@ -45,7 +45,7 @@
expect(response).to_not be_successful
expect(response).to be_a_soap_fault
- expect(response.http.code).to eq(500)
+ expect(response.http.status).to eq(500)
expect(response.http.headers).to eq("x-result" => "invalid")
expect(response.http.body).to eq(soap_fault)
end
diff --git a/spec/savon/observers_spec.rb b/spec/savon/observers_spec.rb
index 3fbb9a0d..4aeec787 100644
--- a/spec/savon/observers_spec.rb
+++ b/spec/savon/observers_spec.rb
@@ -51,7 +51,7 @@ def notify(operation_name, builder, globals, locals)
def notify(*)
# return a response to mock the request
- HTTPI::Response.new(201, { "x-result" => "valid" }, "valid!")
+ Responses.mock_faraday(201, { "x-result" => "valid" }, "valid!")
end
}.new
@@ -60,7 +60,7 @@ def notify(*)
response = new_client.call(:authenticate)
- expect(response.http.code).to eq(201)
+ expect(response.http.status).to eq(201)
expect(response.http.headers).to eq("x-result" => "valid")
expect(response.http.body).to eq("valid!")
end
@@ -77,7 +77,7 @@ def notify(*)
Savon.observers << observer
expect { new_client.call(:authenticate) }.
- to raise_error(Savon::Error, "Observers need to return an HTTPI::Response " \
+ to raise_error(Savon::Error, "Observers need to return an Faraday::Response " \
"to mock the request or nil to execute the request.")
end
end
diff --git a/spec/savon/operation_spec.rb b/spec/savon/operation_spec.rb
index e10a6d61..24ee8cbc 100644
--- a/spec/savon/operation_spec.rb
+++ b/spec/savon/operation_spec.rb
@@ -48,7 +48,7 @@ def new_operation(operation_name, wsdl, globals)
it "raises if the endpoint cannot be reached" do
message = "Error!"
- response = HTTPI::Response.new(500, {}, message)
+ response = Responses.mock_faraday(500, {}, message)
error = Wasabi::Resolver::HTTPError.new(message, response)
Wasabi::Document.any_instance.stubs(:soap_actions).raises(error)
@@ -64,7 +64,7 @@ def new_operation(operation_name, wsdl, globals)
end
end
- describe "#build" do
+ describe "build" do
it "returns the Builder" do
operation = new_operation(:verify_address, wsdl, globals)
builder = operation.build(:message => { :test => 'message' })
@@ -74,7 +74,7 @@ def new_operation(operation_name, wsdl, globals)
end
end
- describe "#call" do
+ describe "call" do
it "returns a response object" do
operation = new_operation(:verify_address, wsdl, globals)
expect(operation.call).to be_a(Savon::Response)
@@ -82,35 +82,32 @@ def new_operation(operation_name, wsdl, globals)
it "uses the global :endpoint option for the request" do
globals.endpoint("http://v1.example.com")
- HTTPI::Request.any_instance.expects(:url=).with("http://v1.example.com")
operation = new_operation(:verify_address, wsdl, globals)
-
- # stub the actual request
- http_response = HTTPI::Response.new(200, {}, "")
- operation.expects(:call_with_logging).returns(http_response)
-
+ http_response = Responses.mock_faraday(200, {}, "")
+ Faraday::Connection.any_instance.expects(:post).with(globals[:endpoint]).returns(http_response)
operation.call
end
it "falls back to use the WSDL's endpoint if the :endpoint option was not set" do
globals_without_endpoint = Savon::GlobalOptions.new(:log => false)
- HTTPI::Request.any_instance.expects(:url=).with(wsdl.endpoint)
operation = new_operation(:verify_address, wsdl, globals_without_endpoint)
# stub the actual request
- http_response = HTTPI::Response.new(200, {}, "")
- operation.expects(:call_with_logging).returns(http_response)
+ http_response = Responses.mock_faraday(200, {}, "")
+ Faraday::Connection.any_instance.expects(:post).with(wsdl.endpoint).returns(http_response)
operation.call
end
it "sets the Content-Length header" do
# XXX: probably the worst spec ever written. refactor! [dh, 2013-01-05]
- http_request = HTTPI::Request.new
- http_request.headers.expects(:[]=).with("Content-Length", "723")
- Savon::SOAPRequest.any_instance.expects(:build).returns(http_request)
+ http_response = Responses.mock_faraday(200, {}, "")
+
+ Faraday::Utils::Headers.any_instance.expects(:[]=).at_least_once
+ Faraday::Utils::Headers.any_instance.expects(:[]=).with('Content-Length', "723")
+ Faraday::Connection.any_instance.expects(:post).returns(http_response)
new_operation(:verify_address, wsdl, globals).call
end
@@ -128,10 +125,10 @@ def new_operation(operation_name, wsdl, globals)
it "uses the local :cookies option" do
globals.endpoint @server.url(:inspect_request)
- cookies = [HTTPI::Cookie.new("some-cookie=choc-chip")]
-
- HTTPI::Request.any_instance.expects(:set_cookies).with(cookies)
+ cookies = {'some-cookie': 'choc-chip'}
+ Faraday::Utils::Headers.any_instance.expects(:[]=).at_least_once
+ Faraday::Utils::Headers.any_instance.expects(:[]=).with('Cookie', 'some-cookie=choc-chip').at_least_once
operation = new_operation(:verify_address, wsdl, globals)
operation.call(:cookies => cookies)
end
@@ -191,7 +188,7 @@ def new_operation(operation_name, wsdl, globals)
end
end
- describe "#request" do
+ describe "request" do
it "returns the request" do
operation = new_operation(:verify_address, wsdl, globals)
request = operation.request
diff --git a/spec/savon/options_spec.rb b/spec/savon/options_spec.rb
index d56e6946..a6be4d18 100644
--- a/spec/savon/options_spec.rb
+++ b/spec/savon/options_spec.rb
@@ -7,6 +7,15 @@
RSpec.describe "Options" do
+ shared_examples(:deprecation) do |option|
+ it "Raises a deprecation error" do
+ expect { new_client(:endpoint => @server.url, option => :none) }.to(
+ raise_error(Savon::DeprecatedOptionError) {|e|
+ expect(e.option).to eql(option.to_s)
+ })
+ end
+ end
+
before :all do
@server = IntegrationServer.run
end
@@ -86,20 +95,22 @@
end
context 'global :follow_redirects' do
+ # From the documentation, this might have compatability issues with ntlm due to its reliance on net-http-persistent
+ # TODO integration test this somehow....
it 'sets whether or not request should follow redirects' do
client = new_client(:endpoint => @server.url, :follow_redirects => true)
- HTTPI::Request.any_instance.expects(:follow_redirect=).with(true)
+ Faraday::Connection.any_instance.expects(:response).with(:follow_redirects)
- response = client.call(:authenticate)
+ client.call(:authenticate)
end
it 'defaults to false' do
client = new_client(:endpoint => @server.url)
- HTTPI::Request.any_instance.expects(:follow_redirect=).with(false)
+ Faraday::Connection.any_instance.expects(:response).with(:follow_redirects).never
- response = client.call(:authenticate)
+ client.call(:authenticate)
end
end
@@ -109,18 +120,25 @@
client = new_client(:endpoint => @server.url, :proxy => proxy_url)
# TODO: find a way to integration test this [dh, 2012-12-08]
- HTTPI::Request.any_instance.expects(:proxy=).with(proxy_url)
+ Faraday::Connection.any_instance.expects(:proxy=).with(proxy_url)
response = client.call(:authenticate)
end
end
context "global :host" do
+ let(:host) { "https://example.com:8080" }
+ let(:path) { "#{host}/webserviceexternal/contracts.asmx"}
it "overrides the WSDL endpoint host" do
- client = new_client(:wsdl => Fixture.wsdl(:no_message_tag), host: "https://example.com:8080")
+ stubs = Faraday::Adapter::Test::Stubs.new
+ stubs.post(path) do
+ [200, {'Content-Type': 'application/xml'}, '']
+ end
+
+ client = new_client(:wsdl => Fixture.wsdl(:no_message_tag), host: host, adapter: [:test, stubs] )
- request = client.build_request(:update_orders)
- expect(request.url.to_s).to eq "https://example.com:8080/webserviceexternal/contracts.asmx"
+ client.call(:update_orders)
+ expect{stubs.verify_stubbed_calls}.not_to raise_error
end
end
@@ -136,10 +154,11 @@
end
context "global :open_timeout" do
+ let(:open_timeout) { 0.1 }
it "makes the client timeout after n seconds" do
non_routable_ip = "http://192.0.2.0"
- client = new_client(:endpoint => non_routable_ip, :open_timeout => 0.1)
-
+ client = new_client(:endpoint => non_routable_ip, :open_timeout => open_timeout)
+ start_time = Time.now
expect { client.call(:authenticate) }.to raise_error { |error|
host_unreachable = error.kind_of? Errno::EHOSTUNREACH
net_unreachable = error.kind_of? Errno::ENETUNREACH
@@ -150,7 +169,9 @@
else
# TODO: make HTTPI tag timeout errors, then depend on HTTPI::TimeoutError
# instead of a specific client error [dh, 2012-12-08]
- expect(error).to be_an(HTTPClient::ConnectTimeoutError)
+ expect(Time.now - start_time).to be_within(0.5).of(open_timeout)
+ expect(error).to be_an(Faraday::ConnectionFailed)
+
end
}
end
@@ -161,7 +182,7 @@
client = new_client(:endpoint => @server.url(:timeout), :open_timeout => 0.1, :read_timeout => 0.1)
expect { client.call(:authenticate) }.
- to raise_error(HTTPClient::ReceiveTimeoutError)
+ to raise_error(Faraday::TimeoutError)
end
end
@@ -313,7 +334,8 @@ def to_s
end
it "silences HTTPI as well" do
- HTTPI.expects(:log=).with(false)
+ Faraday::Connection.any_instance.expects(:response).with(:logger, nil, {:headers => true, :level => 0}).never
+
new_client(:log => false)
end
@@ -327,7 +349,7 @@ def to_s
end
it "turns HTTPI logging back on as well" do
- HTTPI.expects(:log=).with(true)
+ Faraday::Connection.any_instance.expects(:response).with(:logger, nil, {:headers => true, :level => 0}).at_least_once
new_client(:log => true)
end
end
@@ -347,12 +369,14 @@ def to_s
expect(logger).to eq(custom_logger)
end
- it "sets the logger of HTTPI as well" do
- custom_logger = Logger.new($stdout)
+ it "sets the logger of faraday connection as well" do
+ Faraday::Connection.any_instance.expects(:response).with(:logger, nil, {:headers => true, :level => 0}).at_least_once
+ mock_stdout {
+ custom_logger = Logger.new($stdout)
- client = new_client(:logger => custom_logger, :log => true)
-
- expect(HTTPI.logger).to be custom_logger
+ client = new_client(:endpoint => @server.url, :logger => custom_logger, :log => true)
+ client.call(:authenticate)
+ }
end
end
@@ -421,7 +445,7 @@ def to_s
context "global :ssl_version" do
it "sets the SSL version to use" do
- HTTPI::Auth::SSL.any_instance.expects(:ssl_version=).with(:TLSv1).twice
+ Faraday::SSLOptions.any_instance.expects(:version=).with(:TLSv1).twice
client = new_client(:endpoint => @server.url, :ssl_version => :TLSv1)
client.call(:authenticate)
@@ -430,7 +454,7 @@ def to_s
context "global :ssl_min_version" do
it "sets the SSL min_version to use" do
- HTTPI::Auth::SSL.any_instance.expects(:min_version=).with(:TLS1_2).twice
+ Faraday::SSLOptions.any_instance.expects(:min_version=).with(:TLS1_2).twice
client = new_client(:endpoint => @server.url, :ssl_min_version => :TLS1_2)
client.call(:authenticate)
@@ -439,7 +463,7 @@ def to_s
context "global :ssl_max_version" do
it "sets the SSL max_version to use" do
- HTTPI::Auth::SSL.any_instance.expects(:max_version=).with(:TLS1_2).twice
+ Faraday::SSLOptions.any_instance.expects(:max_version=).with(:TLS1_2).twice
client = new_client(:endpoint => @server.url, :ssl_max_version => :TLS1_2)
client.call(:authenticate)
@@ -448,7 +472,7 @@ def to_s
context "global :ssl_verify_mode" do
it "sets the verify mode to use" do
- HTTPI::Auth::SSL.any_instance.expects(:verify_mode=).with(:peer).twice
+ Faraday::SSLOptions.any_instance.expects(:verify_mode=).with(:peer).twice
client = new_client(:endpoint => @server.url, :ssl_verify_mode => :peer)
client.call(:authenticate)
@@ -456,28 +480,17 @@ def to_s
end
context "global :ssl_ciphers" do
- it "sets the ciphers to use" do
- HTTPI::Auth::SSL.any_instance.expects(:ciphers=).with(:none).twice
-
- client = new_client(:endpoint => @server.url, :ssl_ciphers => :none)
- client.call(:authenticate)
- end
+ it_behaves_like(:deprecation, :ssl_ciphers)
end
context "global :ssl_cert_key_file" do
- it "sets the cert key file to use" do
- cert_key = File.expand_path("../../fixtures/ssl/client_key.pem", __FILE__)
- HTTPI::Auth::SSL.any_instance.expects(:cert_key_file=).with(cert_key).twice
-
- client = new_client(:endpoint => @server.url, :ssl_cert_key_file => cert_key)
- client.call(:authenticate)
- end
+ it_behaves_like(:deprecation, :ssl_cert_key_file)
end
context "global :ssl_cert_key" do
it "sets the cert key to use" do
cert_key = File.open(File.expand_path("../../fixtures/ssl/client_key.pem", __FILE__)).read
- HTTPI::Auth::SSL.any_instance.expects(:cert_key=).with(cert_key).twice
+ Faraday::SSLOptions.any_instance.expects(:client_key=).with(cert_key).twice
client = new_client(:endpoint => @server.url, :ssl_cert_key => cert_key)
client.call(:authenticate)
@@ -486,32 +499,17 @@ def to_s
context "global :ssl_cert_key_password" do
- it "sets the encrypted cert key file password to use" do
- cert_key = File.expand_path("../../fixtures/ssl/client_encrypted_key.pem", __FILE__)
- cert_key_pass = "secure-password!42"
- HTTPI::Auth::SSL.any_instance.expects(:cert_key_file=).with(cert_key).twice
- HTTPI::Auth::SSL.any_instance.expects(:cert_key_password=).with(cert_key_pass).twice
-
- client = new_client(:endpoint => @server.url, :ssl_cert_key_file => cert_key, :ssl_cert_key_password => cert_key_pass)
- client.call(:authenticate)
- end
-
+ it_behaves_like(:deprecation, :ssl_cert_key_password)
end
context "global :ssl_cert_file" do
- it "sets the cert file to use" do
- cert = File.expand_path("../../fixtures/ssl/client_cert.pem", __FILE__)
- HTTPI::Auth::SSL.any_instance.expects(:cert_file=).with(cert).twice
-
- client = new_client(:endpoint => @server.url, :ssl_cert_file => cert)
- client.call(:authenticate)
- end
+ it_behaves_like(:deprecation, :ssl_cert_file)
end
context "global :ssl_cert" do
it "sets the cert to use" do
cert = File.open(File.expand_path("../../fixtures/ssl/client_cert.pem", __FILE__)).read
- HTTPI::Auth::SSL.any_instance.expects(:cert=).with(cert).twice
+ Faraday::SSLOptions.any_instance.expects(:client_cert=).with(cert).twice
client = new_client(:endpoint => @server.url, :ssl_cert => cert)
client.call(:authenticate)
@@ -521,7 +519,7 @@ def to_s
context "global :ssl_ca_cert_file" do
it "sets the ca cert file to use" do
ca_cert = File.expand_path("../../fixtures/ssl/client_cert.pem", __FILE__)
- HTTPI::Auth::SSL.any_instance.expects(:ca_cert_file=).with(ca_cert).twice
+ Faraday::SSLOptions.any_instance.expects(:ca_file=).with(ca_cert).twice
client = new_client(:endpoint => @server.url, :ssl_ca_cert_file => ca_cert)
client.call(:authenticate)
@@ -531,7 +529,7 @@ def to_s
context "global :ssl_ca_cert_path" do
it "sets the ca cert path to use" do
ca_cert_path = "../../fixtures/ssl"
- HTTPI::Auth::SSL.any_instance.expects(:ca_cert_path=).with(ca_cert_path).twice
+ Faraday::SSLOptions.any_instance.expects(:ca_path=).with(ca_cert_path).twice
client = new_client(:endpoint => @server.url, :ssl_ca_cert_path => ca_cert_path)
client.call(:authenticate)
@@ -541,7 +539,7 @@ def to_s
context "global :ssl_ca_cert_store" do
it "sets the cert store to use" do
cert_store = OpenSSL::X509::Store.new
- HTTPI::Auth::SSL.any_instance.expects(:cert_store=).with(cert_store).twice
+ Faraday::SSLOptions.any_instance.expects(:cert_store=).with(cert_store).twice
client = new_client(:endpoint => @server.url, :ssl_cert_store => cert_store)
client.call(:authenticate)
@@ -549,13 +547,7 @@ def to_s
end
context "global :ssl_ca_cert" do
- it "sets the ca cert file to use" do
- ca_cert = File.open(File.expand_path("../../fixtures/ssl/client_cert.pem", __FILE__)).read
- HTTPI::Auth::SSL.any_instance.expects(:ca_cert=).with(ca_cert).twice
-
- client = new_client(:endpoint => @server.url, :ssl_ca_cert => ca_cert)
- client.call(:authenticate)
- end
+ it_behaves_like(:deprecation, :ssl_ca_cert)
end
@@ -575,7 +567,7 @@ def to_s
# TODO: find a way to integration test this. including an entire ntlm
# server implementation seems a bit over the top though.
- HTTPI::Auth::Config.any_instance.expects(:ntlm).with(*credentials)
+ Savon::Operation.any_instance.expects(:handle_ntlm)
response = client.call(:authenticate)
end
@@ -921,34 +913,35 @@ def to_s
context 'global: :adapter' do
it 'passes option to Wasabi initializer for WSDL fetching' do
- ## I want to use there something similar to the next mock expectation, but I can't
- ## as due to how Savon sets up Wasabi::Document and Wasabi::Document initialize itself
- ## adapter= method is called first time with nil and second time with adapter. [Envek, 2014-05-03]
- # Wasabi::Document.any_instance.expects(:adapter=).with(:fake_adapter_for_test)
+ stubs = Faraday::Adapter::Test::Stubs.new
+ stubs.get(@server.url('authentication')) do
+ [200, {'Content-Type': 'application/xml'}, Fixture.wsdl('authentication')]
+ end
+ Wasabi::Document.any_instance.expects(:adapter=).with(nil)
+ Wasabi::Document.any_instance.expects(:adapter=).with([:test, stubs])
client = Savon.client(
:log => false,
:wsdl => @server.url(:authentication),
- :adapter => :fake_adapter_for_test,
+ :adapter => [:test, stubs],
)
- operations = client.operations
- expect(operations).to eq([:authenticate])
- expect(FakeAdapterForTest.class_variable_get(:@@requests).size).to eq(1)
- expect(FakeAdapterForTest.class_variable_get(:@@requests).first.url).to eq(URI.parse(@server.url(:authentication)))
- expect(FakeAdapterForTest.class_variable_get(:@@methods)).to eq([:get])
+ client.operations
+ expect{stubs.verify_stubbed_calls}.not_to raise_error
end
- it 'instructs HTTPI to use provided adapter for performing SOAP requests' do
+ it 'instructs Faraday to use a provided adapter for performing SOAP requests' do
+ stubs = Faraday::Adapter::Test::Stubs.new
+ stubs.post(@server.url('repeat')) do
+ [200, {'Content-Type': 'application/xml'}, Fixture.response('authentication')]
+ end
client = new_client_without_wsdl(
:endpoint => @server.url(:repeat),
- :namespace => "http://v1.example.com",
- :adapter => :adapter_for_test,
+ :namespace => "http://v1_0.ws.user.example.com",
+ :adapter => [:test, stubs],
)
response = client.call(:authenticate)
- expect(response.http.body).to include('xmlns:wsdl="http://v1.example.com"')
- expect(response.http.body).to include('')
- expect(AdapterForTest.class_variable_get(:@@requests).size).to eq(1)
- expect(AdapterForTest.class_variable_get(:@@requests).first.url).to eq(URI.parse(@server.url(:repeat)))
- expect(AdapterForTest.class_variable_get(:@@methods)).to eq([:post])
+ expect(response.http.body).to include('')
+ expect(response.http.body).to include('')
+ expect{stubs.verify_stubbed_calls}.not_to raise_error
end
end
@@ -1078,17 +1071,17 @@ def to_s
end
context "request :cookies" do
- it "accepts an Array of HTTPI::Cookie objects for the next request" do
- cookies = [
- HTTPI::Cookie.new("some-cookie=choc-chip"),
- HTTPI::Cookie.new("another-cookie=ny-cheesecake")
- ]
+ it "accepts a hash for the next request" do
+ cookies = {
+ 'some-cookie': 'choc-chip',
+ 'another-cookie': 'ny-cheesecake'
+ }
client = new_client(:endpoint => @server.url(:inspect_request))
response = client.call(:authenticate, :cookies => cookies)
cookie = inspect_request(response).cookie
- expect(cookie.split(";")).to include(
+ expect(cookie.split("; ")).to include(
"some-cookie=choc-chip",
"another-cookie=ny-cheesecake"
)
@@ -1142,4 +1135,6 @@ def inspect_request(response)
OpenStruct.new(hash)
end
+
+
end
diff --git a/spec/savon/request_spec.rb b/spec/savon/request_spec.rb
index fcdc03f4..12b7f310 100644
--- a/spec/savon/request_spec.rb
+++ b/spec/savon/request_spec.rb
@@ -5,17 +5,18 @@
RSpec.describe Savon::WSDLRequest do
let(:globals) { Savon::GlobalOptions.new }
- let(:http_request) { HTTPI::Request.new }
+ let(:http_connection) { Faraday::Connection.new }
let(:ciphers) { OpenSSL::Cipher.ciphers }
def new_wsdl_request
- Savon::WSDLRequest.new(globals, http_request)
+ Savon::WSDLRequest.new(globals, http_connection)
end
- describe "#build" do
- it "returns an HTTPI::Request" do
+ describe "build" do
+ it "returns an Faraday::Request" do
wsdl_request = Savon::WSDLRequest.new(globals)
- expect(wsdl_request.build).to be_an(HTTPI::Request)
+ result = wsdl_request.build
+ expect(result).to be_an(Faraday::Connection)
end
describe "headers" do
@@ -35,13 +36,13 @@ def new_wsdl_request
describe "proxy" do
it "is set when specified" do
globals.proxy("http://proxy.example.com")
- http_request.expects(:proxy=).with("http://proxy.example.com")
+ http_connection.expects(:proxy=).with("http://proxy.example.com")
new_wsdl_request.build
end
it "is not set otherwise" do
- http_request.expects(:proxy=).never
+ http_connection.expects(:proxy=).never
new_wsdl_request.build
end
end
@@ -49,13 +50,13 @@ def new_wsdl_request
describe "open timeout" do
it "is set when specified" do
globals.open_timeout(22)
- http_request.expects(:open_timeout=).with(22)
+ http_connection.options.expects(:open_timeout=).with(22)
new_wsdl_request.build
end
it "is not set otherwise" do
- http_request.expects(:open_timeout=).never
+ http_connection.options.expects(:open_timeout=).never
new_wsdl_request.build
end
end
@@ -63,13 +64,13 @@ def new_wsdl_request
describe "read timeout" do
it "is set when specified" do
globals.read_timeout(33)
- http_request.expects(:read_timeout=).with(33)
+ http_connection.options.expects(:read_timeout=).with(33)
new_wsdl_request.build
end
it "is not set otherwise" do
- http_request.expects(:read_timeout=).never
+ http_connection.options.expects(:read_timeout=).never
new_wsdl_request.build
end
end
@@ -77,13 +78,13 @@ def new_wsdl_request
describe "write timeout" do
it "is set when specified" do
globals.write_timeout(44)
- http_request.expects(:write_timeout=).with(44)
+ http_connection.options.expects(:write_timeout=).with(44)
new_wsdl_request.build
end
it "is not set otherwise" do
- http_request.expects(:read_timeout=).never
+ http_connection.expects(:read_timeout=).never
new_wsdl_request.build
end
end
@@ -91,13 +92,13 @@ def new_wsdl_request
describe "ssl version" do
it "is set when specified" do
globals.ssl_version(:TLSv1)
- http_request.auth.ssl.expects(:ssl_version=).with(:TLSv1)
+ http_connection.ssl.expects(:version=).with(:TLSv1)
new_wsdl_request.build
end
it "is not set otherwise" do
- http_request.auth.ssl.expects(:ssl_version=).never
+ http_connection.ssl.expects(:version=).never
new_wsdl_request.build
end
end
@@ -105,13 +106,13 @@ def new_wsdl_request
describe "ssl min_version" do
it "is set when specified" do
globals.ssl_min_version(:TLS1_2)
- http_request.auth.ssl.expects(:min_version=).with(:TLS1_2)
+ http_connection.ssl.expects(:min_version=).with(:TLS1_2)
new_wsdl_request.build
end
it "is not set otherwise" do
- http_request.auth.ssl.expects(:min_version=).never
+ http_connection.ssl.expects(:min_version=).never
new_wsdl_request.build
end
end
@@ -119,13 +120,13 @@ def new_wsdl_request
describe "ssl max_version" do
it "is set when specified" do
globals.ssl_max_version(:TLS1_2)
- http_request.auth.ssl.expects(:max_version=).with(:TLS1_2)
+ http_connection.ssl.expects(:max_version=).with(:TLS1_2)
new_wsdl_request.build
end
it "is not set otherwise" do
- http_request.auth.ssl.expects(:max_version=).never
+ http_connection.ssl.expects(:max_version=).never
new_wsdl_request.build
end
end
@@ -133,122 +134,13 @@ def new_wsdl_request
describe "ssl verify mode" do
it "is set when specified" do
globals.ssl_verify_mode(:peer)
- http_request.auth.ssl.expects(:verify_mode=).with(:peer)
+ http_connection.ssl.expects(:verify_mode=).with(:peer)
new_wsdl_request.build
end
it "is not set otherwise" do
- http_request.auth.ssl.expects(:verify_mode=).never
- new_wsdl_request.build
- end
- end
-
- describe "ssl ciphers" do
- it "is set when specified" do
- globals.ssl_ciphers(ciphers)
- http_request.auth.ssl.expects(:ciphers=).with(ciphers)
-
- new_wsdl_request.build
- end
-
- it "is not set otherwise" do
- http_request.auth.ssl.expects(:ciphers=).never
- new_wsdl_request.build
- end
- end
-
- describe "ssl cert key file" do
- it "is set when specified" do
- cert_key = File.expand_path("../../fixtures/ssl/client_key.pem", __FILE__)
- globals.ssl_cert_key_file(cert_key)
- http_request.auth.ssl.expects(:cert_key_file=).with(cert_key)
-
- new_wsdl_request.build
- end
-
- it "is not set otherwise" do
- http_request.auth.ssl.expects(:cert_key_file=).never
- new_wsdl_request.build
- end
- end
-
- describe "ssl cert key password" do
- it "is set when specified" do
- the_pass = "secure-password!42"
- globals.ssl_cert_key_password(the_pass)
- http_request.auth.ssl.expects(:cert_key_password=).with(the_pass)
-
- new_wsdl_request.build
- end
-
- it "is not set otherwise" do
- http_request.auth.ssl.expects(:cert_key_password=).never
- new_wsdl_request.build
- end
- end
-
- describe "ssl encrypted cert key file" do
- describe "set with an invalid decrypting password" do
- it "fails when attempting to use the SSL private key" do
- skip("JRuby: find out why this does not raise an error!") if RUBY_PLATFORM == 'java'
- pass = "wrong-password"
- key = File.expand_path("../../fixtures/ssl/client_encrypted_key.pem", __FILE__)
- cert = File.expand_path("../../fixtures/ssl/client_encrypted_key_cert.pem", __FILE__)
-
- globals.ssl_cert_file(cert)
- globals.ssl_cert_key_password(pass)
- globals.ssl_cert_key_file(key)
-
- new_wsdl_request.build
-
- expect { http_request.auth.ssl.cert_key }.to raise_error OpenSSL::PKey::PKeyError
- end
- end
-
- describe "set with a valid decrypting password" do
- it "handles SSL private keys properly" do
- pass = "secure-password!42"
- key = File.expand_path("../../fixtures/ssl/client_encrypted_key.pem", __FILE__)
- cert = File.expand_path("../../fixtures/ssl/client_encrypted_key_cert.pem", __FILE__)
-
- globals.ssl_cert_file(cert)
- globals.ssl_cert_key_password(pass)
- globals.ssl_cert_key_file(key)
-
- new_wsdl_request.build
-
- expect(http_request.auth.ssl.cert_key.to_s).to match(/BEGIN RSA PRIVATE KEY/)
- end
- end
- end
-
- describe "ssl cert file" do
- it "is set when specified" do
- cert = File.expand_path("../../fixtures/ssl/client_cert.pem", __FILE__)
- globals.ssl_cert_file(cert)
- http_request.auth.ssl.expects(:cert_file=).with(cert)
-
- new_wsdl_request.build
- end
-
- it "is not set otherwise" do
- http_request.auth.ssl.expects(:cert_file=).never
- new_wsdl_request.build
- end
- end
-
- describe "ssl ca cert file" do
- it "is set when specified" do
- ca_cert = File.expand_path("../../fixtures/ssl/client_cert.pem", __FILE__)
- globals.ssl_ca_cert_file(ca_cert)
- http_request.auth.ssl.expects(:ca_cert_file=).with(ca_cert)
-
- new_wsdl_request.build
- end
-
- it "is not set otherwise" do
- http_request.auth.ssl.expects(:ca_cert_file=).never
+ http_connection.ssl.expects(:verify_mode=).never
new_wsdl_request.build
end
end
@@ -256,41 +148,33 @@ def new_wsdl_request
describe "basic auth" do
it "is set when specified" do
globals.basic_auth("luke", "secret")
- http_request.auth.expects(:basic).with("luke", "secret")
+ http_connection.expects(:request).with(:authorization, :basic,"luke", "secret")
new_wsdl_request.build
end
it "is not set otherwise" do
- http_request.auth.expects(:basic).never
+ http_connection.expects(:request).with{|args| args.include?(:basic)}.never
new_wsdl_request.build
end
end
- describe "digest auth" do
- it "is set when specified" do
- globals.digest_auth("lea", "top-secret")
- http_request.auth.expects(:digest).with("lea", "top-secret")
-
+ describe "ntlm auth" do
+ it 'tries to load ntlm when set' do
+ globals.ntlm("han", "super-secret")
new_wsdl_request.build
+ expect(require 'rubyntlm').to be(false)
end
- it "is not set otherwise" do
- http_request.auth.expects(:digest).never
- new_wsdl_request.build
- end
- end
-
- describe "ntlm auth" do
- it "is set when specified" do
+ it "applies net-http-persistent when set" do
globals.ntlm("han", "super-secret")
- http_request.auth.expects(:ntlm).with("han", "super-secret")
+ http_connection.expects(:adapter).with{|params| params == :net_http_persistent}.at_least_once
new_wsdl_request.build
end
- it "is not set otherwise" do
- http_request.auth.expects(:ntlm).never
+ it "does not apply net-http-persistent when not set" do
+ http_connection.expects(:adapter).with(:net_http_persistent, pool_size: 5).never
new_wsdl_request.build
end
end
@@ -301,43 +185,49 @@ def new_wsdl_request
RSpec.describe Savon::SOAPRequest do
let(:globals) { Savon::GlobalOptions.new }
- let(:http_request) { HTTPI::Request.new }
+ let(:http_connection) { Faraday::Connection.new }
let(:ciphers) { OpenSSL::Cipher.ciphers }
def new_soap_request
- Savon::SOAPRequest.new(globals, http_request)
+ Savon::SOAPRequest.new(globals, http_connection)
end
- describe "#build" do
- it "returns an HTTPI::Request" do
+ describe "build" do
+ it "returns an Faraday::Request" do
soap_request = Savon::SOAPRequest.new(globals)
- expect(soap_request.build).to be_an(HTTPI::Request)
+ expect(soap_request.build).to be_an(Faraday::Connection)
end
describe "proxy" do
it "is set when specified" do
globals.proxy("http://proxy.example.com")
- http_request.expects(:proxy=).with("http://proxy.example.com")
+ http_connection.expects(:proxy=).with("http://proxy.example.com")
new_soap_request.build
end
it "is not set otherwise" do
- http_request.expects(:proxy=).never
+ http_connection.expects(:proxy=).never
new_soap_request.build
end
end
describe "cookies" do
it "sets the given cookies" do
- cookies = [HTTPI::Cookie.new("some-cookie=choc-chip; Path=/; HttpOnly")]
-
- http_request.expects(:set_cookies).with(cookies)
+ cookies = {
+ 'some-cookie': 'choc-chip',
+ path: '/',
+ HttpOnly: nil
+ }
+
+ http_connection.headers.expects(:[]=).at_least_once
+ http_connection.headers.expects(:[]=).with('Cookie', 'some-cookie=choc-chip; path=/; HttpOnly').at_least_once
new_soap_request.build(:cookies => cookies)
end
it "does not set the cookies if there are none" do
- http_request.expects(:set_cookies).never
+ http_connection.headers.expects(:[]=).at_least_once
+ http_connection.expects(:[]=).with('Cookie').never
new_soap_request.build
end
end
@@ -345,13 +235,13 @@ def new_soap_request
describe "open timeout" do
it "is set when specified" do
globals.open_timeout(22)
- http_request.expects(:open_timeout=).with(22)
+ http_connection.options.expects(:open_timeout=).with(22)
new_soap_request.build
end
it "is not set otherwise" do
- http_request.expects(:open_timeout=).never
+ http_connection.options.expects(:open_timeout=).never
new_soap_request.build
end
end
@@ -359,13 +249,13 @@ def new_soap_request
describe "read timeout" do
it "is set when specified" do
globals.read_timeout(33)
- http_request.expects(:read_timeout=).with(33)
+ http_connection.options.expects(:read_timeout=).with(33)
new_soap_request.build
end
it "is not set otherwise" do
- http_request.expects(:read_timeout=).never
+ http_connection.options.expects(:read_timeout=).never
new_soap_request.build
end
end
@@ -436,13 +326,13 @@ def new_soap_request
describe "ssl version" do
it "is set when specified" do
globals.ssl_version(:TLSv1)
- http_request.auth.ssl.expects(:ssl_version=).with(:TLSv1)
+ http_connection.ssl.expects(:version=).with(:TLSv1)
new_soap_request.build
end
it "is not set otherwise" do
- http_request.auth.ssl.expects(:ssl_version=).never
+ http_connection.ssl.expects(:version=).never
new_soap_request.build
end
end
@@ -450,87 +340,13 @@ def new_soap_request
describe "ssl verify mode" do
it "is set when specified" do
globals.ssl_verify_mode(:peer)
- http_request.auth.ssl.expects(:verify_mode=).with(:peer)
-
- new_soap_request.build
- end
-
- it "is not set otherwise" do
- http_request.auth.ssl.expects(:verify_mode=).never
- new_soap_request.build
- end
- end
-
- describe "ssl ciphers" do
- it "is set when specified" do
- globals.ssl_ciphers(ciphers)
- http_request.auth.ssl.expects(:ciphers=).with(ciphers)
-
- new_soap_request.build
- end
-
- it "is not set otherwise" do
- http_request.auth.ssl.expects(:ciphers=).never
- new_soap_request.build
- end
- end
-
- describe "ssl cert key file" do
- it "is set when specified" do
- cert_key = File.expand_path("../../fixtures/ssl/client_key.pem", __FILE__)
- globals.ssl_cert_key_file(cert_key)
- http_request.auth.ssl.expects(:cert_key_file=).with(cert_key)
-
- new_soap_request.build
- end
-
- it "is not set otherwise" do
- http_request.auth.ssl.expects(:cert_key_file=).never
- new_soap_request.build
- end
- end
-
- describe "ssl cert key password" do
- it "is set when specified" do
- the_pass = "secure-password!42"
- globals.ssl_cert_key_password(the_pass)
- http_request.auth.ssl.expects(:cert_key_password=).with(the_pass)
-
- new_soap_request.build
- end
-
- it "is not set otherwise" do
- http_request.auth.ssl.expects(:cert_key_password=).never
- new_soap_request.build
- end
- end
-
- describe "ssl cert file" do
- it "is set when specified" do
- cert = File.expand_path("../../fixtures/ssl/client_cert.pem", __FILE__)
- globals.ssl_cert_file(cert)
- http_request.auth.ssl.expects(:cert_file=).with(cert)
-
- new_soap_request.build
- end
-
- it "is not set otherwise" do
- http_request.auth.ssl.expects(:cert_file=).never
- new_soap_request.build
- end
- end
-
- describe "ssl ca cert file" do
- it "is set when specified" do
- ca_cert = File.expand_path("../../fixtures/ssl/client_cert.pem", __FILE__)
- globals.ssl_ca_cert_file(ca_cert)
- http_request.auth.ssl.expects(:ca_cert_file=).with(ca_cert)
+ http_connection.ssl.expects(:verify_mode=).with(:peer)
new_soap_request.build
end
it "is not set otherwise" do
- http_request.auth.ssl.expects(:ca_cert_file=).never
+ http_connection.ssl.expects(:verify_mode=).never
new_soap_request.build
end
end
@@ -538,41 +354,25 @@ def new_soap_request
describe "basic auth" do
it "is set when specified" do
globals.basic_auth("luke", "secret")
- http_request.auth.expects(:basic).with("luke", "secret")
-
+ http_connection.expects(:request).with(:authorization, :basic, "luke", "secret")
new_soap_request.build
end
it "is not set otherwise" do
- http_request.auth.expects(:basic).never
- new_soap_request.build
- end
- end
-
- describe "digest auth" do
- it "is set when specified" do
- globals.digest_auth("lea", "top-secret")
- http_request.auth.expects(:digest).with("lea", "top-secret")
-
- new_soap_request.build
- end
-
- it "is not set otherwise" do
- http_request.auth.expects(:digest).never
+ http_connection.expects(:request).with(:authorization, :basic, "luke", 'secret').never
new_soap_request.build
end
end
describe "ntlm auth" do
- it "is set when specified" do
+ it "uses the net-http-persistent adapter in faraday" do
globals.ntlm("han", "super-secret")
- http_request.auth.expects(:ntlm).with("han", "super-secret")
-
+ http_connection.expects(:adapter).with(:net_http_persistent, {:pool_size => 5})
new_soap_request.build
end
it "is not set otherwise" do
- http_request.auth.expects(:ntlm).never
+ http_connection.expects(:adapter).with(:net_http_persistent, {:pool_size => 5}).never
new_soap_request.build
end
end
diff --git a/spec/savon/response_spec.rb b/spec/savon/response_spec.rb
index 5cb874dd..9bab16af 100644
--- a/spec/savon/response_spec.rb
+++ b/spec/savon/response_spec.rb
@@ -244,16 +244,15 @@
end
describe "#http" do
- it "should return the HTTPI::Response" do
- expect(soap_response.http).to be_an(HTTPI::Response)
+ it "should return the Faraday::Response" do
+ expect(soap_response.http).to be_an(Faraday::Response)
end
end
def soap_response(options = {})
defaults = { :code => 200, :headers => {}, :body => Fixture.response(:authentication) }
response = defaults.merge options
- http_response = HTTPI::Response.new(response[:code], response[:headers], response[:body])
-
+ http_response = Responses.mock_faraday(response[:code], response[:headers], response[:body])
Savon::Response.new(http_response, globals, locals)
end
@@ -268,7 +267,7 @@ def http_error_response
def invalid_soap_response(options = {})
defaults = { :code => 200, :headers => {}, :body => "I'm not SOAP" }
response = defaults.merge options
- http_response = HTTPI::Response.new(response[:code], response[:headers], response[:body])
+ http_response = Responses.mock_faraday(response[:code], response[:headers], response[:body])
Savon::Response.new(http_response, globals, locals)
end
diff --git a/spec/savon/soap_fault_spec.rb b/spec/savon/soap_fault_spec.rb
index 1dde0501..6e38dbe0 100644
--- a/spec/savon/soap_fault_spec.rb
+++ b/spec/savon/soap_fault_spec.rb
@@ -9,7 +9,7 @@
let(:soap_fault_nc) { Savon::SOAPFault.new new_response(:body => Fixture.response(:soap_fault)), nori_no_convert }
let(:soap_fault_nc2) { Savon::SOAPFault.new new_response(:body => Fixture.response(:soap_fault12)), nori_no_convert }
let(:another_soap_fault) { Savon::SOAPFault.new new_response(:body => Fixture.response(:another_soap_fault)), nori }
- let(:soap_fault_no_body) { Savon::SOAPFault.new new_response(:body => {}), nori }
+ let(:soap_fault_no_body) { Savon::SOAPFault.new new_response(:body => ''), nori }
let(:no_fault) { Savon::SOAPFault.new new_response, nori }
let(:nori) { Nori.new(:strip_namespaces => true, :convert_tags_to => lambda { |tag| Savon::StringUtils.snakecase(tag).to_sym }) }
@@ -19,9 +19,9 @@
expect(Savon::SOAPFault.ancestors).to include(Savon::Error)
end
- describe "#http" do
- it "returns the HTTPI::Response" do
- expect(soap_fault.http).to be_an(HTTPI::Response)
+ describe "http" do
+ it "returns the Faraday::Response" do
+ expect(soap_fault.http).to be_an(Faraday::Response)
end
end
@@ -52,7 +52,7 @@
end
[:message, :to_s].each do |method|
- describe "##{method}" do
+ describe "#{method}" do
it "returns a SOAP 1.1 fault message" do
expect(soap_fault.send method).to eq("(soap:Server) Fault occurred while processing.")
end
@@ -83,7 +83,7 @@
end
end
- describe "#to_hash" do
+ describe "to_hash" do
it "returns the SOAP response as a Hash unless a SOAP fault is present" do
expect(no_fault.to_hash[:authenticate_response][:return][:success]).to be_truthy
end
@@ -141,7 +141,7 @@ def new_response(options = {})
defaults = { :code => 500, :headers => {}, :body => Fixture.response(:authentication) }
response = defaults.merge options
- HTTPI::Response.new response[:code], response[:headers], response[:body]
+ Responses.mock_faraday response[:code], response[:headers], response[:body]
end
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index d780f663..50fc7097 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -28,4 +28,3 @@
config.disable_monkey_patching!
end
-HTTPI.log = false
diff --git a/spec/support/adapters.rb b/spec/support/adapters.rb
deleted file mode 100644
index d791e275..00000000
--- a/spec/support/adapters.rb
+++ /dev/null
@@ -1,49 +0,0 @@
-# frozen_string_literal: true
-require 'httpi/adapter/httpclient'
-
-# Proxy adapter. Records all requests and passes them to HTTPClient
-class AdapterForTest < HTTPI::Adapter::Base
-
- register :adapter_for_test
-
- def initialize(request)
- @@requests ||= []
- @@requests.push request
- @request = request
- @worker = HTTPI::Adapter::HTTPClient.new(request)
- end
-
- def client
- @worker.client
- end
-
- def request(method)
- @@methods ||= []
- @@methods.push method
- @worker.request(method)
- end
-
-end
-
-# Fake adapter with request recording.
-# Takes path from url and returns fixture WSDL with that name.
-class FakeAdapterForTest < HTTPI::Adapter::Base
-
- register :fake_adapter_for_test
-
- def initialize(request)
- @@requests ||= []
- @@requests.push request
- @request = request
- end
-
- attr_reader :client
-
- def request(method)
- @@methods ||= []
- @@methods.push method
- target = @request.url.path.to_sym
- HTTPI::Response.new(200, {}, Fixture.wsdl(target))
- end
-
-end
diff --git a/spec/support/responses.rb b/spec/support/responses.rb
new file mode 100644
index 00000000..1d4b005e
--- /dev/null
+++ b/spec/support/responses.rb
@@ -0,0 +1,8 @@
+class Responses
+ class << self
+ def mock_faraday(code, headers, body)
+ env = Faraday::Env.from(status: code, response_headers: headers, response_body: body)
+ Faraday::Response.new(env)
+ end
+ end
+end
\ No newline at end of file