How to add attachment to doc/lit soap request?

318 views
Skip to first unread message

sandi

unread,
Oct 11, 2005, 10:52:04 AM10/11/05
to soap4r
I'm a ruby/soap newbie, so sorry if the answer to this should be
obvious.

I'm invoking a doc/literal web service using SOAP::RPC::Driver and the
add_document_method. Everything works fine; the Axis web service I'm
calling gets invoked and I get the correct response.

However, I need to add an attachment to my request and I can't figure
out how to do it. I've looked at the swa samples, but they don't seem
to be exactly what I want.

I've crawled through the code and it seems maybe I'm supposed to be
adding 'external_content' to my req_env. Is this correct? If so,
could someone post an example of how to do it?

Thanks in advance,
Sandi


My code so far:

require 'soap/rpc/driver'
require 'soap/rpc/element'

class GrantsGovClient

def initialize()
XSD::Charset.encoding = 'UTF8'
@server =
"http://localhost:11870/app-s2s-server/services/ApplicantIntegrationSoapPort"
@namespaceOfMyRequest =
"http://apply.grants.gov/WebServices/ApplicantIntegrationServices-V1.0"
@soapActionInHeader =
"http://localhost:11870/app-s2s-server/services/ApplicantIntegrationSoapPort/SubmitApplication"

end

def run
# Driver initialize and method definition
@drv1 = SOAP::RPC::Driver.new(@server, @namespaceOfMyRequest)
@drv1.add_document_method('submitIt', @namespaceOfMyRequest +
'GrantApplicationXML',
XSD::QName.new(@namespaceOfMyRequest,
'SubmitApplicationRequest'),
XSD::QName.new(@namespaceOfMyRequest,
'SubmitApplicationResponse'))

# Method invocation
theXML = SOAP::SOAPRawString.new('<GrantApplicationXML
xsi:type="xsd:string">' + body + '</GrantApplicationXML>')
@drv1.submitIt(theXML)

return 0
end

def body
theBody =
'&lt;?xml version=&quot;1.0&quot;
encoding=&quot;UTF-8&quot;?&gt;&#xd;
&lt;grant:GrantApplication
xmlns:grant=&quot;http://apply.grants.gov/system/MetaGrantApplication&quot;
xmlns:att=&quot;http://apply.grants.gov/system/Attachments-V1.0&quot;
xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
xsi:schemaLocation=&quot;http://apply.grants.gov/system/MetaGrantApplication
http://atws.grants.gov/applicant/samples/opportunities/schemas/oppAPP-S2S-TEST-RR-cfda00000.xsd&quot;&gt;&#xd;
&#xd;

...etc...

end
end

Emil Marceta

unread,
Oct 12, 2005, 1:41:12 AM10/12/05
to soa...@googlegroups.com
On 10/11/05, sandi <sand...@mindspring.com> wrote:

> However, I need to add an attachment to my request and I can't figure
> out how to do it. I've looked at the swa samples, but they don't seem
> to be exactly what I want.

Could you explain a little bit more what is not working for you?
The simplest example goes like this:
driver.submitIt(SOAP::Attachment.new(File.open('picture.jpeg')))

The service (Axis or else) has to declare the mime support in the
wsdl binding/operation.

Consider the wsdl snippet. It is rpc/encoded but should provide
the idea what needs to be done.

<wsdlsoap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="submitIt">
<wsdlsoap:operation soapAction=""/>
<wsdl:input name="submitItRequest">
<mime:multipartRelated>
<mime:part>
<wsdlsoap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="urn:EchoAttachmentsService" use="encoded"/>
</mime:part>
<mime:part>
<mime:content part="source" type="*/*"/>
</mime:part>
</mime:multipartRelated>
</wsdl:input>
<wsdl:output name="echoOneResponse">
<mime:multipartRelated>
<mime:part>
<wsdlsoap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="urn:EchoAttachmentsService" use="encoded"/>
</mime:part>
<mime:part>
<mime:content part="returnqname" type="*/*"/>
</mime:part>
</mime:multipartRelated>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>

And of course server implementaiton.

The swa is the SOAP with attachments implementation in soap4r
as per spec http://www.w3.org/TR/SOAP-attachments, and there is
no other of sending attachments in soap4r that I'm aware of.

Things that might be of relating interest is that .NET never supported
swa standard. They used properietary 'dime' and moving towards MTOM
(supposed to superceed swa)


> # Method invocation
> theXML = SOAP::SOAPRawString.new('<GrantApplicationXML
> xsi:type="xsd:string">' + body + '</GrantApplicationXML>')
> @drv1.submitIt(theXML)

As a side note - this is unrelated to the swa - the above is rpc/encoded
and not the doc/literal.

cheers,
emil

Sandi Metz

unread,
Oct 12, 2005, 11:32:56 AM10/12/05
to soa...@googlegroups.com
Emil,

Thanks for your reply.  And sorry again in advance; I feel like I'm missing something very basic here and that when I finally understand this whole thing it will seem very simple.

Here's the relevant bit of the wsdl of the service I'm invoking.

    <wsdl:binding name="ApplicantIntegrationSoapBinding"
        type="apis:ApplicantIntegrationPortType">
        <soap:binding style="document"
            transport="http://schemas.xmlsoap.org/soap/http" />
        <wsdl:operation name="SubmitApplication">
            <soap:operation
                soapAction="http://localhost:8080/app-s2s-server/services/ApplicantIntegrationSoapPort/SubmitApplication"
                style="document" />
            <wsdl:input>
                <soap:body use="literal" />
                <mime:content part="Attachment" type="*/*" />
    
        </wsdl:input>
            <wsdl:output>
                <soap:body use="literal" />
            </wsdl:output>
            <wsdl:fault name="SubmitApplicationErrorMessage">
                <soap:fault name="SubmitApplicationErrorMessage"
                    use="literal" />
            </wsdl:fault>
        </wsdl:operation>
    </wsdl:binding>


As the soap body, I'm sending a xml document that's been turned into a long string (by changing <> into &lt; and :gt;).  The xml document contains a node (blue below) that describes an attachment to the invoked web service.  Here's an example of the document before it gets changed into a string.
<grant:GrantApplication xmlns:grant="http://apply.grants.gov/system/MetaGrantApplication" xmlns:att="http://apply.grants.gov/system/Attachments-V1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://apply.grants.gov/system/MetaGrantApplication http://atws.grants.gov/applicant/samples/opportunities/schemas/oppAPP-S2S-TEST-RR-cfda00000.xsd">
    <header:GrantSubmissionHeader xmlns:header="http://apply.grants.gov/system/Header-V1.0" xmlns:glob="http://apply.grants.gov/system/Global-V1.0" xsi:schemaLocation="http://apply.grants.gov/system/Header-V1.0 http://apply.grants.gov/system/schemas/Header-V1.0.xsd" glob:schemaVersion="1.0">
        <glob:HashValue glob:hashAlgorithm="SHA-1">3ca7e2b20a21112c0587e4ef35ea64c043120616</glob:HashValue>
        <header:AgencyName>S2S Testing</header:AgencyName>
    </header:GrantSubmissionHeader>
    <grant:Forms>
        <RR_SF424:RR_SF424 xmlns:RR_SF424="http://apply.grants.gov/forms/RR_SF424-V1.0" xmlns:globLib="http://apply.grants.gov/system/GlobalLibrary-V1.0" xmlns:glob="http://apply.grants.gov/system/Global-V1.0" globLib:FormVersion="1.0" xsi:schemaLocation="http://apply.grants.gov/forms/RR_SF424-V1.0 http://apply.grants.gov/forms/schemas/RR_SF424-V1.0.xsd">
            <RR_SF424:SubmissionTypeCode>Preapplication</RR_SF424:SubmissionTypeCode>
            <RR_SF424:SubmittedDate>1967-08-13</RR_SF424:SubmittedDate>
            <RR_SF424:ApplicantID>String</RR_SF424:ApplicantID>
            <RR_SF424:StateReceivedDate>1967-08-13</RR_SF424:StateReceivedDate>
            <RR_SF424:StateID>String</RR_SF424:StateID>
            <RR_SF424:FederalID>String</RR_SF424:FederalID>
             ...snip...
            <RR_SF424:PreApplicationAttachment>
                <att:FileName>dhtml1-sample.pdf</att:FileName>
                <att:MimeType>application/octet-stream</att:MimeType>
                <att:FileLocation att:href='cid:dhtml1-sample.pdf_1128620854368@localhost' />
                <glob:HashValue glob:hashAlgorithm='SHA-1'>qyRZasWxrQHjt9v1ftkYNRhKVEE=</glob:HashValue>
            </RR_SF424:PreApplicationAttachment>

        </RR_SF424:RR_SF424>
         ...snip...
    </grant:Forms>
</grant:GrantApplication>
After turning it into a string it (obviously) looks like this:
&lt;grant:GrantApplication xmlns:grant="http://apply.grants.gov/system/MetaGrantApplication" xmlns:att="http://apply.grants.gov/system/Attachments-V1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://apply.grants.gov/system/MetaGrantApplication http://atws.grants.gov/applicant/samples/opportunities/schemas/oppAPP-S2S-TEST-RR-cfda00000.xsd"&gt;
    etc.

So, at this point, my soap body really is just a long string, which I can pass along to the web service by saying

    @drv1 = SOAP::RPC::Driver.new(@server, @namespaceOfMyRequest)
    @drv1.add_document_method('submitIt', @namespaceOfMyRequest + 'GrantApplicationXML',
      XSD::QName.new(@namespaceOfMyRequest, 'SubmitApplicationRequest'),
      XSD::QName.new(@namespaceOfMyRequest, 'SubmitApplicationResponse'))
    
    # Method invocation
    theXML = SOAP::SOAPRawString.new('<GrantApplicationXML xsi:type="xsd:string">' + body + '</GrantApplicationXML>')   
    @drv1.submitIt(theXML)


All is well and good.  The web service is invoked and I get the correct response back. 

But now I need to send the attachment also, and while the syntax for that is probably obvious, it escapes me.

So, can you suggest a way for me to send a long string in the soap body and ALSO send an attachment?  I don't need soap4r to generate the stuff in blue above.  I actually need to generate, or at least have access to, that node myself, since I need to add the HashValue to it.

I just want to send my string along, and then also send attachments.  My submit method only takes one argument, so I can't
   att = SOAP::Attachment.new("c:/temp/dhtml-sample.pdf")
    theXML = SOAP::SOAPRawString.new('<GrantApplicationXML xsi:type="xsd:string">' + body + '</GrantApplicationXML>')   
    @drv1.submitIt(theXML, att)  #wrong number of arguments

I've looked at using 'add_document_operation', since when I look in proxy.route(req_header, req_body, reqopt, resopt), I see code that takes external_content and seems to send attachments.  It looks like 'add_document_operation' might let me set this up, but I can't sort out how.

I greatly appreciate any suggestions that you can give.

Thanks,
Sandi

Emil Marceta

unread,
Oct 13, 2005, 2:22:02 AM10/13/05
to soa...@googlegroups.com
On 10/12/05, Sandi Metz <sand...@mindspring.com> wrote:
>
> I just want to send my string along, and then also send attachments. My
> submit method only takes one argument, so I can't
> att = SOAP::Attachment.new("c:/temp/dhtml-sample.pdf")
> theXML = SOAP::SOAPRawString.new('<GrantApplicationXML
> xsi:type="xsd:string">' + body + '</GrantApplicationXML>')
> @drv1.submitIt(theXML, att) #wrong number of arguments
>
> I've looked at using 'add_document_operation', since when I look in
> proxy.route(req_header, req_body, reqopt, resopt), I see code that takes
> external_content and seems to send attachments. It looks like
> 'add_document_operation' might let me set this up, but I can't sort out how.

I've looked into it in more detail, and it appears that the doc/lit does not
support swa at the moment. I see only rpc/encoded swa support. If you
would like to use it today, you probably need to switch to rpc/encoded.
Looking at your service and arguments it looks this may be an option
- assuming you have control of the server side -, as the message does
not appear to be xsd driven, it is basically a string ....

Basically the issue is a around argument rules with those two uses.
With the doc/lit use service there is only one message part and the part
is defined by the schema. This results in only one possible argument,
so there is no logical place at the moment where to put the SOAPAttachment
argument.

The rpc/encoded services accept multiple message parts as arguments and the
soap4r basically support allows passing attachments as arguments. This
is very easy to use, but incompatible with doc/lit. The underlying cause
is basically how swa is designed, as it is out of soap really.

To fix this, the place where to pass the SOAPAttachment with doc/lit
has to be found.
Perhaps allowing accepting arbitrary number of arguments and
include only non-SOAPAttachment in the actual arguments count check.

Or maybe as the block of the invoking method where the block
argument represents some sort of invocation/message context of
the current invocation, where attachments can be collected. This
could serve as a context for many things later, including security etc.
Something like :

p driver.put_file('my_file_name') { |x|
x.attachments << SOAP::Attachment.new(file)
}

I think I vote for this one.

Also the wsdl2ruby does not work at the moment with wsdl that declares
attachments (multipart messages). When it encounters the
<mime:multipartRelated> in the wsdl input/output binding definition...

emil

Sandi Metz

unread,
Oct 14, 2005, 1:16:53 PM10/14/05
to soa...@googlegroups.com
Thanks again, Emil,  for your reply.  Now that I know that I'm not just missing something obvious, I'll go hack around in the code and see what I can do.

I appreciate your help.

Sandi

Sandi Metz

unread,
Oct 20, 2005, 11:42:15 AM10/20/05
to soa...@googlegroups.com, Sandra K Metz
All (and especially, Emil :-) ),

I've extended soap4r to allow the client of a doc/literal web service to send attachments when invoking the service.  As a ruby AND soap newbie, I'd deeply appreciate feedback about the potential perils of the following code. 

I have tested these changes against the doc/literal service I'm using and they do seem to work; the attachments are received correctly on the server side.

Thanks in advance for your thoughts and input.
Sandi

-----------------------------------

Here's a snippet of code that gets a driver, adds a document method and invokes the doc/literal web service with attachments.  The full source of class
DUKE::GrantSubmitter is at the end of this email.

      grantXML        = createXML(aGrantSubmission)          #an instance of SOAP::SOAPRawString
      soapAttachments = createAttachments(aGrantSubmission)  #an array    of SOAP::Attachment

      aDriver = SOAP::RPC::Driver.new(SERVER + LOCATION, NAMESPACE)
      aDriver.add_document_method('submitGrant', nil, Array.new(soapAttachments.size + 1).fill(@reqQName), @resQName)
      result = eval("aDriver.submitGrant(grantXML #{createAttachmentArgString(soapAttachments)})")

Two things are of interest above.
1) In the add_document_method call, for the third argument I must pass an array instead of just a single QName.  This array must contain a QName entry for the body of the soap message and a QName entry for each attachment.  Therefore, the size of the array must be soapAttachments.size + 1. 

2) When calling the added method 'submitGrant', the number of arguments that get passed must exactly equal the number of items in the QName array above.  Therefore, this method call is built dynamically.

The above syntax causes the LiteralHandler class to attempt to process each of the arguments used in the 'submitGrant' call.  Thus, LiteralHandler must handle attachments.  By the time LiteralHandler gets invoked, the attachments have been turned into instances of SOAPExternalReference.  I made the following changes to LiteralHandler (mostly, as you can see, stolen straight from SoapHandler).

Original code is in green
Added code is in orange
################################################################################################
# SOAP::EncodingStyle::LiteralHandler
#
# Add attachment support
################################################################################################
require 'soap/encodingstyle/handler'
module SOAP
  module EncodingStyle
    class LiteralHandler < Handler
      ###
      ## encode interface.
      #
      def encode_data(generator, ns, data, parent)
        attrs = {}
        name = generator.encode_name(ns, data, attrs)
        data.extraattr.each do |k, v|
          # ToDo: check generator.attributeformdefault here
          if k.is_a?(XSD::QName)
            if k.namespace
              SOAPGenerator.assign_ns(attrs, ns, k.namespace)
              k = ns.name(k)
            else
              k = k.name
            end
          end
          attrs[k] = v
        end
        case data
        
        #////////////////////  DUKE ADDITION BEGINS \\\\\\\\\\\\\\\\\\\\\\
        when SOAPExternalReference
          data.referred
    #         I don't need to create the 'href', you better have done that yourself,
    #         but I DO need to know that this attachment exists and should be sent!
    #      attrs['href'] = data.refidstr
    #      generator.encode_tag(name, attrs)
        #////////////////////  DUKE ADDITION ENDS \\\\\\\\\\\\\\\\\\\\\\
        
        when SOAPRawString
          generator.encode_tag(name, attrs)
          generator.encode_rawstring(data.to_s)
        when XSD::XSDString
          generator.encode_tag(name, attrs)
          str = data.to_s
          str = XSD::Charset.encoding_to_xml(str, @charset) if @charset
          generator.encode_string(str)
        when XSD::XSDAnySimpleType
          generator.encode_tag(name, attrs)
          generator.encode_string(data.to_s)
        when SOAPStruct
          generator.encode_tag(name, attrs)
          data.each do |key, value|
            generator.encode_child(ns, value, data)
          end
        when SOAPArray
          generator.encode_tag(name, attrs)
          data.traverse do |child, *rank|
      data.position = nil
            generator.encode_child(ns, child, data)
          end
        when SOAPElement
          # passes 2 times for simplifying namespace definition
          data.each do |key, value|
            if value.elename.namespace
              SOAPGenerator.assign_ns(attrs, ns, value.elename.namespace)
            end
          end
          generator.encode_tag(name, attrs)
          generator.encode_rawstring(data.text) if data.text
          data.each do |key, value|
            generator.encode_child(ns, value, data)
          end
        else
          raise EncodingStyleError.new(
            "unknown object:#{data} in this encodingStyle")
        end
      end

      def encode_data_end(generator, ns, data, parent)
        #////////////////////  DUKE ADDITION BEGINS \\\\\\\\\\\\\\\\\\\\\\
        # if it was an attachment, there was no start tag and I need no end tag
        return nil if data.is_a?(SOAPAttachment)
        #////////////////////  DUKE ADDITION ENDS \\\\\\\\\\\\\\\\\\\\\\
        
        name = generator.encode_name_end(ns, data)
        cr = (data.is_a?(SOAPCompoundtype) or
          (data.is_a?(SOAPElement) and !data.text))
        generator.encode_tag_end(name, cr)
      end
    end
  end
end     


################################################################################################
# DUKE::GrantSubmitter
#
# Add attachment support
################################################################################################
module Duke
  #
  # GrantSubmitter
  # Given a GrantSumbmission, submit it to Grants.gov via their web service
  #
  class GrantSubmitter
  public
    SERVER    = "http://"+GRANTS_GOV_SERVER
    LOCATION  = "/app-s2s-server/services/ApplicantIntegrationSoapPort"
    NAMESPACE = "http://apply.grants.gov/WebServices/ApplicantIntegrationServices-V1.0"
    REQUEST   = 'SubmitApplicationRequest'
    RESPONSE  = 'SubmitApplicationResponse'
    BEGIN_XML = '<GrantApplicationXML xsi:type="xsd:string">'
    END_XML   = '</GrantApplicationXML>'
 
    def self.submit(aGrantSubmission)
      new.submit(aGrantSubmission)
    end


    def initialize()
      XSD::Charset.encoding = 'UTF8'
      @reqQName             = XSD::QName.new(NAMESPACE, REQUEST)
      @resQName             = XSD::QName.new(NAMESPACE, RESPONSE)   
    end   

    def submit(aGrantSubmission)
     
      aGrantSubmission.timeanddatesent = Time.now
      grantXML        = createXML(aGrantSubmission)   
      soapAttachments = createAttachments(aGrantSubmission)
     
      aDriver = SOAP::RPC::Driver.new(SERVER + LOCATION, NAMESPACE)
      aDriver.add_document_method('submitGrant', nil, Array.new(soapAttachments.size + 1).fill(@reqQName), @resQName)
     
      #must dynamically build this method since the number of args is variable
      #final method looks like 'aDriver.submitGrant(grantXML, soapAttachment[0], soapAttachment[1], etc)'
      begin     
        result = eval("aDriver.submitGrant(grantXML #{createAttachmentArgString(soapAttachments)})")
        aGrantSubmission.status               = 'ok'
        aGrantSubmission.trackingnumber       = result.grants_govTrackingNumber
        aGrantSubmission.timeanddatereceived  = result.receivedDateTime
      rescue SOAP::FaultError => e
        aGrantSubmission.status               = 'failed'
        #TODO truncate message size to however long ActiveRecord thinks soapmessage is
        aGrantSubmission.soapmessage = e.message
      end
    end
   
  private
   
    def createXML(aGrantSubmission)
      SOAP::SOAPRawString.new(BEGIN_XML + transform(aGrantSubmission.xmlstream) + END_XML)
    end

    def createAttachments(aGrantSubmission)
      atts = []
      aGrantSubmission.attachments.each do |aGrantAttachment|
        att = SOAP::Attachment.new(aGrantAttachment.content)       
        att.contentid=(aGrantAttachment.contentid.to_s)
        atts << att
      end

      atts
    end

    # return a string like ... 
    #   ', soapAttachments[0], soapAttachments[1], soapAttachments[2]'  etc
    def createAttachmentArgString(atts)
      s = ''
      atts.size.times {|i| s << ", soapAttachments[#{i}]"}
      s
    end
   
    def transform(someXML)
      someXML.gsub(/&/,'&amp;').gsub(/"/,'&quot;').gsub(/</,'&lt;').gsub(/>/,'&gt;')
    end
   
  end

Sandi Metz

unread,
Oct 20, 2005, 2:09:36 PM10/20/05
to soa...@googlegroups.com
    def initialize()
      XSD::Charset.encoding = 'UTF8'

NAKAMURA, Hiroshi

unread,
Oct 21, 2005, 1:02:17 AM10/21/05
to soa...@googlegroups.com, Sandra K Metz
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Hi,

Sandi Metz wrote:
> I've extended soap4r to allow the client of a doc/literal web service to
> send attachments when invoking the service. As a ruby AND soap newbie,
> I'd deeply appreciate feedback about the potential perils of the
> following code.
>
> I have tested these changes against the doc/literal service I'm using
> and they do seem to work; the attachments are received correctly on the
> server side.

Thank you. I'll look into the code and try to merge it till 1.5.6. And
also thanks to Emil.

Regards,
// NaHi
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.1 (Cygwin)

iD8DBQFDWHZXf6b33ts2dPkRAghgAJ9bu96u9Dyfc/rLvTIcIS998Dw6lACgzW9Y
gQmzRE1z5VrcIzFpsZdS7zI=
=5t/S
-----END PGP SIGNATURE-----

Emil Marceta

unread,
Oct 21, 2005, 2:44:40 AM10/21/05
to soa...@googlegroups.com
On 10/20/05, Sandi Metz <sand...@mindspring.com> wrote:

> I've extended soap4r to allow the client of a doc/literal web service to
> send attachments when invoking the service. As a ruby AND soap newbie, I'd
> deeply appreciate feedback about the potential perils of the following code.

I think this is good, you did the guts right,the EncodingStyle I was
thinking about
too.

Few minor things.

The dynamic argument passing may be simplified using splat operator
instead of eval.
Run this in your irb to see how it works : [1,2,3, *[4,5]] =>[1,2,3,4,5]

Also I'm not sure whether the attachment arguments should be declared in
add_document_method. The namespace is not relevant to the multipart
elements etc.

I'm thinking more along the lines that the multipart message should somehow
reflect the natural order of the parts. Parts may be ordered in array, and the
first element is soap body.

So it becomes something like: aDriver.submitGrant([grantXML, *soapAttachment])
(note the splat operator mentioned earlier)
Or (assume that the first parameter is named :grant form wsdl perhaps) :
aDriver.submitGrant([{ :grant => grantXML}, *soapAttachment])

If the response contains the attachments the similar rule applies. The array
is returned, where the first element is soap body, and the attachments follow.

If no attachments present, it remains the same as it is now.

Also, a quick note to NaHi, the wsdl2ruby appears needs to learn how to process
the SwA message wsdl declarations. See http://dev.ctor.org/soap4r/ticket/164
for more.

Cheers,
em

Sandi Metz

unread,
Oct 28, 2005, 12:40:31 PM10/28/05
to soa...@googlegroups.com
Emil,

Thanks for this feedback, and sorry about the silence on my end.  I am extremely appreciative of your help.

Having a fix for this problem makes a difference in getting Ruby adopted at my workplace, so this is hugely important for me.

I'll study your syntax suggestions and see if I can learn more about the Ruby way.

Thanks again,

Sandi

Emil Marceta

unread,
Oct 28, 2005, 3:27:58 PM10/28/05
to soa...@googlegroups.com
> Having a fix for this problem makes a difference in getting Ruby adopted at
> my workplace, so this is hugely important for me.

The issue is tracked as http://dev.ctor.org/soap4r/ticket/165
There is also another SwA related on http://dev.ctor.org/soap4r/ticket/164

Hopefully it will be addressed shortly.

Just of curiosity, what is the server SOAP/SwA toolkit you use?
If I remember correctly .NET supports DIME only, and will move to MTOM
directly, so I'm assuming it is not .NET.

The only SwA I've used was Java Apache Axis. What are you using?

Thanks,
emil

Sandi Metz

unread,
Nov 1, 2005, 3:56:29 PM11/1/05
to soa...@googlegroups.com
Yes.  The folks who wrote the service are using Apache Axis. 

They are trying to provide a generic service, written in open source software, that will work for clients written in anything, including .net.

I have sample code for their server, so I'm actually running it locally, though in real life I'll be invoking the service from their site.

I worry a bit about the DIME/MTOM issue, since it's all kind of a mystery to me.  BUT, I have hopes that the Ruby/soap4r solution is ok since I _am_ successfully sending attachments from Ruby to this Axis service.

Thanks for the update about the tickets.

Sandi

Sandi Metz

unread,
Nov 29, 2005, 4:24:58 PM11/29/05
to soa...@googlegroups.com
All,

I'm running soap4r 1.5.5 with http-access2 2.0.6 over Ruby 1.8.2 on a Windows platform. 

When I invoke a web service that returns 'chunked' output, my client side sits a while and then times out.  A bit farther down in this mail there's a wiredump of the timeout, with a few extra bits, showing the problem.

The timeout is caused by a failure on my client side.  HTTPAccess2::Session reads all of the available chunks, fails to realize that everything has been read, and then does one more read.  The server doesn't respond to this extra read and the client eventually times out.  I've fixed this problem locally by adding the code in green.

Can anyone give me some feedback on this fix?  It surprises me that I'm even having this problem and I'm worried that.... well, actually, I worry about everything.  :-)

Thanks in advance for your thoughts,
Sandi

********** FIX ***************
require 'http-access2'
module HTTPAccess2
  class Session
    alias orig_read_body_chunked read_body_chunked
    def read_body_chunked
      data = orig_read_body_chunked
       
        #If the last attempt didn't get any data, close the socket to prevent trying again.
        #After the socket is closed, eof? will return true and get_data will no longer
        #attempt another read.
      @socket.close if data.size == 0
      data
    end
  end
end


********** Wire dump of timeout***************
= Request

! CONNECT TO a.b.c:446
! CONNECTION ESTABLISHED
POST /app-s2s-server/services/ApplicantIntegrationSoapPort HTTP/1.1

SOAPAction: ""

Content-Type: text/xml; charset=utf-8

User-Agent: SOAP4R/1.5.5 (/114, ruby 1.8.2 (2004-12-25) [i386-mswin32])

Date: Tue Nov 29 15:58:01 Eastern Standard Time 2005

Content-Length: 489

Host: a.b.c:446

<?xml version="1.0" encoding="utf-8" ?>
<env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <env:Body>
    <GetOpportunityListRequest xmlns="http://apply.grants.gov/WebServices/ApplicantIntegrationServices-V1.0"><OpportunityID></OpportunityID><CFDANumber>00.000</CFDANumber><CompetitionID></CompetitionID></GetOpportunityListRequest>
  </env:Body>
</env:Envelope>

= Response

HTTP/1.1 200 OK

Date: Tue, 29 Nov 2005 20:58:04 GMT

Server: WebLogic Server 7.0 SP5 Wed Mar 31 23:12:50 PST 2004 363281 with CR194073 CR184612

Content-Type: text/xml; charset=utf-8

Transfer-Encoding: chunked


*** In get_data until eof? loop, eof? =
*** Start of read_body_chunked
0fe8
<?xml version="1.0" encoding="UTF-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
--SNIP boring details--

*** End of read_body_chunked before return data, data.size = 4072

*** In get_data until eof? loop, eof? =
*** Start of read_body_chunked
04cb
instructions.pdf</InstructionURL></OpportunityInformation><OpportunityInformation><OpportunityID>APP-S2S-TEST-RR</OpportunityID><OpportunityTitle>R&amp;R
--SNIP boring details--
</GetOpportunityListResponse></soapenv:Body></soapenv:Envelope>
*** End of read_body_chunked before return data, data.size = 1227

*** In get_data until eof? loop, eof? =
*** Start of read_body_chunked
0000
*** End of read_body_chunked before return data, data.size = 0

*** In get_data until eof? loop, eof? =
*** Start of read_body_chunked
C:/ruby/lib/ruby/1.8/timeout.rb:42:in `fill_rbuff': execution expired (Timeout::Error)
    from C:/ruby/lib/ruby/site_ruby/1.8/openssl/buffering.rb:75:in `gets'
    from C:/ruby/lib/ruby/site_ruby/1.8/http-access2.rb:1041:in `gets'
    from C:/ruby/lib/ruby/site_ruby/1.8/http-access2.rb:1554:in `read_body_chunked'
    from C:/ruby/lib/ruby/site_ruby/1.8/http-access2.rb:1508:in `read_body'
    from C:/ruby/lib/ruby/site_ruby/1.8/http-access2.rb:1304:in `get_data'
    from C:/ruby/lib/ruby/site_ruby/1.8/http-access2.rb:1303:in `timeout'
    from C:/ruby/lib/ruby/1.8/timeout.rb:55:in `timeout'
    from C:/ruby/lib/ruby/site_ruby/1.8/http-access2.rb:1303:in `get_data'
     ... 23 levels...

********** Wire dump after fix***************
= Request

! CONNECT TO a.b.c:446
! CONNECTION ESTABLISHED
POST /app-s2s-server/services/ApplicantIntegrationSoapPort HTTP/1.1

SOAPAction: ""

Content-Type: text/xml; charset=utf-8

User-Agent: SOAP4R/1.5.5 (/114, ruby 1.8.2 (2004-12-25) [i386-mswin32])

Date: Tue Nov 29 16:12:52 Eastern Standard Time 2005

Content-Length: 489

Host:
a.b.c:446

<?xml version="1.0" encoding="utf-8" ?>
<env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <env:Body>
    <GetOpportunityListRequest xmlns="http://apply.grants.gov/WebServices/ApplicantIntegrationServices-V1.0"><OpportunityID></OpportunityID><CFDANumber>00.000</CFDANumber><CompetitionID></CompetitionID></GetOpportunityListRequest>
  </env:Body>
</env:Envelope>

= Response

HTTP/1.1 200 OK

Date: Tue, 29 Nov 2005 21:12:55 GMT

Server: WebLogic Server 7.0 SP5 Wed Mar 31 23:12:50 PST 2004 363281 with CR194073 CR184612

Content-Type: text/xml; charset=utf-8

Transfer-Encoding: chunked

*** In get_data until eof? loop, eof? =
*** Start of read_body_chunked
0fe8
<?xml version="1.0" encoding="UTF-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><soapenv:Body><GetOpportunityListResponse
--SNIP boring details--
 *** End of read_body_chunked before return data, data.size = 4072

*** In get_data until eof? loop, eof? =
*** Start of read_body_chunked
04cb
--SNIP boring details--
</OpportunityInformation></GetOpportunityListResponse></soapenv:Body></soapenv:Envelope>
*** End of read_body_chunked before return data, data.size = 1227

*** In get_data until eof? loop, eof? =
*** Start of read_body_chunked
0000
*** End of read_body_chunked before return data, data.size = 0
! CONNECTION CLOSED



NAKAMURA, Hiroshi

unread,
Jul 17, 2006, 6:05:07 AM7/17/06
to soa...@googlegroups.com
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Hi,

Sorry for very late reply...

Sandi Metz wrote:
> I'm running soap4r 1.5.5 with http-access2 2.0.6 over Ruby 1.8.2 on a
> Windows platform.
>
> When I invoke a web service that returns 'chunked' output, my client
> side sits a while and then times out. A bit farther down in this mail
> there's a wiredump of the timeout, with a few extra bits, showing the
> problem.
>
> The timeout is caused by a failure on my client side.
> HTTPAccess2::Session reads all of the available chunks, fails to realize
> that everything has been read, and then does one more read. The server
> doesn't respond to this extra read and the client eventually times out.
> I've fixed this problem locally by adding the code in green.

Thank you for your explanation. Now I'm planning new http-access
release. I changed code about chunked size counting (patch is from
another person, not me).

If you're still using http-access2, would you please try the following
preview release at your leisure?
http://dev.ctor.org/download/http-access-20060717.tar.gz

I want to be sure that this release fixes the problem you encountered.

Regards,
// NaHi
-----BEGIN PGP SIGNATURE-----

Version: GnuPG v1.4.2.1 (Cygwin)

iQEVAwUBRLtg0h9L2jg5EEGlAQLJoggAg+iCUb2q4QPyMdN49hQDD8iTmfvDsoVz
do/72cSWfoQIq2sxqGdOU0FydDV8qvxf0igIVDckUAv841SzX787WOiICY634syq
JibF0Fju97wF3w1bGGLD51GXsQMFkeUdQ2t6nQgKWGTbkmlVLhWm9oIyXJ5dEKs6
d1/N//5/jR8Cktw+oedM9hdXOzJJqREDucELDGvLPqJZ6rq6VK9UNkM8cEJVTBdy
8/DAafIE/g+CPuxuGrySGd6w6Rs2MixiXKdfCG3dGHCQg0o+M+CmmpYVGQ900Tf2
j5iMpCNeGxs6Mc30SlLepJWquwyciti954p5sqXSw5ia4DBCOX0KlA==
=siTt
-----END PGP SIGNATURE-----

Reply all
Reply to author
Forward
0 new messages