spyne icon indicating copy to clipboard operation
spyne copied to clipboard

Wrong in_header name

Open DmitriyMoseev opened this issue 10 years ago • 7 comments

Hello! I run into a problem the other day. If I create soap service and use in_header in it, suds client fails. Here is a minimal example

client.py

from suds.client import Client
client = Client('http://localhost:8000/?wsdl', cache=None)

server.py

from spyne import rpc, ServiceBase, Unicode
from spyne.model.complex import ComplexModel
from spyne.model.complex import ComplexModelBase, ComplexModelMeta

class InHeader(ComplexModel):
    __namespace__ = 'namespace_a'

    token = Unicode

class Service(ServiceBase):
    namespace = 'namespace_b' # 

    __in_header__ = InHeader

    @rpc()
    def soapmethod(ctx):
        pass

if __name__ == '__main__':
    from wsgiref.simple_server import make_server
    from spyne import Application
    from spyne.protocol.soap import Soap11
    from spyne.server.wsgi import WsgiApplication

    application = Application([Service], 'namespace_c',
        in_protocol=Soap11(validator='lxml'),
        out_protocol=Soap11()
    )

    wsgi_application = WsgiApplication(application)

    server = make_server('127.0.0.1', 8000, wsgi_application)
    server.serve_forever()

When I run the client I get

$ python client.py
Traceback (most recent call last):
  File "req.py", line 2, in <module>
    client = Client('http://localhost:8000/?wsdl', cache=None)
  File "/home/moseev/work/env/local/lib/python2.7/site-packages/suds/client.py", line 112, in __init__
    self.wsdl = reader.open(url)
  File "/home/moseev/work/env/local/lib/python2.7/site-packages/suds/reader.py", line 152, in open
    d = self.fn(url, self.options)
  File "/home/moseev/work/env/local/lib/python2.7/site-packages/suds/wsdl.py", line 158, in __init__
    self.resolve()
  File "/home/moseev/work/env/local/lib/python2.7/site-packages/suds/wsdl.py", line 207, in resolve
    c.resolve(self)
  File "/home/moseev/work/env/local/lib/python2.7/site-packages/suds/wsdl.py", line 661, in resolve
    self.resolveheaders(definitions, op)
  File "/home/moseev/work/env/local/lib/python2.7/site-packages/suds/wsdl.py", line 725, in resolveheaders
    raise Exception, "message'%s', not-found" % mn
Exception: message's0:InHeader', not-found

Look at wsdl

<?xml version="1.0"?>
<wsdl:definitions xmlns:wsa="http://schemas.xmlsoap.org/ws/2003/03/addressing" xmlns:tns="namespace_c" xmlns:plink="http://schemas.xmlsoap.org/ws/2003/05/partner-link/" xmlns:xop="http://www.w3.org/2004/08/xop/include" xmlns:soap12env="http://www.w3.org/2003/05/soap-envelope/" xmlns:s0="namespace_a" xmlns:soap11env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap12enc="http://www.w3.org/2003/05/soap-encoding/" xmlns:soap11enc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" targetNamespace="namespace_c" name="Application">
  <wsdl:types>...</wsdl:types>
  <wsdl:message name="soapmethod">...</wsdl:message>
  <wsdl:message name="soapmethodResponse">...</wsdl:message>
  <wsdl:message name="InHeader">
    <wsdl:part name="InHeader" element="s0:InHeader"/>
  </wsdl:message>
  <wsdl:service name="Service">...</wsdl:service>
  <wsdl:portType name="Application">...</wsdl:portType>
  <wsdl:binding name="Application" type="tns:Application">
    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
    <wsdl:operation name="soapmethod">
      <soap:operation soapAction="soapmethod" style="document"/>
      <wsdl:input name="soapmethod">
        <soap:body use="literal"/>
        <soap:header use="literal" message="s0:InHeader" part="InHeader"/>
      </wsdl:input>
      <wsdl:output name="soapmethodResponse">...</wsdl:output>
    </wsdl:operation>
  </wsdl:binding>
</wsdl:definitions>

we have a message called InHeader here

<wsdl:message name="InHeader">
  <wsdl:part name="InHeader" element="s0:InHeader"/>
</wsdl:message>

but a definition of operation links to the message using wrong name "s0:InHeader"

<wsdl:operation name="soapmethod">
  <soap:operation soapAction="soapmethod" style="document"/>
  <wsdl:input name="soapmethod">
    <soap:body use="literal"/>
    <soap:header use="literal" message="s0:InHeader" part="InHeader"/>
  </wsdl:input>
  <wsdl:output name="soapmethodResponse">...</wsdl:output>
</wsdl:operation>

I am not sure. But it looks like a bug.

DmitriyMoseev avatar Jan 21 '15 07:01 DmitriyMoseev

Looks like a suds bug to me.

You have xmlns:s0="namespace_a" so Spyne seems to be doing the right thing here.

plq avatar Jan 21 '15 08:01 plq

Yes, there is a type InHeader in s0 namespace, but soapmethod does not link to it, it links to message which is defined in namespace tns.

DmitriyMoseev avatar Jan 21 '15 10:01 DmitriyMoseev

sorry but i don't get it. can you show what the correct wsdl document should look like?

plq avatar Jan 21 '15 19:01 plq

I think this would be right.

<wsdl:operation name="soapmethod">
  <soap:operation soapAction="soapmethod" style="document"/>
  <wsdl:input name="soapmethod">
    <soap:body use="literal"/>
    <soap:header use="literal" message="tns:InHeader" part="InHeader"/>
  </wsdl:input>
  <wsdl:output name="soapmethodResponse">...</wsdl:output>
</wsdl:operation>

I replaced message="s0:InHeader" with message="tns:InHeader"

DmitriyMoseev avatar Jan 22 '15 08:01 DmitriyMoseev

That'd be wrong, InHeader is not in tns it's in s0. If you need spyne to put InHeader to tns, you should put the object in "namespace_c".

I'm still thinking it's a suds bug.

plq avatar Jan 24 '15 00:01 plq

<?xml version="1.0"?>
<wsdl:definitions xmlns:wsa="http://schemas.xmlsoap.org/ws/2003/03/addressing" xmlns:tns="namespace_c" xmlns:plink="http://schemas.xmlsoap.org/ws/2003/05/partner-link/" xmlns:xop="http://www.w3.org/2004/08/xop/include" xmlns:soap12env="http://www.w3.org/2003/05/soap-envelope/" xmlns:s0="namespace_a" xmlns:soap11env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap12enc="http://www.w3.org/2003/05/soap-encoding/" xmlns:soap11enc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" targetNamespace="namespace_c" name="Application">
  <wsdl:types>
    <xs:schema targetNamespace="namespace_a" elementFormDefault="qualified">
      <xs:complexType name="InHeader">
        <xs:sequence>
          <xs:element name="token" type="xs:string" minOccurs="0" nillable="true"/>
        </xs:sequence>
      </xs:complexType>
      <xs:element name="InHeader" type="s0:InHeader"/>
    </xs:schema>
    <xs:schema targetNamespace="namespace_c" elementFormDefault="qualified">...</xs:schema>
  </wsdl:types>
  <wsdl:message name="soapmethod">...</wsdl:message>
  <wsdl:message name="soapmethodResponse">
      <wsdl:part name="soapmethodResponse" element="tns:soapmethodResponse"/>
  </wsdl:message>
  <wsdl:message name="InHeader">
      <wsdl:part name="InHeader" element="s0:InHeader"/>
  </wsdl:message>
  <wsdl:service name="Service">...</wsdl:service>
  <wsdl:portType name="Application">
    <wsdl:operation name="soapmethod" parameterOrder="soapmethod">
      <wsdl:input name="soapmethod" message="tns:soapmethod"/>
      <wsdl:output name="soapmethodResponse" message="tns:soapmethodResponse"/>
    </wsdl:operation>
  </wsdl:portType>
  <wsdl:binding name="Application" type="tns:Application">
    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
    <wsdl:operation name="soapmethod">
      <soap:operation soapAction="soapmethod" style="document"/>
      <wsdl:input name="soapmethod">
        <soap:body use="literal"/>
        <soap:header use="literal" message="s0:InHeader" part="InHeader"/>
      </wsdl:input>
      <wsdl:output name="soapmethodResponse">...</wsdl:output>
    </wsdl:operation>
  </wsdl:binding>
</wsdl:definitions>

Look at wsdl once again please. I have some clues why it has to be message="tns:InHeader" instead of message="s0:InHeader"

We have three objects called InHeader. One of them is type. And the second is element. Both of them are defined inside wsdl:types and they are in namespace s0 (namespace_a):

<wsdl:types>
  <xs:schema targetNamespace="namespace_a" elementFormDefault="qualified">
    <xs:complexType name="InHeader">
      <xs:sequence>
        <xs:element name="token" type="xs:string" minOccurs="0" nillable="true"/>
      </xs:sequence>
    </xs:complexType>
    <xs:element name="InHeader" type="s0:InHeader"/>
  </xs:schema>
  <xs:schema targetNamespace="namespace_c" elementFormDefault="qualified">...</xs:schema>
</wsdl:types>

The third one is message.

<wsdl:message name="InHeader">
  <wsdl:part name="InHeader" element="s0:InHeader"/>
</wsdl:message>

Let's compare the message InHeader with the message soapmethodResponse.

<wsdl:message name="soapmethodResponse">
  <wsdl:part name="soapmethodResponse" element="tns:soapmethodResponse"/>
</wsdl:message>
<wsdl:message name="InHeader">
  <wsdl:part name="InHeader" element="s0:InHeader"/>
</wsdl:message>

They look pretty similar to each other and they are defined right next to each other, so I believe they are in the same namespace. But we mention one of them using message="tns:soapmethodResponse" here

<wsdl:operation name="soapmethod" parameterOrder="soapmethod">
  <wsdl:input name="soapmethod" message="tns:soapmethod"/>
  <wsdl:output name="soapmethodResponse" message="tns:soapmethodResponse"/>
</wsdl:operation>

and we mention another one by message="s0:InHeader" here

<wsdl:input name="soapmethod">
  <soap:body use="literal"/>
  <soap:header use="literal" message="s0:InHeader" part="InHeader"/>
</wsdl:input>

And it seems very suspicious to me. Why are these two messages in different namespaces?

DmitriyMoseev avatar Jan 26 '15 09:01 DmitriyMoseev

Hello. I've encountered the issue with soap header. I'm unable to put my header as the direct child of soapenv:Header with specified namespace. In order to WSDL be correct and operational, I must put my header in the same namespace as my main service. If I need a header in another namespace, the generated WSDL creates this: <wsdl:message name="MyMethodInHeaderMsg> <wsdl:part name="MyHeader" element="s0:MyHeader" /> </wsdl:message> ... <soap:header use="literal" message="s0:MyMethodInHeaderMsg part="MyHeader">

and it is not linkable. I've found the source file "spyne/interface/wsd/wsdl11.py" and found a function responsible for generating that wrong message link in soap:header. (currently it is on line 398 of wsdl11.py): soap_header.set('message', '%s:%s' % (header.get_namespace_prefix(self.interface), in_header_message_name)) I think, that function header.get_namespace_prefix(self.interface) returns a prefix for a header, but we need a prefix for a message that is linkable to the main WSDL. So, I've tried to change that function to simply a variable "pref_tns", which is defined as "self.interface.get_namespace_prefix(self.interface.get_tns())"

and solution works! Now the "soap:header" elements of wsdl:input have a correct "tns:" prefix. The same should be done in out_header section, which is defined on line 431 My working solution looks like this: soap_header.set('message', '%s:%s' % (pref_tns, in_header_message_name)) and soap_header.set('message', '%s:%s' % (pref_tns, out_header_message_name))

It is my first message in github, and I don't know many things, so I'm asking for those who are able to fix the issue and investigate if my solution will not break something in spyne - to do this.

Best regards

fortor48 avatar Jul 28 '19 16:07 fortor48