Description
Given I execute some code as follows:
require "savon"
# create a client for your SOAP service
client = Savon::Client.new("http://service.example.com?wsdl")
client.wsdl.soap_actions
# => [:create_user, :get_user, :get_all_users]
# execute a SOAP request to call the "getUser" action
response = client.request(:get_user) do
soap.body = { :id => 1 }
end
# ... do stuff with response...
# update the user
response = client.request(:update_user) do
soap.body = { :id => user.id, :email => "cornholio@email.com", :name => "Alter Ego" }
end
The second call to client.request ends up using a recycled http object.
It's allocated here:
https://github.com/rubiii/savon/blob/6b6fe262/lib/savon/client.rb#L45
Then used here:
https://github.com/rubiii/savon/blob/6b6fe262/lib/savon/client.rb#L79
Then it's ultimately configured here:
https://github.com/rubiii/savon/blob/6b6fe262/lib/savon/soap/request.rb#L40-43
The problem is with this:
http.headers["Content-Length"] ||= soap.to_xml.length.to_s
Since the header was sent from the first request (the call to get_user), the call to update_user inherits the content length. Ultimately, this causes the server to receive truncated XML, because the call to update_user in fact has a longer content length, so the server usually returns a 400 (malformed) response.
I'm not sure what the best way to fix this is. As it stands, I see two options:
- Change the '||=' default assignment operators to '=' assignment operators here:
https://github.com/rubiii/savon/blob/6b6fe262/lib/savon/soap/request.rb#L40-43
- Instantiate a new HTTP object in client.rb with each request. This would mean storing the cookie in another object and setting the cookie each time.
What way do you think is better? I tend to think #2
is better, but #1
is obviously the easiest fix.