Skip to content
33 changes: 17 additions & 16 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
.rvmrc
.byebug_history
.DS_Store
.yardoc
doc
rdox
coverage
tmp
*.rbc
*~
*.gem
.bundle
.rspec
Gemfile.lock
bin/
.rspec_status
.rvmrc
.byebug_history
.DS_Store
.yardoc
doc
rdox
coverage
tmp
*.rbc
*~
*.gem
.bundle
.rspec
Gemfile.lock
bin/
.rspec_status
.idea
12 changes: 0 additions & 12 deletions lib/savon/operation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -91,24 +91,12 @@ def set_locals(locals, block)
end

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 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

def build_connection(builder)
@globals[:endpoint] ||= endpoint
@locals[:soap_action] ||= soap_action
Expand Down
6 changes: 6 additions & 0 deletions lib/savon/options.rb
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ def initialize(options = {})
:convert_attributes_to => lambda { |k,v| [k,v] },
:multipart => false,
:adapter => nil,
:idle_timeout => 60*5,
:use_wsa_headers => false,
:no_message_tag => false,
:follow_redirects => false,
Expand Down Expand Up @@ -165,6 +166,11 @@ def write_timeout(write_timeout)
@options[:write_timeout] = write_timeout
end

# Idle Timeout in seconds. Only used for keep-alive connections.
def idle_timeout(idle_timeout)
@options[:idle_timeout] = idle_timeout
end

# The encoding to use. Defaults to "UTF-8".
def encoding(encoding)
@options[:encoding] = encoding
Expand Down
9 changes: 5 additions & 4 deletions lib/savon/request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,11 @@ def basic_auth

def ntlm_auth
begin
require 'rubyntlm'
require 'faraday/net_http_persistent'
connection.adapter :net_http_persistent, pool_size: 5
require 'faraday/ntlm_auth'
connection.adapter :net_http_persistent, pool_size: 5, idle_timeout: @globals[:idle_timeout] if @globals.include?(:idle_timeout)
connection.request :ntlm_auth, auth: @globals[:ntlm]
rescue LoadError
raise LoadError, 'Using NTLM Auth requires both `rubyntlm` and `faraday-net_http_persistent` to be installed.'
raise LoadError, 'The "faraday-ntlm_auth" gem is required for NTLM authentication. Please add it to your Gemfile and run `bundle install`.'
end
end

Expand Down Expand Up @@ -108,6 +108,7 @@ def build

def configure_headers
connection.headers = @globals[:headers] if @globals.include? :headers
connection.headers["Content-Type"] ||= "text/xml;charset=#{@globals[:encoding]}"
end
end

Expand Down
2 changes: 1 addition & 1 deletion lib/savon/version.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# frozen_string_literal: true
module Savon
VERSION = '3.0.0.rc1'
VERSION = '3.0.0.rc2'
end
1 change: 1 addition & 0 deletions savon.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ Gem::Specification.new do |s|
s.add_development_dependency "httpclient"
s.add_development_dependency "mutex_m"
s.add_development_dependency 'ostruct', '~> 0.6'
s.add_development_dependency "faraday-ntlm_auth"

s.add_development_dependency "byebug"
s.add_development_dependency "rake", ">= 12.3.3"
Expand Down
13 changes: 9 additions & 4 deletions spec/savon/options_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,13 @@
end
end

context "global :idle_timeout" do
it "sets the idle timeout for connection pools when net-http_persistent is used" do
client = new_client(:endpoint => @server.url, idle_timeout: 5, ntlm: ["admin", "secret", "domain"])
expect(client.globals[:idle_timeout]).to eq(5) # No great way to test this actually _works_ from a public setting
end
end

context "global :encoding" do
it "changes the XML instruction" do
client = new_client(:endpoint => @server.url(:repeat), :encoding => "ISO-8859-1")
Expand Down Expand Up @@ -563,15 +570,13 @@ def to_s
end

context "global :ntlm" do
it "sets the ntlm credentials to use" do
it "sets the ntlm credentials to use, sending a challenge to the server" do
credentials = ["admin", "secret"]
client = new_client(:endpoint => @server.url, :ntlm => credentials)

# TODO: find a way to integration test this. including an entire ntlm
# server implementation seems a bit over the top though.
Savon::Operation.any_instance.expects(:handle_ntlm)

response = client.call(:authenticate)
expect(response.http.env[:request_headers]["Authorization"]).to match(/NTLM/)
end
end

Expand Down
4 changes: 2 additions & 2 deletions spec/savon/request_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -389,12 +389,12 @@ def new_soap_request
describe "ntlm auth" do
it "uses the net-http-persistent adapter in faraday" do
globals.ntlm("han", "super-secret")
http_connection.expects(:adapter).with(:net_http_persistent, {:pool_size => 5})
http_connection.expects(:adapter).with(:net_http_persistent, {:pool_size => 5, idle_timeout: 60 * 5})
new_soap_request.build
end

it "is not set otherwise" do
http_connection.expects(:adapter).with(:net_http_persistent, {:pool_size => 5}).never
http_connection.expects(:adapter).with(:net_http_persistent, {:pool_size => 5, idle_timeout: 60 * 5}).never
new_soap_request.build
end
end
Expand Down