Make Soap Body tag self closing?

J

jammendolia

Hello all,

I posted this question last week in the soap4r group but haven't
received any replies. So I'm hoping maybe someone here can help.

My situation is this; I 've generated the default/driver/
mappingRegistry using wsdl2ruby for a client that will consume .Net
web service

I have created a customized SOAP::Filter::Handler that takes care of
assigning the soap: namespace to the elements, and I've created a
customized SOAP::Header::Handler that takes care of the custom auth
header elements. So far, so good.

I've compared the generated request between a working .Net client and
my ruby client, and they are virtually identical with one exception;
for the initial authentication call (e.g. signon), there is no payload
in the soap:Body. The .Net client generates this empty body as
<soap:Body/> (self closing) whereas the ruby version generates
<soap:Body></soap:Body>.

I realize both are perfectly valid and should be interpreted the same
way, but unfortunately, this is not the case. I don''t have access to
the source code for this web service, but I was able to decompile the
dll, and guess what... Whoever wrote it, has some code that is
checking specifically for the existence of a self closed body tag.
When the code doesn't see the self closed tag, it assumes there will
be a payload, and proceeds to do a substring on it to get a known
piece of data out. Unfortunately again, this is causing a .Net
exception to be thrown (index out of range type of exception) which
blows the whole deal.

Ideally, I'd have the web service modified to use an XML DOM parser
instead, but I don't have this luxury.

So, my question is; Is there any way to override the default behavior
in soap4r to make it render the empty body with a self closing tag
(<soap:Body/>)?

I really hope so because I'd hate to have to scrap this project over
something so silly!

Thanks in advance!
 
J

James Britt

jammendolia said:
Hello all,

I posted this question last week in the soap4r group but haven't
received any replies. So I'm hoping maybe someone here can help.

My situation is this; I 've generated the default/driver/
mappingRegistry using wsdl2ruby for a client that will consume .Net
web service

I have created a customized SOAP::Filter::Handler that takes care of
assigning the soap: namespace to the elements, and I've created a
customized SOAP::Header::Handler that takes care of the custom auth
header elements. So far, so good.

I've compared the generated request between a working .Net client and
my ruby client, and they are virtually identical with one exception;
for the initial authentication call (e.g. signon), there is no payload
in the soap:Body. The .Net client generates this empty body as
<soap:Body/> (self closing) whereas the ruby version generates
<soap:Body></soap:Body>.
...

So, my question is; Is there any way to override the default behavior
in soap4r to make it render the empty body with a self closing tag
(<soap:Body/>)?

A hack: take the final XML and do a string substitution.

final_xml.sub!( '<soap:Body></soap:Body>', '<soap:Body/>')


untested, YMMV, etc.
 
J

jammendolia

A hack: take the final XML and do a string substitution.

final_xml.sub!( '<soap:Body></soap:Body>', '<soap:Body/>')

Thanks for the reply James!

I had thought of using this approach, and I'm completely open to it,
my problem is that I'm not sure where I can intercept the request to
make this change. I need to hook in somewhere after it's been
generated but before it's sent across the wire.

Any suggestions?

Thanks again,

Joe
 
Y

yermej

Thanks for the reply James!

I had thought of using this approach, and I'm completely open to it,
my problem is that I'm not sure where I can intercept the request to
make this change. I need to hook in somewhere after it's been
generated but before it's sent across the wire.

Any suggestions?

Thanks again,

Joe

I'm not positive, but I think you could implement James's idea in
SOAP::RPC::proxy#marshal (in /usr/lib/ruby/1.8/soap/rpc/proxy.rb
here).

I haven't tested this, but I would save this:

module SOAP
module RPC

class Proxy
private
class Operation
private
def marshal(env, opt)
send_string = Processor.marshal(env, opt)
send_string && send_string.sub!('<soap:Body></soap:Body>',
'<soap:Body/>')
StreamHandler::ConnectionData.new(send_string)
end
end
end

end
end

and require it in your project. I'm not sure I have all the privates
in the right place, but that should be close.

Jeremy
 
Y

yermej

I'm not positive, but I think you could implement James's idea in
SOAP::RPC::proxy#marshal (in /usr/lib/ruby/1.8/soap/rpc/proxy.rb
here).

Sorry. That should be SOAP::RPC::proxy::Operation#marshal.
 
J

jammendolia

Sorry. That should be SOAP::RPC::proxy::Operation#marshal.

Thanks for the feedback. I managed to get it working by extending the
SOAP::RPC::proxy class like this:


require 'soap/rpc/proxy'

class CustomProxy < SOAP::RPC::proxy

# Here, I override the route method so that I can generate a self
closed body tag.
def route(req_header, req_body, reqopt, resopt)
req_env = ::SOAP::SOAPEnvelope.new(req_header, req_body)
unless reqopt[:envelopenamespace].nil?
set_envelopenamespace(req_env, reqopt[:envelopenamespace])
end
reqopt[:external_content] = nil
conn_data = marshal(req_env, reqopt)

#hack to generate self-closed soap:Body tag
conn_data.send_string = conn_data.send_string.sub!('<soap:Body></
soap:Body>', '<soap:Body/>') if !
conn_data.send_string.rindex("<soap:Body></soap:Body>").nil?

if ext = reqopt[:external_content]
mime = MIMEMessage.new
ext.each do |k, v|
mime.add_attachment(v.data)
end
mime.add_part(conn_data.send_string + "\r\n")
mime.close
conn_data.send_string = mime.content_str
conn_data.send_contenttype = mime.headers['content-type'].str
end
conn_data = @streamhandler.send(@endpoint_url, conn_data,
reqopt[:soapaction])
if conn_data.receive_string.empty?
return nil
end
unmarshal(conn_data, resopt)
end
end
end

And then, to make the soap4r generated driver use it, I modified the
generated initializer so that it no longer calls "super". I then
implemented the code from the original proxy.rb initialize which
includes instantiating the proxy. Bu instead of using the default
proxy, I use my custom one. here's an example:

Driver.rb

def initialize...
....

# we do not call the base initialize here because we
# need to use a custom proxy. So all of the super
# initialize components are performed here

# super(endpoint_url, nil)

@namespace = nil
@soapaction = nil
@options = setup_options
@wiredump_file_base = nil
@proxy = CusromProxy.new(endpoint_url, @soapaction, @options)

....

Thanks again for pointing me in the right direction.

I hope this will help someone else in the future :)

Joe
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,994
Messages
2,570,223
Members
46,810
Latest member
Kassie0918

Latest Threads

Top