The new signal pendingConnectionAvailable is added to QTcpServer

The new signal pendingConnnectionAvailable is emitted after a new
connection has been added to the pending connections queue. Connect
to this signal and call nextPendingConnection to handle incoming
connections.

The existing unchanged newConnection signal is emitted after the
overridable function incomingConnection is called, regardless of whether
a new connection is added to the pending connections queue in the
incomingConnection function or not.

If a subclass that overrides incomingConnection either decides to not
add all incoming connections to the pending connections queue, or to
postpone adding the connection until a handshake is successfully
completed, the pendingConnectionAvailable signal should be to used,
because this signal directly corresponds to insertions to the pending
connections queue.

[ChangeLog][QtNetwork][QTcpServer] New signal pendingConnectionAvailable
is emitted when a new connection is added

Task-number: QTBUG-100823
Change-Id: I00c76761389065f68271553e69e6c45c393a2fa8
Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
This commit is contained in:
Øystein Heskestad 2022-05-30 17:35:53 +02:00 committed by Mårten Nordheim
parent 8b26233573
commit 782fbe0f63
3 changed files with 95 additions and 6 deletions

View File

@ -18,7 +18,9 @@
Call listen() to have the server listen for incoming connections. Call listen() to have the server listen for incoming connections.
The newConnection() signal is then emitted each time a client The newConnection() signal is then emitted each time a client
connects to the server. connects to the server. When the client connection has been added
to the pending connection queue using the addPendingConnection()
function, the pendingConnectionAvailable() signal is emitted.
Call nextPendingConnection() to accept the pending connection as Call nextPendingConnection() to accept the pending connection as
a connected QTcpSocket. The function returns a pointer to a a connected QTcpSocket. The function returns a pointer to a
@ -47,11 +49,21 @@
/*! \fn void QTcpServer::newConnection() /*! \fn void QTcpServer::newConnection()
This signal is emitted every time a new connection is available. This signal is emitted every time a new connection is available, regardless
of whether it has been added to the pending connections queue or not.
\sa hasPendingConnections(), nextPendingConnection() \sa hasPendingConnections(), nextPendingConnection()
*/ */
/*! \fn void QTcpServer::pendingConnectionAvailable()
This signal is emitted every time a new connection has been added to the
pending connections queue.
\sa hasPendingConnections(), nextPendingConnection()
\since 6.4
*/
/*! \fn void QTcpServer::acceptError(QAbstractSocket::SocketError socketError) /*! \fn void QTcpServer::acceptError(QAbstractSocket::SocketError socketError)
\since 5.0 \since 5.0
@ -182,10 +194,12 @@ void QTcpServerPrivate::readNotification()
#if defined (QTCPSERVER_DEBUG) #if defined (QTCPSERVER_DEBUG)
qDebug("QTcpServerPrivate::_q_processIncomingConnection() accepted socket %i", descriptor); qDebug("QTcpServerPrivate::_q_processIncomingConnection() accepted socket %i", descriptor);
#endif #endif
QPointer<QTcpServer> that = q;
q->incomingConnection(descriptor); q->incomingConnection(descriptor);
QPointer<QTcpServer> that = q; if (that)
emit q->newConnection(); emit q->newConnection();
if (!that || !q->isListening()) if (!that || !q->isListening())
return; return;
} }
@ -572,14 +586,17 @@ void QTcpServer::incomingConnection(qintptr socketDescriptor)
\note Don't forget to call this member from reimplemented \note Don't forget to call this member from reimplemented
incomingConnection() if you do not want to break the incomingConnection() if you do not want to break the
Pending Connections mechanism. Pending Connections mechanism. This function emits the
pendingConnectionAvailable() signal after the socket has been
added.
\sa incomingConnection() \sa incomingConnection(), pendingConnectionAvailable()
\since 4.7 \since 4.7
*/ */
void QTcpServer::addPendingConnection(QTcpSocket* socket) void QTcpServer::addPendingConnection(QTcpSocket* socket)
{ {
d_func()->pendingConnections.append(socket); d_func()->pendingConnections.append(socket);
emit pendingConnectionAvailable(QPrivateSignal());
} }
/*! /*!

View File

@ -66,6 +66,7 @@ protected:
Q_SIGNALS: Q_SIGNALS:
void newConnection(); void newConnection();
void pendingConnectionAvailable(QPrivateSignal);
void acceptError(QAbstractSocket::SocketError socketError); void acceptError(QAbstractSocket::SocketError socketError);
private: private:

View File

@ -94,6 +94,9 @@ private slots:
void pauseAccepting(); void pauseAccepting();
void pendingConnectionAvailable_data();
void pendingConnectionAvailable();
private: private:
bool shouldSkipIpv6TestsForBrokenGetsockopt(); bool shouldSkipIpv6TestsForBrokenGetsockopt();
#ifdef SHOULD_CHECK_SYSCALL_SUPPORT #ifdef SHOULD_CHECK_SYSCALL_SUPPORT
@ -1058,5 +1061,73 @@ void tst_QTcpServer::pauseAccepting()
QCOMPARE(spy.count(), 6); QCOMPARE(spy.count(), 6);
} }
// Only adds the socket to the pending connections list after emitNextSocket is
// called. It's very artificial, but it allows us to test the behavior of
// the pendingConnectionAvailable signal when a server doesn't add the socket
// during the incomingConnection virtual function.
class DerivedServer : public QTcpServer
{
public:
explicit DerivedServer(QObject *parent = nullptr)
: QTcpServer(parent)
{
}
void emitNextSocket()
{
if (m_socketDescriptors.isEmpty())
return;
auto *socket = new QTcpSocket(this);
socket->setSocketDescriptor(m_socketDescriptors.back());
m_socketDescriptors.pop_back();
addPendingConnection(socket);
}
protected:
void incomingConnection(qintptr socketDescriptor) override
{
m_socketDescriptors.push_back(socketDescriptor);
}
private:
QList<qintptr> m_socketDescriptors;
};
void tst_QTcpServer::pendingConnectionAvailable_data()
{
QTest::addColumn<bool>("useDerivedServer");
QTest::newRow("QTcpServer") << false;
QTest::newRow("DerivedServer") << true;
}
void tst_QTcpServer::pendingConnectionAvailable()
{
QFETCH_GLOBAL(bool, setProxy);
if (setProxy)
QSKIP("This feature does not differentiate with or without proxy");
QFETCH(bool, useDerivedServer);
QTcpServer *server = useDerivedServer ? new DerivedServer : new QTcpServer;
if (!server->listen(QHostAddress::LocalHost, 0)) {
qWarning() << "Server failed to listen:" << server->errorString();
QSKIP("Server failed to listen");
}
QSignalSpy newConnectionSpy(server, &QTcpServer::newConnection);
QSignalSpy pendingConnectionSpy(server, &QTcpServer::pendingConnectionAvailable);
QTcpSocket socket;
socket.connectToHost(QHostAddress::LocalHost, server->serverPort());
QVERIFY(newConnectionSpy.wait());
QVERIFY(socket.waitForConnected());
QCOMPARE(socket.state(), QTcpSocket::ConnectedState);
int expectedPendingConnections = useDerivedServer ? 0 : 1;
QCOMPARE(pendingConnectionSpy.count(), expectedPendingConnections);
if (useDerivedServer)
static_cast<DerivedServer *>(server)->emitNextSocket();
QCOMPARE(pendingConnectionSpy.count(), 1);
}
QTEST_MAIN(tst_QTcpServer) QTEST_MAIN(tst_QTcpServer)
#include "tst_qtcpserver.moc" #include "tst_qtcpserver.moc"