KD SOAP API Documentation 2.1
Loading...
Searching...
No Matches
KDSoapClientInterface.cpp
Go to the documentation of this file.
1/****************************************************************************
2**
3** This file is part of the KD Soap project.
4**
5** SPDX-FileCopyrightText: 2010-2023 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
6**
7** SPDX-License-Identifier: MIT
8**
9****************************************************************************/
14#ifndef QT_NO_SSL
16#include "KDSoapSslHandler.h"
17#endif
18#include "KDSoapPendingCall_p.h"
19#include <QAuthenticator>
20#include <QBuffer>
21#include <QDebug>
22#include <QNetworkProxy>
23#include <QNetworkReply>
24#include <QNetworkRequest>
25#include <QSslConfiguration>
26#include <QTimer>
27
28KDSoapClientInterface::KDSoapClientInterface(const QString &endPoint, const QString &messageNamespace)
30{
32 d->m_messageNamespace = messageNamespace;
34}
35
42
47
52
54 : m_accessManager(nullptr)
55 , m_authentication()
56 , m_version(KDSoap::SOAP1_1)
57 , m_style(KDSoapClientInterface::RPCStyle)
58 , m_ignoreSslErrors(false)
59 , m_timeout(30 * 60 * 1000) // 30 minutes, as documented
60{
61#ifndef QT_NO_SSL
62 m_sslHandler = nullptr;
63#endif
64}
65
67{
68#ifndef QT_NO_SSL
69 delete m_sslHandler;
70#endif
71}
72
74{
75 if (!m_accessManager) {
77 connect(m_accessManager, &QNetworkAccessManager::authenticationRequired, this, &KDSoapClientInterfacePrivate::_kd_slotAuthenticationRequired);
78 }
79 return m_accessManager;
80}
81
83{
84 QNetworkRequest request(QUrl(this->m_endPoint));
85
86#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
87 // HTTP/2 (on by default since Qt 6) creates trouble, disable it for now (https://github.com/KDAB/KDSoap/issues/246)
89#endif
90
91 QString soapAction = action;
92
93 if (soapAction.isNull()) {
94 // The automatic generation of SoapAction done in this block is a mistake going back to KDSoap 1.0.
95 // The spec says "there is no default value for SoapAction" (https://www.w3.org/TR/wsdl#_soap:operation)
96 // but we keep this for compatibility, when nothing was passed as argument (see the webcalls unittest)
97 soapAction = this->m_messageNamespace;
98 if (!soapAction.endsWith(QLatin1Char('/'))) {
99 soapAction += QLatin1Char('/');
100 }
101 soapAction += method;
102 }
103 // qDebug() << "soapAction=" << soapAction;
104
105 QString soapHeader;
106 if (m_version == KDSoap::SOAP1_1) {
107 soapHeader += QString::fromLatin1("text/xml;charset=utf-8");
108 request.setRawHeader("SoapAction", '\"' + soapAction.toUtf8() + '\"');
109 } else if (m_version == KDSoap::SOAP1_2) {
110 soapHeader += QString::fromLatin1("application/soap+xml;charset=utf-8");
112 soapHeader += QString::fromLatin1(";action=") + soapAction;
113 }
114
116
117 // FIXME need to find out which version of Qt this is no longer necessary
118 // without that the server might respond with gzip compressed data and
119 // Qt 4.6.2 fails to decode that properly
120 //
121 // happens with retrieval calls in against SugarCRM 5.5.1 running on Apache 2.2.15
122 // when the response seems to reach a certain size threshold
123 request.setRawHeader("Accept-Encoding", "compress");
124
126 request.setRawHeader(it.key(), it.value());
127 }
128
129#ifndef QT_NO_SSL
130 if (!m_sslConfiguration.isNull()) {
132 }
133#endif
134
135 return request;
136}
137
138QBuffer *KDSoapClientInterfacePrivate::prepareRequestBuffer(const QString &method, const KDSoapMessage &message, const QString &soapAction, const KDSoapHeaders &headers)
139{
140 KDSoapMessageWriter msgWriter;
142 msgWriter.setVersion(m_version);
143 QBuffer *buffer = new QBuffer;
144 auto setBufferData = [=](const KDSoapMessage &msg) {
145 buffer->setData(msgWriter.messageToXml(msg,
147 headers, m_persistentHeaders,
149 };
150
152 KDSoapMessage messageCopy = message;
154 if (!prop.action().isEmpty())
155 qWarning("Overwriting the action addressing parameter (%s) with the SOAP action (%s)",
156 prop.action().toLocal8Bit().constData(), soapAction.toLocal8Bit().constData());
157 prop.setAction(soapAction);
158 messageCopy.setMessageAddressingProperties(prop);
159 setBufferData(messageCopy);
160 } else {
161 setBufferData(message);
162 }
163 buffer->open(QIODevice::ReadOnly);
164 return buffer;
165}
166
167KDSoapPendingCall KDSoapClientInterface::asyncCall(const QString &method, const KDSoapMessage &message, const QString &soapAction,
168 const KDSoapHeaders &headers)
169{
170 QBuffer *buffer = d->prepareRequestBuffer(method, message, soapAction, headers);
171 QNetworkRequest request = d->prepareRequest(method, soapAction);
172 QNetworkReply *reply = d->accessManager()->post(request, buffer);
173 d->setupReply(reply);
174 maybeDebugRequest(buffer->data(), reply->request(), reply);
175 KDSoapPendingCall call(reply, buffer);
176 call.d->soapVersion = d->m_version;
177 return call;
178}
179
180KDSoapMessage KDSoapClientInterface::call(const QString &method, const KDSoapMessage &message, const QString &soapAction,
181 const KDSoapHeaders &headers)
182{
183 d->accessManager()->cookieJar(); // create it in the right thread, the secondary thread will use it
184 // Problem is: I don't want a nested event loop here. Too dangerous for GUI programs.
185 // I wanted a socket->waitFor... but we don't have access to the actual socket in QNetworkAccess.
186 // So the only option that remains is a thread and acquiring a semaphore...
187 KDSoapThreadTaskData *task = new KDSoapThreadTaskData(this, method, message, soapAction, headers);
189 d->m_thread.enqueue(task);
190 if (!d->m_thread.isRunning()) {
191 d->m_thread.start();
192 }
193 task->waitForCompletion();
194 KDSoapMessage ret = task->response();
196 delete task;
197 return ret;
198}
199
201 const QString &soapAction, const KDSoapHeaders &headers)
202{
203 QBuffer *buffer = d->prepareRequestBuffer(method, message, soapAction, headers);
204 QNetworkRequest request = d->prepareRequest(method, soapAction);
205 QNetworkReply *reply = d->accessManager()->post(request, buffer);
206 d->setupReply(reply);
207 maybeDebugRequest(buffer->data(), reply->request(), reply);
210}
211
212void KDSoapClientInterfacePrivate::_kd_slotAuthenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator)
213{
214 m_authentication.handleAuthenticationRequired(reply, authenticator);
215}
216
218{
219 d->m_authentication = authentication;
220}
221
223{
224 return d->m_endPoint;
225}
226
228{
229 d->m_endPoint = endPoint;
230}
231
233{
234 d->m_persistentHeaders[name] = header;
235 d->m_persistentHeaders[name].setQualified(true);
236}
237
242
243#ifndef QT_NO_SSL
248#endif
249
250// Workaround for lack of connect-to-lambdas in Qt4
251// The pure Qt5 code could read like
252/*
253 QTimer *timeoutTimer = new QTimer(reply);
254 timeoutTimer->setSingleShot(true);
255 connect(timeoutTimer, &QTimer::timeout, reply, [reply]() { contents_of_the_slot });
256*/
257class TimeoutHandler : public QTimer // this way a single QObject is needed
258{
260public:
261 TimeoutHandler(QNetworkReply *reply)
262 : QTimer(reply)
263 {
264 setSingleShot(true);
265 }
266public Q_SLOTS:
267 void replyTimeout()
268 {
270 Q_ASSERT(reply);
271
272 // contents_of_the_slot:
273 reply->setProperty("kdsoap_reply_timed_out", true); // see KDSoapPendingCall.cpp
274 reply->abort();
275 }
276};
277
279{
280#ifndef QT_NO_SSL
281 if (m_ignoreSslErrors) {
283 } else {
285 if (m_sslHandler) {
286 // create a child object of the reply, which will forward to m_sslHandler.
287 // this is a workaround for the lack of the reply pointer in the signal,
288 // and sender() doesn't work for sync calls (from another thread) (SOAP-79/issue29)
290 }
291 }
292#endif
293 if (m_timeout >= 0) {
294 TimeoutHandler *timeoutHandler = new TimeoutHandler(reply);
295 connect(timeoutHandler, &TimeoutHandler::timeout, timeoutHandler, &TimeoutHandler::replyTimeout);
297 }
298}
299
304
309
314
319
321{
322 QObject *oldParent = jar->parent();
323 d->accessManager()->setCookieJar(jar);
324 jar->setParent(oldParent); // see comment in QNAM::setCookieJar...
325}
326
331
333{
334 return d->accessManager()->proxy();
335}
336
341
343{
344 return d->m_timeout;
345}
346
348{
349 d->m_timeout = msecs;
350}
351
356
358{
359 d->m_sendSoapActionInHttpHeader = sendInHttpHeader;
360}
361
366
368{
369 d->m_sendSoapActionInWsAddressingHeader = sendInWsAddressingHeader;
370}
371
372#ifndef QT_NO_OPENSSL
377
382
384{
385 if (!d->m_sslHandler) {
387 }
388 return d->m_sslHandler;
389}
390#endif
391
392#include "KDSoapClientInterface.moc"
393#include "moc_KDSoapClientInterface_p.cpp"
void maybeDebugRequest(const QByteArray &data, const QNetworkRequest &request, QNetworkReply *reply)
QNetworkAccessManager * m_accessManager
KDSoapClientInterface::Style m_style
QMap< QString, KDSoapMessage > m_persistentHeaders
void setupReply(QNetworkReply *reply)
QNetworkRequest prepareRequest(const QString &method, const QString &action)
QNetworkAccessManager * accessManager()
QMap< QByteArray, QByteArray > m_httpHeaders
QBuffer * prepareRequestBuffer(const QString &method, const KDSoapMessage &message, const QString &soapAction, const KDSoapHeaders &headers)
void setSoapVersion(KDSoapClientInterface::SoapVersion version)
void callNoReply(const QString &method, const KDSoapMessage &message, const QString &soapAction=QString(), const KDSoapHeaders &headers=KDSoapHeaders())
void setProxy(const QNetworkProxy &proxy)
QSslConfiguration sslConfiguration() const
KDSoapPendingCall asyncCall(const QString &method, const KDSoapMessage &message, const QString &soapAction=QString(), const KDSoapHeaders &headers=KDSoapHeaders())
KDSoapSslHandler * sslHandler() const
void setRawHTTPHeaders(const QMap< QByteArray, QByteArray > &headers)
QNetworkProxy proxy() const
bool sendSoapActionInHttpHeader() const
sendActionInHTTP_Header
bool sendSoapActionInWsAddressingHeader() const
sendSoapActionInWsAddressingHeader
void setCookieJar(QNetworkCookieJar *jar)
QNetworkCookieJar * cookieJar() const
KDSoapClientInterface(const QString &endPoint, const QString &messageNamespace)
void setSslConfiguration(const QSslConfiguration &config)
KDSoapHeaders lastResponseHeaders() const
@ RPCStyle
the method name is sent as an xml element wrapping the message parameters
void setEndPoint(const QString &endPoint)
KDSoapMessage call(const QString &method, const KDSoapMessage &message, const QString &soapAction=QString(), const KDSoapHeaders &headers=KDSoapHeaders())
void setSendSoapActionInHttpHeader(bool sendInHttpHeader)
setSendSoapActionInHttpHeader
void setSendSoapActionInWsAddressingHeader(bool sendInWsAddressingHeader)
setSendSoapActionInWsAddressingHeader
void setAuthentication(const KDSoapAuthentication &authentication)
void setHeader(const QString &name, const KDSoapMessage &header)
KDSoapClientInterface::SoapVersion soapVersion() const
void enqueue(KDSoapThreadTaskData *taskData)
QByteArray messageToXml(const KDSoapMessage &message, const QString &method, const KDSoapHeaders &headers, const QMap< QString, KDSoapMessage > &persistentHeaders, const KDSoapAuthentication &authentication=KDSoapAuthentication()) const
void setMessageNamespace(const QString &ns)
void setVersion(KDSoap::SoapVersion version)
void setMessageAddressingProperties(const KDSoapMessageAddressingProperties &map)
KDSoapMessageAddressingProperties messageAddressingProperties() const
A class for handling SSL errors during SOAP calls.
KDSoapAuthentication m_authentication
KDSoapMessage response() const
KDSoapHeaders responseHeaders() const
const QByteArray & data() const const
virtual bool open(QIODevice::OpenMode flags) override
void setData(const QByteArray &data)
const char * constData() const const
QMap::const_iterator constBegin() const const
QMap::const_iterator constEnd() const const
void authenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator)
QNetworkCookieJar * cookieJar() const const
QNetworkReply * post(const QNetworkRequest &request, QIODevice *data)
QNetworkProxy proxy() const const
void setCookieJar(QNetworkCookieJar *cookieJar)
void setProxy(const QNetworkProxy &proxy)
virtual void abort()=0
virtual void ignoreSslErrors()
void ignoreSslErrors(const QList< QSslError > &errors)
QNetworkRequest request() const const
void sslErrors(const QList< QSslError > &errors)
void setAttribute(QNetworkRequest::Attribute code, const QVariant &value)
void setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value)
void setRawHeader(const QByteArray &headerName, const QByteArray &headerValue)
void setSslConfiguration(const QSslConfiguration &config)
Q_OBJECTQ_OBJECT
Q_SLOTSQ_SLOTS
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void deleteLater()
QObject * parent() const const
T qobject_cast(QObject *object)
void setParent(QObject *parent)
bool setProperty(const char *name, const QVariant &value)
bool isNull() const const
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const const
QString fromLatin1(const char *str, int size)
bool isEmpty() const const
bool isNull() const const
QByteArray toLocal8Bit() const const
QByteArray toUtf8() const const
bool isRunning() const const
void start(QThread::Priority priority)
bool wait(QDeadlineTimer deadline)
void setSingleShot(bool singleShot)
void timeout()

© 2010-2023 Klarälvdalens Datakonsult AB (KDAB)
"The Qt, C++ and OpenGL Experts"
https://www.kdab.com/
https://www.kdab.com/development-resources/qt-tools/kd-soap/
Generated on Tue Dec 26 2023 00:00:25 for KD SOAP API Documentation by doxygen 1.9.8