Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

kdwsdl2cpp doesn't generate necessary headers #254

Open
Javierd opened this issue Nov 22, 2022 · 9 comments
Open

kdwsdl2cpp doesn't generate necessary headers #254

Javierd opened this issue Nov 22, 2022 · 9 comments

Comments

@Javierd
Copy link

Javierd commented Nov 22, 2022

Hello everyone,
I'm working on a project in which I need to use KDSoap to consume a SOAP service. The service is given by this wsdl file.
I have generate the .h and .cpp files and they seem to work fine with most of the methods, but there's one specific (though critical) method which doesn't work.
The method itself is the one named PullMessages. The problem is that the services returns an HTTP 400 respose to the call, and after spending some time with SoapUI and wireshark to check the differences, I have managed to found them. The only thing which prevents the method from working is that KDSoap isn't including any SOAP header, and it needs to include the endpoint url on a header named To, which uses "http://schemas.xmlsoap.org/ws/2004/08/addressing" as a namespace.
To make it clearer, the message currently sent is:

<soap:Envelope
    xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
    xmlns:soap-enc="http://www.w3.org/2003/05/soap-encoding"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:n1="http://www.onvif.org/ver10/events/wsdl">
    <soap:Header>
    <soap:Body>
        <n1:PullMessages>
            <n1:Timeout>
                PT00H00M10S
                </n1:Timeout>
            <n1:MessageLimit>
                1
                </n1:MessageLimit>
            </n1:PullMessages>
        </soap:Body>
    </soap:Envelope>

while it should be

<soap:Envelope
    xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
    xmlns:soap-enc="http://www.w3.org/2003/05/soap-encoding"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:n1="http://www.onvif.org/ver10/events/wsdl"
    xmlns:wsa5="http://schemas.xmlsoap.org/ws/2004/08/addressing">
    <soap:Header>
        <wsa5:To
                SOAP-ENV:mustUnderstand="true">
                http://example.com/onvif/event_service?idx=4
                </wsa5:To>
        </soap:Header>
    <soap:Body>
        <n1:PullMessages>
            <n1:Timeout>
                PT00H00M10S
                </n1:Timeout>
            <n1:MessageLimit>
                1
                </n1:MessageLimit>
            </n1:PullMessages>
        </soap:Body>
    </soap:Envelope>

Note that on the message that should be used there's a new namespace.

I don't really know why that namespace and header isn't automatically added, as it's mandatory. However, I've also tried to add it manually by adding a header to the KDSoapClientInterface:

KDSoapMessage header;
header.setNamespaceUri("http://schemas.xmlsoap.org/ws/2004/08/addressing");
QXmlStreamNamespaceDeclaration nsDeclaration("wsa5", "http://schemas.xmlsoap.org/ws/2004/08/addressing");
header.addNamespaceDeclaration(nsDeclaration);
header.setQualified(true);

header.addArgument("To", url.toString());
bindingService.clientInterface()->setHeader("url", header);

In this case, the header is added, but the specified namespace is not used, instead the header is added as a tag "ns1:To". Is this a bug? Or am I doing something wrong? I've been using the documentation and this seems to be the right way to do this, but it isn't working.

Any help would be appreciated.

Thanks a lot.

@dfaure-kdab
Copy link
Member

The API to use is rather something along the lines of

  KDSoapMessageAddressingProperties addressing;
  addressing.setDestination("http://example.com/onvif/event_service?idx=4");
  message.setMessageAddressingProperties(addressing);

but the question is indeed why kdwsdl2cpp doesn't generate that.

Maybe bindingService.clientInterface()->setSendSoapActionInWsAddressingHeader(true); is enough to fix it?

It sounds like WS/Addressing support isn't fully integrated with kdwsdl2cpp indeed, please let me know if this helps, then we can take a look at improving this integration. The first step will be to extend unittests/ws_addressing_support ...

@Javierd
Copy link
Author

Javierd commented Dec 2, 2022

Hi,
sorry for the delay on the answer, but I lost access to the service and couldn't test your suggestions.
I just tested adding bindingService.clientInterface()->setSendSoapActionInWsAddressingHeader(true); but it is not working yet.
By checking the message sent using wireshark I can see that this adds a new evement to the header:

<wsa:Action>
    http://www.onvif.org/ver10/events/wsdl/PullPointSubscription/PullMessagesRequest
    </wsa:Action>

However, that's not the element I needed to add.

So, maybe I need to implement a manual call using the API directly instead of using the automatically generated files? Or is there any other solution?

Thanks a lot

dfaure-kdab added a commit that referenced this issue Dec 3, 2022
so that WS-Addressing support can be used with WSDL-generated services

Fixes #254
@dfaure-kdab
Copy link
Member

Please update to latest master, and do

  KDSoapMessageAddressingProperties addressing;
  addressing.setDestination("http://example.com/onvif/event_service?idx=4");
  bindingService.clientInterface()->setMessageAddressingProperties(addressing);

I suppose that the WSDL files contain all the information necessary for kdwsdl2cpp to generate the message addressing properties automatically, but I don't know enough about ws-addressing to implement that (someone else implemented the current support for WS-addressing). Meanwhile, this new method at least allows to set WS-addressing properties manually on WSDL-generated messages.

Please let me know how this works for you.

@Javierd
Copy link
Author

Javierd commented Dec 5, 2022

I've been trying to use the method you propose, but although I've updated to master and build new headers and sources files which contains that method, it does nothing. The sent soap header is empty, there is no addressing information added to it

@dfaure-kdab
Copy link
Member

That is surprising, given that the unittest I wrote as part of that commit, shows that the addressing information is sent correctly. There must be a difference somewhere.

Ah, I found it, after writing a testcase based on the onvif WSDL. The code required a SOAP action to be set in order to write addressing properties. I removed that check now, I hope that's correct (no time to read the WS-Addressing spec).

Please git pull and try again.

@Javierd
Copy link
Author

Javierd commented Dec 5, 2022

It's working now! Thanks a lot!
By the way, this service may return an error when some of the request's parameters are not right. When using gsoap, a class with the error information was available by calling response.GetFaultObject().
Is there any way of getting that same information using KDSoap? Even if I have to parse it myself from the XML. As far as I know, the only way of getting some information is by calling the lastError and lastFault methods of the service, but there is no way of getting the complete fault error, am i wrong?

@dfaure-kdab
Copy link
Member

If you use the blocking API (bindingService.pullMessages(...)), then indeed you're limited to lastError/lastFault.
If you use the job API instead -- which is recommended anyhow in the GUI thread, then you can get the full response message.

Something like:

  TEV__PullMessages in;
  // ...
  auto job = new PullMessagesJob(&bindingService);
  job->setParameters(in);
  QObject::connect(job, &KDSoapJob::finished, nullptr, [job]() {
    if (job->reply().isFault()) {
      qDebug() << job->reply().arguments();
    } else {
      qDebug() << "success";
      // use job->resultParameters();
    }
  });
  job->start();

@dfaure-kdab
Copy link
Member

I suppose we could add a method KDSoapMessage lastReply(); to KDSoapClientInterface though, much like lastResponseHeaders(), for the case of blocking calls.

@Javierd
Copy link
Author

Javierd commented Dec 6, 2022

Ok, so I'll use jobs for now, but u think it would be useful to have the lastReply() method you mentioned
Thanks a lot for all your help!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants