Wrong in_header name
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.
Looks like a suds bug to me.
You have xmlns:s0="namespace_a" so Spyne seems to be doing the right thing here.
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.
sorry but i don't get it. can you show what the correct wsdl document should look like?
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"
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.
<?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?
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