Skip to content

Commit

Permalink
Fix mixing up raw-xml requests in the same server object
Browse files Browse the repository at this point in the history
Create a server object for each incoming connection.

Fixes #288
  • Loading branch information
dfaure-kdab committed Aug 21, 2024
1 parent b38ef53 commit f89999e
Show file tree
Hide file tree
Showing 6 changed files with 26 additions and 24 deletions.
3 changes: 2 additions & 1 deletion docs/CHANGES_2_3.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ Client-side:

Server-side:
============
*
* To avoid mixing up raw-xml requests in the same server object (#288), KDSoap now creates a server object for each incoming connection.
Make sure your server object is ready to be created multiple times (this was already a requirement when enabling multi-threading with setThreadPool()).
2 changes: 2 additions & 0 deletions src/KDSoapServer/KDSoapServerSocket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ KDSoapServerSocket::~KDSoapServerSocket()
{
// same as m_owner->socketDeleted, but safe in case m_owner is deleted first
emit socketDeleted(this);

delete m_serverObject;
}

typedef QMap<QByteArray, QByteArray> HeadersMap;
Expand Down
5 changes: 1 addition & 4 deletions src/KDSoapServer/KDSoapSocketList.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,18 @@

KDSoapSocketList::KDSoapSocketList(KDSoapServer *server)
: m_server(server)
, m_serverObject(server->createServerObject())
, m_totalConnectionCount(0)
{
Q_ASSERT(m_server);
Q_ASSERT(m_serverObject);
}

KDSoapSocketList::~KDSoapSocketList()
{
delete m_serverObject;
}

KDSoapServerSocket *KDSoapSocketList::handleIncomingConnection(int socketDescriptor)
{
KDSoapServerSocket *socket = new KDSoapServerSocket(this, m_serverObject);
KDSoapServerSocket *socket = new KDSoapServerSocket(this, m_server->createServerObject());
socket->setSocketDescriptor(socketDescriptor);

#ifndef QT_NO_SSL
Expand Down
1 change: 0 additions & 1 deletion src/KDSoapServer/KDSoapSocketList_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ public Q_SLOTS:

private:
KDSoapServer *m_server;
QObject *m_serverObject;
QSet<KDSoapServerSocket *> m_sockets;
QAtomicInt m_totalConnectionCount;
};
Expand Down
30 changes: 16 additions & 14 deletions unittests/serverlib/test_serverlib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ Q_DECLARE_METATYPE(QFile::Permissions)
static const char *myWsdlNamespace = "http://www.kdab.com/xml/MyWsdl/";

class CountryServerObject;
typedef QMap<QThread *, CountryServerObject *> ServerObjectsMap;
ServerObjectsMap s_serverObjects;
typedef QList<CountryServerObject *> ServerObjectsList;
ServerObjectsList s_serverObjects;
QMutex s_serverObjectsMutex;

class PublicThread : public QThread
Expand Down Expand Up @@ -88,13 +88,13 @@ class CountryServerObject : public QObject,
{
// qDebug() << "Server object created in thread" << QThread::currentThread();
QMutexLocker locker(&s_serverObjectsMutex);
s_serverObjects.insert(QThread::currentThread(), this);
s_serverObjects.append(this);
}
~CountryServerObject()
{
QMutexLocker locker(&s_serverObjectsMutex);
Q_ASSERT(s_serverObjects.value(QThread::currentThread()) == this);
s_serverObjects.remove(QThread::currentThread());
Q_ASSERT(s_serverObjects.contains(this));
s_serverObjects.removeOne(this);
}

virtual void processRequest(const KDSoapMessage &request, KDSoapMessage &response, const QByteArray &soapAction) override;
Expand Down Expand Up @@ -189,7 +189,7 @@ class CountryServerObject : public QObject,
{
// Should be called in same thread as constructor
s_serverObjectsMutex.lock();
Q_ASSERT(s_serverObjects.value(QThread::currentThread()) == this);
Q_ASSERT(s_serverObjects.contains(this));
s_serverObjectsMutex.unlock();
if (employeeName.isEmpty()) {
setFault(QLatin1String("Client.Data"), QLatin1String("Empty employee name"), QLatin1String("CountryServerObject"),
Expand Down Expand Up @@ -403,7 +403,7 @@ private Q_SLOTS:
QCOMPARE(response.childValues().first().value().toString(), expectedCountry());

QCOMPARE(s_serverObjects.count(), 1);
QVERIFY(s_serverObjects.value(&serverThread)); // request handled by server thread itself (no thread pool)
QCOMPARE(s_serverObjects.at(0)->thread(), &serverThread); // request handled by server thread itself (no thread pool)
QCOMPARE(server->totalConnectionCount(), 1);
delete client;
QTest::qWait(100);
Expand Down Expand Up @@ -533,7 +533,7 @@ private Q_SLOTS:
const KDSoapMessage response = client->call(QLatin1String("getEmployeeCountry"), countryMessage());
QCOMPARE(response.childValues().first().value().toString(), expectedCountry());
QCOMPARE(s_serverObjects.count(), 1);
QThread *thread = s_serverObjects.begin().key();
QThread *thread = s_serverObjects.at(0)->thread();
QVERIFY(thread != qApp->thread());
QVERIFY(thread != &serverThread);
QCOMPARE(server->totalConnectionCount(), 1);
Expand All @@ -547,7 +547,7 @@ private Q_SLOTS:
QTest::addColumn<int>("maxThreads");
QTest::addColumn<int>("numRequests");
QTest::addColumn<int>("numClients");
QTest::addColumn<int>("expectedServerObjects");
QTest::addColumn<int>("expectedThreads");

// QNetworkAccessManager only does 6 concurrent http requests
// (QHttpNetworkConnectionPrivate::defaultHttpChannelCount = 6)
Expand All @@ -564,7 +564,7 @@ private Q_SLOTS:
QFETCH(int, maxThreads);
QFETCH(int, numRequests);
QFETCH(int, numClients);
QFETCH(int, expectedServerObjects);
QFETCH(int, expectedThreads);
{
KDSoapThreadPool threadPool;
threadPool.setMaxThreadCount(maxThreads);
Expand All @@ -586,13 +586,15 @@ private Q_SLOTS:
for (const KDSoapMessage &response : std::as_const(m_returnMessages)) {
QCOMPARE(response.childValues().first().value().toString(), expectedCountry());
}
QCOMPARE(s_serverObjects.count(), expectedServerObjects);
QMapIterator<QThread *, CountryServerObject *> it(s_serverObjects);
while (it.hasNext()) {
QThread *thread = it.next().key();
QCOMPARE(s_serverObjects.count(), numRequests);
QSet<QThread *> usedThreads;
for (CountryServerObject *obj : qAsConst(s_serverObjects)) {
QThread *thread = obj->thread();
QVERIFY(thread != qApp->thread());
QVERIFY(thread != &serverThread);
usedThreads.insert(thread);
}
QCOMPARE(usedThreads.count(), expectedThreads);
}
QCOMPARE(server->totalConnectionCount(), numClients * numRequests);
}
Expand Down
9 changes: 5 additions & 4 deletions unittests/wsdl_document/test_wsdl_document.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1237,6 +1237,7 @@ void WsdlDocumentTest::testDisconnectDuringDelayedCall()
{
TestServerThread<DocServer> serverThread;
DocServer *server = serverThread.startThread();
QPointer<DocServerObject> serverObject;
{
MyWsdlDocument service;
service.setEndPoint(server->endPoint());
Expand All @@ -1246,14 +1247,14 @@ void WsdlDocumentTest::testDisconnectDuringDelayedCall()
job->start();
// Wait until the server method is called
QTRY_VERIFY(server->lastServerObject());
QTRY_COMPARE(server->lastServerObject()->m_lastMethodCalled, QString::fromLatin1("delayedAddEmployee"));
serverObject = server->lastServerObject();
QTRY_COMPARE(serverObject->m_lastMethodCalled, QString::fromLatin1("delayedAddEmployee"));

delete job;
} // Disconnect the client

// Let the server continue

QTRY_COMPARE(server->lastServerObject()->m_lastMethodCalled, QString::fromLatin1("slotDelayedResponse"));
// Now that there's one server object per socket (#289), the server object gets deleted before the delayed response
QTRY_VERIFY(serverObject.isNull());
}

// Same as testSequenceInResponse (thomas-bayer.wsdl), but as a server test, by calling DocServer on a different path
Expand Down

0 comments on commit f89999e

Please sign in to comment.