Monday, August 17, 2009

SOAP4R clients with proxy and basic authentication

If you go and google "soap4r proxy" you'll get a lot of helpful hits that show you how to set your http_proxy environment variable. Unfortunately there's not many (read none) that are devoted to configuring it programatically. So here it is.

If you run the wsdl2r tool to generate code you'll end up with a defaultDriver.rb file. This defines the RPC driver that you can call the webservices against. You'll want to ensure that it's going to use the HTTPClient libraries. If you installed it manually you should be fine, but if you installed it with rubygems you'll need to add requires rubygems to the top of your file.

Next you'll need to add the basic authentication configuration and the proxy configuration to the SOAP driver (using some horrendously poorly documented features of the SOAP4R library). The best place to do this is in your initialize method. You'll end up with a class that resembles the following (I generated mine from a WSDL pertaining to sending SMS messages):


require 'rubygems'
require 'xsd/qname'
require 'httpclient'
require 'soap/rpc/driver'

class MessagingService < ::SOAP::RPC::Driver

DefaultEndpointUrl = "https://a.secure.soap/endpoint"
MappingRegistry = ::SOAP::Mapping::Registry.new

Methods = [
[ XSD::QName.new("https://somesoapgeneratedmappingname", "sendSMS"),
"",
"sendSMS",
[ ["in", "to", ["::SOAP::SOAPString"]],
["in", "application", ["::SOAP::SOAPString"]],
["in", "message", ["::SOAP::SOAPString"]],
["in", "options", ["::SOAP::SOAPInt"]],
["retval", "sendSMSReturn", ["::SOAP::SOAPString"]] ],
{ :request_style => :rpc, :request_use => :encoded,
:response_style => :rpc, :response_use => :encoded }
]
]

def initialize(endpoint_url = nil)
endpoint_url ||= DefaultEndpointUrl
super(endpoint_url, nil)
self.mapping_registry = MappingRegistry
init_methods
self.options["protocol.http.basic_auth"] << [endpoint_url,'username','password']
self.options["protocol.http.proxy"] = "http://yourproxyserver:8080/"
end

private

def init_methods
Methods.each do |definitions|
opt = definitions.last
if opt[:request_style] == :document
add_document_operation(*definitions)
else
add_rpc_operation(*definitions)
qname = definitions[0]
name = definitions[2]
if qname.name != name and qname.name.capitalize == name.capitalize
::SOAP::Mapping.define_singleton_method(self, qname.name) do |*arg|
__send__(name, *arg)
end
end
end
end
end
end

4 comments:

  1. Question for you: Should I see something involving basic auth in the SOAP request message being transmitted to the server?

    I modified my soap driver class to match what you have done, but I still don't see the basic auth information going across the wire.

    ReplyDelete
  2. unfortunately it does not work for me either

    ReplyDelete
  3. Hi AJ,

    As per your suggestion here, I need to add a proxy to my code if I am to host in the USA on Engine Yard and access the API I need to in the UK.

    I have grabbed a couple of proxies from HideMyAss.com and added them as per your suggestions above. You can see it here on line 241
    http://github.com/lukebyrne/Betfair-API/blob/master/lib/BFGlobalServiceDriver.rb.

    I am getting the following error though, have you come across them when trying this method and if so is there a way around them?

    Argument error -
    unsupported proxy https://131.179.50.72:80

    Here is the App trace.
    /Users/lukeb/.gem/ruby/1.8/gems/httpclient-2.1.5.2/lib/httpclient.rb:398:in `proxy='
    /Users/lukeb/.gem/ruby/1.8/gems/soap4r-1.5.8/lib/soap/httpconfigloader.rb:21:in `set_options'
    /Users/lukeb/.gem/ruby/1.8/gems/soap4r-1.5.8/lib/soap/property.rb:115:in `call'
    /Users/lukeb/.gem/ruby/1.8/gems/soap4r-1.5.8/lib/soap/property.rb:115:in `[]='
    /Users/lukeb/.gem/ruby/1.8/gems/soap4r-1.5.8/lib/soap/property.rb:114:in `each'
    /Users/lukeb/.gem/ruby/1.8/gems/soap4r-1.5.8/lib/soap/property.rb:114:in `[]='
    /Users/lukeb/Documents/Dev Projects/Rails/workspace/sno/vendor/plugins/Betfair-API/lib/BFGlobalServiceDriver.rb:243:in `initialize'
    /Users/lukeb/Documents/Dev Projects/Rails/workspace/sno/vendor/plugins/Betfair-API/lib/betfair_api.rb:42:in `new'
    /Users/lukeb/Documents/Dev Projects/Rails/workspace/sno/vendor/plugins/Betfair-API/lib/betfair_api.rb:42:in `global'
    /Users/lukeb/Documents/Dev Projects/Rails/workspace/sno/vendor/plugins/Betfair-API/lib/betfair_api.rb:38:in `login'
    /Users/lukeb/Documents/Dev Projects/Rails/workspace/sno/vendor/plugins/Betfair-API/lib/betfair_api.rb:56:in `initialize'
    /Users/lukeb/Documents/Dev Projects/Rails/workspace/sno/app/models/betfair_runner.rb:5:in `new'
    /Users/lukeb/Documents/Dev Projects/Rails/workspace/sno/app/models/betfair_runner.rb:5
    /Users/lukeb/Documents/Dev Projects/Rails/workspace/sno/app/controllers/betfair_runners_controller.rb:14:in `all'

    I look forward to your response.

    Kind regards,

    LB

    ReplyDelete
  4. @Rob: Not in the SOAP envelope but definitely in the HTTP request, I wiresharked the requests at dev time to make sure HttpClient was doing what it should and Basic HTTP auth was occurring.

    @Lukey: Looks like you've found a solution in the meantime. The problem looked like you were trying to hit the proxy server over ssl on port 80 instead of 443.

    ReplyDelete