Ticket #233 (closed defect: fixed)

Opened 5 years ago

Last modified 5 years ago

custom soapheaders passed as suds.sax.element.Element are modified

Reported by: hstock Owned by: jortel
Priority: major Milestone: 0.3.7
Component: marshalling (XML/SOAP output) Version: 0.3.6
Keywords: Cc:
Blocked By: Blocking:

Description

I have a Webservice where I have to pass a soap header with a type that is defined in a namespace not contained in the WSDL.

So I created a suds.sax.element.Element object with a custom prefix and namespace specified and added that to the soapheaders option.

The first call works fine. However the second call fails:

WebFault: Server raised fault: 'org.xml.sax.SAXParseException: The prefix "ns1" for element "ns1:sessionID" is not bound.'

The message that caused the error:

<SOAP-ENV:Envelope xmlns:ns0="http://ws.polarion.com/TrackerWebService-impl"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
   <SOAP-ENV:Header>
      <ns1:sessionID>-3225748651892946753</ns1:sessionID>
   </SOAP-ENV:Header>
...

The "healthy message":

<SOAP-ENV:Envelope xmlns:ns0="http://ws.polarion.com/TrackerWebService-impl"
xmlns:ns1="http://ws.polarion.com/session"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
   <SOAP-ENV:Header>
      <ns1:sessionID>-3225748651892946753</ns1:sessionID>
   </SOAP-ENV:Header>
...

Examination with a debugger showed that when the Element from the soapheaders array is first used, it gets modified by putting it into the document tree. The namespace declaration for ns1 gets "pushed up" and removed from the Element.

When further calls reuse the soapheaders, the namespace is missing from the Element.

I have attached a patch that makes a deepcopy of the Element before using it in the soap message. This fixes the issue.

I also think this would be the right behaviour since one does not expect objects passed as options to be changed.

Attachments

copy-element-header.patch (1.0 KB) - added by hstock 5 years ago.
make deep copy of "Element" soap headers
TrackerWebService.wsdl (140.2 KB) - added by hstock 5 years ago.
WSDL of affected service

Change History

Changed 5 years ago by hstock

make deep copy of "Element" soap headers

comment:1 Changed 5 years ago by jortel

  • Milestone set to 0.3.6
  • Status changed from new to assigned

Although suds makes provision for passing sax.Element to method parameters and soapheaders, it really not recommended and makes things harder then they need to be for the user. Instead of creating the XML fragment yourself and passing it, pass a python object instead and let suds do the work.

For simple types (string, int, etc) you can simply do:

client.set_options(soapheaders='string')

Or

client.set_options(soapheaders=['string', 10])

For complex types, the object is created using the factory and passed as above.

I do agree though that when sax.Element is passed in as a param or header it should not be changed. Your patch will copy every object that is passed which is not necessary (since they almost always suds.object) and expensive. But, I will update the marshaller.ElementAppender to deep copy the element before it is appended to the message. I do appreciate you patch though :)

comment:2 Changed 5 years ago by jortel

  • Milestone changed from 0.3.6 to 0.3.7

comment:3 Changed 5 years ago by jortel

  • Status changed from assigned to closed
  • Resolution set to fixed

Fixed r530 & r531.

Copy in the ElementAppender which should copy Element content when passed as either parameters or soapheaders.

comment:4 follow-up: ↓ 5 Changed 5 years ago by hstock

I still have the same problem with your fixes. The Element header doesn't seem to pass the marshaller.

Are you sure the code in my patch would be executed for every header? Are suds objects descendants of Element?

I need to pass an Element because I need a special namespace declaration on it which is not in the WSDL. (Even though it is just string content.)

Is there a better way of registering a namespace and setting it for the header element?

Changed 5 years ago by hstock

WSDL of affected service

comment:5 in reply to: ↑ 4 Changed 5 years ago by jortel

  • Status changed from closed to reopened
  • Resolution fixed deleted

Replying to hstock:

I still have the same problem with your fixes. The Element header doesn't seem to pass the marshaller.

Yes. You're correct. My mistake :)

Are you sure the code in my patch would be executed for every header? Are suds objects descendants of Element?

Nope, I was wrong about this too. Your patch is dead on and applied r533.

Sorry, I processed this ticket too quickly and didn't test it properly. Thanks for keeping on me about this. Please close the ticket after you've tried r533.

I need to pass an Element because I need a special namespace declaration on it which is not in the WSDL. (Even though it is just string content.)

Unfortunately, some services require a soap header and don't define them in the WSDL which precludes you from using the intended (easy) mechanism in suds for passing soap headers. You're doing this the only way you can.

Is there a better way of registering a namespace and setting it for the header element?

There are other ways but this is the best for what you're trying to do.

comment:6 Changed 5 years ago by jortel

I also fixed ticket #232 in r533 so you can pass the SessionID soap header as:

sid = Element('SessionId', ns=('ssn','http://ws.polarion.com/session')).setText('123')
client.set_options(soapheaders=sid)

For single headers.

comment:7 Changed 5 years ago by hstock

  • Status changed from reopened to closed
  • Resolution set to fixed

Both issues are now resolved. Thanks!

Note: See TracTickets for help on using tickets.