SOAP can be a huge
PITA in Ruby if you're not dealing with a web service that falls
under the defaults. In particular, if your web service falls under
HTTPS where you need to change the default
certificate acceptance, or if you need to authenticate before seeing the
WSDL, you're
SOL as far as I
can tell as of writing this post. (If you know of a way that doesn't resort to this complexity, please speak up!)
I was using Ruby 1.8.7 and soap4r 1.5.8, but this may apply to other versions.
Anyway, here are a couple of monkey patches to help get you there if you're having trouble.
If you need to change the SSL verify mode, for example, to accept a certificate unconditionally, you can use this
monkeypatch:
def monkeypatch_httpclient_sslsocketwrap(ssl_verify_mode)
return unless ssl_verify_mode
@sslsocket_monkeypatched = true
require 'soap/nethttpclient'
block = <<END
alias :original_initialize :initialize
def initialize(socket, context, debug_dev = nil)
unless SOAP::NetHttpClient::SSLEnabled
raise ConfigurationError.new('Ruby/OpenSSL module is required')
end
@context = context
@context.verify_mode = #{ssl_verify_mode}
@socket = socket
@ssl_socket = create_openssl_socket(@socket)
@debug_dev = debug_dev
end
END
HTTPClient::SSLSocketWrap.class_eval block
end
If you need to authenticate before seeing the WSDL, you'll need this patch:
def monkeypatch_authentication(username, password)
return unless username && password
@auth_monkeypatched = true
require 'wsdl/xmlSchema/importer'
block = <<END
alias :original_fetch :fetch
def fetch(location)
warn("importing: " + location) if $DEBUG
content = nil
normalizedlocation = location
if location.scheme == 'file' or
(location.relative? and FileTest.exist?(location.path))
content = File.open(location.path).read
normalizedlocation = URI.parse('file://' + File.expand_path(location.path))
elsif location.scheme and location.scheme.size == 1 and
FileTest.exist?(location.to_s)
content = File.open(location.to_s).read
else
client = web_client.new(nil, "WSDL4R")
client.proxy = ::SOAP::Env::HTTP_PROXY
client.no_proxy = ::SOAP::Env::NO_PROXY
client.set_auth(location, "#{username}", "#{password}") #added for auth
if opt = ::SOAP::Property.loadproperty(::SOAP::PropertyName)
http_opt = opt["client.protocol.http"]
::SOAP::HTTPConfigLoader.set_options(client, http_opt) if http_opt
end
content = client.get_content(location)
end
return content, normalizedlocation
end
END
WSDL::XMLSchema::Importer.class_eval block
end
Hope that helps someone else avoid days' long foray into piecing together blogs posts, message boards, and
searching through source code.
And because you might get here via a search for related terms, normal access that only requires basic authentication
could be done like this, without opening existing classes:
class SendBasicAuthFilter < SOAP::Filter::StreamHandler
def initialize(loginname, password)
@authorization = 'Basic ' + [ loginname + ':' + password ].pack('m').delete("\r\n")
end
def on_http_outbound(req)
req.header.delete('Authorization')
req.header['Authorization'] = @authorization
end
def on_http_inbound(req, res)
end
end
list_service_url = 'https://somesite/path/?WSDL'
list_service_driver = SOAP::WSDLDriverFactory.new(list_service_url).create_rpc_driver
user = 'username'
pass = 'password'
list_service_driver.streamhandler.filterchain << SendBasicAuthFilter.new(user,pass)
I'm very welcoming of suggestions regarding how these things might be better accomplished. Resorting to this
messy level of monkeypatching just sucks. Let me know in the comments.
Hey! Why don't you make your life easier and subscribe to the full post
or short blurb RSS feed? I'm so confident you'll love my smelly pasta plate
wisdom that I'm offering a no-strings-attached, lifetime money back guarantee!
Leave a comment
There are no comments for this entry yet.
Leave a comment