48f25caac4
QTcpServer::addPendingConnection() is protected, so it is possible to add pending connections from a class derived from QTcpServer. This derived class can get a QTcpSocket from somewhere else, in which case d->socketEngine will not be set (or used). Given that it is possible to add pending connections in this scenario, it would make sense to be able to retrieve them as well. [ChangeLog][QtNetwork][QTcpServer] Permit using a QTcpServer with externally created QTcpSocket. Task-number: QTBUG-51288 Change-Id: I830c10f1a881e2bca4e4ad716d8be865e1c27a9f Reviewed-by: Lars Knoll <lars.knoll@theqtcompany.com>
1014 lines
32 KiB
C++
1014 lines
32 KiB
C++
/****************************************************************************
|
|
**
|
|
** Copyright (C) 2016 The Qt Company Ltd.
|
|
** Contact: https://www.qt.io/licensing/
|
|
**
|
|
** This file is part of the test suite of the Qt Toolkit.
|
|
**
|
|
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
|
|
** Commercial License Usage
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
** accordance with the commercial license agreement provided with the
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
** a written agreement between you and The Qt Company. For licensing terms
|
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
|
** information use the contact form at https://www.qt.io/contact-us.
|
|
**
|
|
** GNU General Public License Usage
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
** General Public License version 3 as published by the Free Software
|
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
|
** included in the packaging of this file. Please review the following
|
|
** information to ensure the GNU General Public License requirements will
|
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
|
**
|
|
** $QT_END_LICENSE$
|
|
**
|
|
****************************************************************************/
|
|
|
|
#include <qglobal.h>
|
|
// To prevent windows system header files from re-defining min/max
|
|
#define NOMINMAX 1
|
|
#if defined(_WIN32)
|
|
#include <winsock2.h>
|
|
#else
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#define SOCKET int
|
|
#define INVALID_SOCKET -1
|
|
#endif
|
|
|
|
#include <QtTest/QtTest>
|
|
|
|
#ifndef Q_OS_WIN
|
|
#include <unistd.h>
|
|
#include <sys/ioctl.h>
|
|
#endif
|
|
|
|
#include <qcoreapplication.h>
|
|
#include <qtcpsocket.h>
|
|
#include <qtcpserver.h>
|
|
#include <qhostaddress.h>
|
|
#include <qprocess.h>
|
|
#include <qstringlist.h>
|
|
#include <qplatformdefs.h>
|
|
#include <qhostinfo.h>
|
|
|
|
#include <QNetworkProxy>
|
|
|
|
#include <QNetworkSession>
|
|
#include <QNetworkConfiguration>
|
|
#include <QNetworkConfigurationManager>
|
|
#include "../../../network-settings.h"
|
|
|
|
class tst_QTcpServer : public QObject
|
|
{
|
|
Q_OBJECT
|
|
|
|
private slots:
|
|
void initTestCase_data();
|
|
void initTestCase();
|
|
void init();
|
|
void cleanup();
|
|
void getSetCheck();
|
|
void constructing();
|
|
void clientServerLoop();
|
|
void ipv6Server();
|
|
void dualStack_data();
|
|
void dualStack();
|
|
void ipv6ServerMapped();
|
|
void crashTests();
|
|
void maxPendingConnections();
|
|
void listenError();
|
|
void waitForConnectionTest();
|
|
#ifndef Q_OS_WINRT
|
|
void setSocketDescriptor();
|
|
#endif
|
|
void listenWhileListening();
|
|
void addressReusable();
|
|
void setNewSocketDescriptorBlocking();
|
|
#ifndef QT_NO_NETWORKPROXY
|
|
void invalidProxy_data();
|
|
void invalidProxy();
|
|
void proxyFactory_data();
|
|
void proxyFactory();
|
|
#endif // !QT_NO_NETWORKPROXY
|
|
|
|
void qtbug14268_peek();
|
|
|
|
void serverAddress_data();
|
|
void serverAddress();
|
|
|
|
void qtbug6305_data() { serverAddress_data(); }
|
|
void qtbug6305();
|
|
|
|
void linkLocal();
|
|
|
|
void eagainBlockingAccept();
|
|
|
|
void qtbug51288();
|
|
|
|
private:
|
|
#ifndef QT_NO_BEARERMANAGEMENT
|
|
QNetworkSession *networkSession;
|
|
#endif
|
|
QString crashingServerDir;
|
|
};
|
|
|
|
// Testing get/set functions
|
|
void tst_QTcpServer::getSetCheck()
|
|
{
|
|
QTcpServer obj1;
|
|
// int QTcpServer::maxPendingConnections()
|
|
// void QTcpServer::setMaxPendingConnections(int)
|
|
obj1.setMaxPendingConnections(0);
|
|
QCOMPARE(0, obj1.maxPendingConnections());
|
|
obj1.setMaxPendingConnections(INT_MIN);
|
|
QCOMPARE(INT_MIN, obj1.maxPendingConnections());
|
|
obj1.setMaxPendingConnections(INT_MAX);
|
|
QCOMPARE(INT_MAX, obj1.maxPendingConnections());
|
|
}
|
|
|
|
void tst_QTcpServer::initTestCase_data()
|
|
{
|
|
QTest::addColumn<bool>("setProxy");
|
|
QTest::addColumn<int>("proxyType");
|
|
|
|
QTest::newRow("WithoutProxy") << false << 0;
|
|
#ifndef QT_NO_SOCKS5
|
|
QTest::newRow("WithSocks5Proxy") << true << int(QNetworkProxy::Socks5Proxy);
|
|
#endif
|
|
|
|
crashingServerDir = QFINDTESTDATA("crashingServer");
|
|
QVERIFY2(!crashingServerDir.isEmpty(), qPrintable(
|
|
QString::fromLatin1("Couldn't find crashingServer dir starting from %1.").arg(QDir::currentPath())));
|
|
}
|
|
|
|
void tst_QTcpServer::initTestCase()
|
|
{
|
|
if (!QtNetworkSettings::verifyTestNetworkSettings())
|
|
QSKIP("No network test server available");
|
|
#ifndef QT_NO_BEARERMANAGEMENT
|
|
QNetworkConfigurationManager man;
|
|
networkSession = new QNetworkSession(man.defaultConfiguration(), this);
|
|
networkSession->open();
|
|
QVERIFY(networkSession->waitForOpened());
|
|
#endif
|
|
}
|
|
|
|
void tst_QTcpServer::init()
|
|
{
|
|
QFETCH_GLOBAL(bool, setProxy);
|
|
if (setProxy) {
|
|
#ifndef QT_NO_NETWORKPROXY
|
|
QFETCH_GLOBAL(int, proxyType);
|
|
if (proxyType == QNetworkProxy::Socks5Proxy) {
|
|
QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1080));
|
|
}
|
|
#else // !QT_NO_NETWORKPROXY
|
|
QSKIP("No proxy support");
|
|
#endif // QT_NO_NETWORKPROXY
|
|
}
|
|
}
|
|
|
|
void tst_QTcpServer::cleanup()
|
|
{
|
|
#ifndef QT_NO_NETWORKPROXY
|
|
QNetworkProxy::setApplicationProxy(QNetworkProxy::DefaultProxy);
|
|
#endif
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------
|
|
|
|
void tst_QTcpServer::constructing()
|
|
{
|
|
QTcpServer socket;
|
|
|
|
// Check the initial state of the QTcpSocket.
|
|
QCOMPARE(socket.isListening(), false);
|
|
QCOMPARE((int)socket.serverPort(), 0);
|
|
QCOMPARE(socket.serverAddress(), QHostAddress());
|
|
QCOMPARE(socket.maxPendingConnections(), 30);
|
|
QCOMPARE(socket.hasPendingConnections(), false);
|
|
QCOMPARE(socket.socketDescriptor(), (qintptr)-1);
|
|
QCOMPARE(socket.serverError(), QAbstractSocket::UnknownSocketError);
|
|
|
|
// Check the state of the socket layer?
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------
|
|
void tst_QTcpServer::clientServerLoop()
|
|
{
|
|
QTcpServer server;
|
|
|
|
QSignalSpy spy(&server, SIGNAL(newConnection()));
|
|
|
|
QVERIFY(!server.isListening());
|
|
QVERIFY(!server.hasPendingConnections());
|
|
QVERIFY(server.listen(QHostAddress::Any, 11423));
|
|
QVERIFY(server.isListening());
|
|
|
|
QTcpSocket client;
|
|
|
|
QHostAddress serverAddress = QHostAddress::LocalHost;
|
|
if (!(server.serverAddress() == QHostAddress::Any) && !(server.serverAddress() == QHostAddress::AnyIPv6) && !(server.serverAddress() == QHostAddress::AnyIPv4))
|
|
serverAddress = server.serverAddress();
|
|
|
|
client.connectToHost(serverAddress, server.serverPort());
|
|
QVERIFY(client.waitForConnected(5000));
|
|
|
|
QVERIFY(server.waitForNewConnection(5000));
|
|
QVERIFY(server.hasPendingConnections());
|
|
|
|
QCOMPARE(spy.count(), 1);
|
|
|
|
QTcpSocket *serverSocket = server.nextPendingConnection();
|
|
QVERIFY(serverSocket != 0);
|
|
|
|
QVERIFY(serverSocket->write("Greetings, client!\n", 19) == 19);
|
|
serverSocket->flush();
|
|
|
|
QVERIFY(client.waitForReadyRead(5000));
|
|
QByteArray arr = client.readAll();
|
|
QCOMPARE(arr.constData(), "Greetings, client!\n");
|
|
|
|
QVERIFY(client.write("Well, hello to you!\n", 20) == 20);
|
|
client.flush();
|
|
|
|
QVERIFY(serverSocket->waitForReadyRead(5000));
|
|
arr = serverSocket->readAll();
|
|
QCOMPARE(arr.constData(), "Well, hello to you!\n");
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------
|
|
void tst_QTcpServer::ipv6Server()
|
|
{
|
|
if (!QtNetworkSettings::hasIPv6())
|
|
QSKIP("system doesn't support ipv6!");
|
|
//### need to enter the event loop for the server to get the connection ?? ( windows)
|
|
QTcpServer server;
|
|
if (!server.listen(QHostAddress::LocalHostIPv6, 8944)) {
|
|
QCOMPARE(server.serverError(), QAbstractSocket::UnsupportedSocketOperationError);
|
|
return;
|
|
}
|
|
|
|
QCOMPARE(server.serverPort(), quint16(8944));
|
|
QVERIFY(server.serverAddress() == QHostAddress::LocalHostIPv6);
|
|
|
|
QTcpSocket client;
|
|
client.connectToHost("::1", 8944);
|
|
QVERIFY(client.waitForConnected(5000));
|
|
|
|
QVERIFY(server.waitForNewConnection());
|
|
QVERIFY(server.hasPendingConnections());
|
|
|
|
QTcpSocket *serverSocket = 0;
|
|
QVERIFY((serverSocket = server.nextPendingConnection()));
|
|
serverSocket->close();
|
|
delete serverSocket;
|
|
}
|
|
|
|
Q_DECLARE_METATYPE(QHostAddress);
|
|
|
|
void tst_QTcpServer::dualStack_data()
|
|
{
|
|
QTest::addColumn<QHostAddress>("bindAddress");
|
|
QTest::addColumn<bool>("v4ok");
|
|
QTest::addColumn<bool>("v6ok");
|
|
QTest::newRow("any") << QHostAddress(QHostAddress::Any) << true << true;
|
|
QTest::newRow("anyIPv4") << QHostAddress(QHostAddress::AnyIPv4) << true << false;
|
|
QTest::newRow("anyIPv6") << QHostAddress(QHostAddress::AnyIPv6) << false << true;
|
|
}
|
|
|
|
void tst_QTcpServer::dualStack()
|
|
{
|
|
QFETCH_GLOBAL(bool, setProxy);
|
|
if (setProxy)
|
|
QSKIP("test server proxy doesn't support ipv6");
|
|
if (!QtNetworkSettings::hasIPv6())
|
|
QSKIP("system doesn't support ipv6!");
|
|
QFETCH(QHostAddress, bindAddress);
|
|
QFETCH(bool, v4ok);
|
|
QFETCH(bool, v6ok);
|
|
|
|
QTcpServer server;
|
|
QVERIFY(server.listen(bindAddress));
|
|
|
|
QTcpSocket v4client;
|
|
v4client.connectToHost(QHostAddress::LocalHost, server.serverPort());
|
|
|
|
QTcpSocket v6client;
|
|
v6client.connectToHost(QHostAddress::LocalHostIPv6, server.serverPort());
|
|
|
|
QCOMPARE(v4client.waitForConnected(5000), v4ok);
|
|
QCOMPARE(v6client.waitForConnected(5000), v6ok);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------
|
|
void tst_QTcpServer::ipv6ServerMapped()
|
|
{
|
|
QFETCH_GLOBAL(bool, setProxy);
|
|
if (setProxy)
|
|
return;
|
|
|
|
QTcpServer server;
|
|
QVERIFY(server.listen(QHostAddress::LocalHost));
|
|
|
|
// let's try the normal case
|
|
QTcpSocket client1;
|
|
client1.connectToHost("127.0.0.1", server.serverPort());
|
|
QVERIFY(server.waitForNewConnection(5000));
|
|
delete server.nextPendingConnection();
|
|
|
|
if (!QtNetworkSettings::hasIPv6())
|
|
QSKIP("system doesn't support ipv6!");
|
|
|
|
// let's try the mapped one in the nice format
|
|
QTcpSocket client2;
|
|
client2.connectToHost("::ffff:127.0.0.1", server.serverPort());
|
|
QVERIFY(server.waitForNewConnection(5000));
|
|
delete server.nextPendingConnection();
|
|
|
|
// let's try the mapped in hex format
|
|
QTcpSocket client3;
|
|
client3.connectToHost("::ffff:7F00:0001", server.serverPort());
|
|
QVERIFY(server.waitForNewConnection(5000));
|
|
delete server.nextPendingConnection();
|
|
|
|
// However connecting to the v6 localhost should not work
|
|
QTcpSocket client4;
|
|
client4.connectToHost("::1", server.serverPort());
|
|
QVERIFY(!server.waitForNewConnection(5000));
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------
|
|
void tst_QTcpServer::crashTests()
|
|
{
|
|
QTcpServer server;
|
|
server.close();
|
|
QVERIFY(server.listen());
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------
|
|
void tst_QTcpServer::maxPendingConnections()
|
|
{
|
|
QFETCH_GLOBAL(bool, setProxy);
|
|
if (setProxy) {
|
|
#ifndef QT_NO_NETWORKPROXY
|
|
QFETCH_GLOBAL(int, proxyType);
|
|
if (proxyType == QNetworkProxy::Socks5Proxy)
|
|
QSKIP("With socks5 only 1 connection is allowed ever");
|
|
#else // !QT_NO_NETWORKPROXY
|
|
QSKIP("No proxy support");
|
|
#endif // QT_NO_NETWORKPROXY
|
|
}
|
|
//### sees to fail sometimes ... a timing issue with the test on windows
|
|
QTcpServer server;
|
|
server.setMaxPendingConnections(2);
|
|
|
|
QTcpSocket socket1;
|
|
QTcpSocket socket2;
|
|
QTcpSocket socket3;
|
|
|
|
QVERIFY(server.listen());
|
|
|
|
socket1.connectToHost(QHostAddress::LocalHost, server.serverPort());
|
|
socket2.connectToHost(QHostAddress::LocalHost, server.serverPort());
|
|
socket3.connectToHost(QHostAddress::LocalHost, server.serverPort());
|
|
|
|
QVERIFY(server.waitForNewConnection(5000));
|
|
|
|
QVERIFY(server.hasPendingConnections());
|
|
QVERIFY(server.nextPendingConnection());
|
|
QVERIFY(server.hasPendingConnections());
|
|
QVERIFY(server.nextPendingConnection());
|
|
QVERIFY(!server.hasPendingConnections());
|
|
QCOMPARE(server.nextPendingConnection(), (QTcpSocket*)0);
|
|
|
|
QVERIFY(server.waitForNewConnection(5000));
|
|
|
|
QVERIFY(server.hasPendingConnections());
|
|
QVERIFY(server.nextPendingConnection());
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------
|
|
void tst_QTcpServer::listenError()
|
|
{
|
|
QFETCH_GLOBAL(bool, setProxy);
|
|
if (setProxy) {
|
|
#ifndef QT_NO_NETWORKPROXY
|
|
QFETCH_GLOBAL(int, proxyType);
|
|
if (proxyType == QNetworkProxy::Socks5Proxy)
|
|
QSKIP("With socks5 we can not make hard requirements on the address or port");
|
|
#else // !QT_NO_NETWORKPROXY
|
|
QSKIP("No proxy support");
|
|
#endif //QT_NO_NETWORKPROXY
|
|
}
|
|
QTcpServer server;
|
|
QVERIFY(!server.listen(QHostAddress("1.2.3.4"), 0));
|
|
QCOMPARE(server.serverError(), QAbstractSocket::SocketAddressNotAvailableError);
|
|
QCOMPARE(server.errorString().toLatin1().constData(), "The address is not available");
|
|
}
|
|
|
|
class ThreadConnector : public QThread
|
|
{
|
|
public:
|
|
ThreadConnector(const QHostAddress &host, quint16 port)
|
|
: host(host), port(port)
|
|
{ }
|
|
|
|
~ThreadConnector()
|
|
{
|
|
wait();
|
|
}
|
|
|
|
protected:
|
|
void run()
|
|
{
|
|
sleep(2);
|
|
|
|
QTcpSocket socket;
|
|
socket.connectToHost(host, port);
|
|
|
|
QEventLoop loop;
|
|
QTimer::singleShot(5000, &loop, SLOT(quit()));
|
|
loop.exec();
|
|
}
|
|
|
|
private:
|
|
QHostAddress host;
|
|
quint16 port;
|
|
};
|
|
|
|
//----------------------------------------------------------------------------------
|
|
void tst_QTcpServer::waitForConnectionTest()
|
|
{
|
|
|
|
QFETCH_GLOBAL(bool, setProxy);
|
|
if (setProxy) {
|
|
#ifndef QT_NO_NETWORKPROXY
|
|
QFETCH_GLOBAL(int, proxyType);
|
|
if (proxyType == QNetworkProxy::Socks5Proxy)
|
|
QSKIP("Localhost servers don't work well with SOCKS5");
|
|
#else // !QT_NO_NETWORKPROXY
|
|
QSKIP("No proxy support");
|
|
#endif // QT_NO_NETWORKPROXY
|
|
}
|
|
|
|
QTcpSocket findLocalIpSocket;
|
|
findLocalIpSocket.connectToHost(QtNetworkSettings::serverName(), 143);
|
|
QVERIFY(findLocalIpSocket.waitForConnected(5000));
|
|
|
|
QTcpServer server;
|
|
bool timeout = false;
|
|
QVERIFY(server.listen(findLocalIpSocket.localAddress()));
|
|
QVERIFY(!server.waitForNewConnection(1000, &timeout));
|
|
QCOMPARE(server.serverError(), QAbstractSocket::SocketTimeoutError);
|
|
QVERIFY(timeout);
|
|
|
|
ThreadConnector connector(findLocalIpSocket.localAddress(), server.serverPort());
|
|
connector.start();
|
|
|
|
#if defined(Q_OS_WINCE)
|
|
QVERIFY(server.waitForNewConnection(9000, &timeout));
|
|
#else
|
|
QVERIFY(server.waitForNewConnection(3000, &timeout));
|
|
#endif
|
|
QVERIFY(!timeout);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------
|
|
#ifndef Q_OS_WINRT
|
|
void tst_QTcpServer::setSocketDescriptor()
|
|
{
|
|
QTcpServer server;
|
|
QVERIFY(!server.setSocketDescriptor(42));
|
|
QCOMPARE(server.serverError(), QAbstractSocket::UnsupportedSocketOperationError);
|
|
//adopting Open C sockets is not supported, neither is adopting externally created RSocket
|
|
#ifdef Q_OS_WIN
|
|
// ensure winsock is started
|
|
WSADATA wsaData;
|
|
QVERIFY(WSAStartup(MAKEWORD(2,0), &wsaData) == NO_ERROR);
|
|
#endif
|
|
|
|
SOCKET sock = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
|
|
|
QVERIFY(sock != INVALID_SOCKET);
|
|
|
|
sockaddr_in sin;
|
|
memset(&sin, 0, sizeof(sockaddr_in));
|
|
sin.sin_family = AF_INET;
|
|
sin.sin_port = 0;
|
|
sin.sin_addr.s_addr = 0x00000000;
|
|
QVERIFY(::bind(sock, (sockaddr*)&sin, sizeof(sockaddr_in)) == 0);
|
|
QVERIFY(::listen(sock, 10) == 0);
|
|
QVERIFY(server.setSocketDescriptor(sock));
|
|
|
|
#ifdef Q_OS_WIN
|
|
WSACleanup();
|
|
#endif
|
|
}
|
|
#endif // !Q_OS_WINRT
|
|
|
|
//----------------------------------------------------------------------------------
|
|
void tst_QTcpServer::listenWhileListening()
|
|
{
|
|
QTcpServer server;
|
|
QVERIFY(server.listen());
|
|
QTest::ignoreMessage(QtWarningMsg, "QTcpServer::listen() called when already listening");
|
|
QVERIFY(!server.listen());
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------
|
|
|
|
class SeverWithBlockingSockets : public QTcpServer
|
|
{
|
|
public:
|
|
SeverWithBlockingSockets()
|
|
: ok(false) { }
|
|
|
|
bool ok;
|
|
|
|
protected:
|
|
#ifndef Q_OS_WINRT
|
|
void incomingConnection(qintptr socketDescriptor)
|
|
{
|
|
// how a user woulddo it (qabstractsocketengine is not public)
|
|
unsigned long arg = 0;
|
|
#if defined(Q_OS_WIN)
|
|
ok = ::ioctlsocket(socketDescriptor, FIONBIO, &arg) == 0;
|
|
::closesocket(socketDescriptor);
|
|
#else
|
|
ok = ::ioctl(socketDescriptor, FIONBIO, &arg) == 0;
|
|
::close(socketDescriptor);
|
|
#endif
|
|
}
|
|
#endif // !Q_OS_WINRT
|
|
};
|
|
|
|
void tst_QTcpServer::addressReusable()
|
|
{
|
|
#ifdef QT_NO_PROCESS
|
|
QSKIP("No qprocess support", SkipAll);
|
|
#else
|
|
#ifdef Q_OS_LINUX
|
|
QSKIP("The addressReusable test is unstable on Linux. See QTBUG-39985.");
|
|
#endif
|
|
QFETCH_GLOBAL(bool, setProxy);
|
|
if (setProxy) {
|
|
#ifndef QT_NO_NETWORKPROXY
|
|
QFETCH_GLOBAL(int, proxyType);
|
|
if (proxyType == QNetworkProxy::Socks5Proxy)
|
|
QSKIP("With socks5 this test does not make senans at the momment");
|
|
#else // !QT_NO_NETWORKPROXY
|
|
QSKIP("No proxy support");
|
|
#endif // QT_NO_NETWORKPROXY
|
|
}
|
|
#if defined(Q_OS_WINCE)
|
|
QString signalName = QString::fromLatin1("/test_signal.txt");
|
|
QFile::remove(signalName);
|
|
// The crashingServer process will crash once it gets a connection.
|
|
QProcess process;
|
|
QString processExe = crashingServerDir + "/crashingServer";
|
|
process.start(processExe);
|
|
QVERIFY2(process.waitForStarted(), qPrintable(
|
|
QString::fromLatin1("Could not start %1: %2").arg(processExe, process.errorString())));
|
|
int waitCount = 5;
|
|
while (waitCount-- && !QFile::exists(signalName))
|
|
QTest::qWait(1000);
|
|
QVERIFY(QFile::exists(signalName));
|
|
QFile::remove(signalName);
|
|
#else
|
|
// The crashingServer process will crash once it gets a connection.
|
|
QProcess process;
|
|
QString processExe = crashingServerDir + "/crashingServer";
|
|
process.start(processExe);
|
|
QVERIFY2(process.waitForStarted(), qPrintable(
|
|
QString::fromLatin1("Could not start %1: %2").arg(processExe, process.errorString())));
|
|
QVERIFY(process.waitForReadyRead(5000));
|
|
#endif
|
|
|
|
QTcpSocket socket;
|
|
socket.connectToHost(QHostAddress::LocalHost, 49199);
|
|
QVERIFY(socket.waitForConnected(5000));
|
|
|
|
QVERIFY(process.waitForFinished(30000));
|
|
|
|
// Give the system some time.
|
|
QTest::qSleep(10);
|
|
|
|
QTcpServer server;
|
|
QVERIFY(server.listen(QHostAddress::LocalHost, 49199));
|
|
#endif
|
|
}
|
|
|
|
void tst_QTcpServer::setNewSocketDescriptorBlocking()
|
|
{
|
|
QFETCH_GLOBAL(bool, setProxy);
|
|
if (setProxy) {
|
|
#ifndef QT_NO_NETWORKPROXY
|
|
QFETCH_GLOBAL(int, proxyType);
|
|
if (proxyType == QNetworkProxy::Socks5Proxy)
|
|
QSKIP("With socks5 we can not make the socket descripter blocking");
|
|
#else // !QT_NO_NETWORKPROXY
|
|
QSKIP("No proxy support");
|
|
#endif // QT_NO_NETWORKPROXY
|
|
}
|
|
SeverWithBlockingSockets server;
|
|
QVERIFY(server.listen());
|
|
|
|
QTcpSocket socket;
|
|
socket.connectToHost(QHostAddress::LocalHost, server.serverPort());
|
|
QVERIFY(server.waitForNewConnection(5000));
|
|
QVERIFY(server.ok);
|
|
}
|
|
|
|
#ifndef QT_NO_NETWORKPROXY
|
|
void tst_QTcpServer::invalidProxy_data()
|
|
{
|
|
QTest::addColumn<int>("type");
|
|
QTest::addColumn<QString>("host");
|
|
QTest::addColumn<int>("port");
|
|
QTest::addColumn<int>("expectedError");
|
|
|
|
QString fluke = QHostInfo::fromName(QtNetworkSettings::serverName()).addresses().first().toString();
|
|
QTest::newRow("ftp-proxy") << int(QNetworkProxy::FtpCachingProxy) << fluke << 143
|
|
<< int(QAbstractSocket::UnsupportedSocketOperationError);
|
|
QTest::newRow("http-proxy") << int(QNetworkProxy::HttpProxy) << fluke << 3128
|
|
<< int(QAbstractSocket::UnsupportedSocketOperationError);
|
|
|
|
QTest::newRow("no-such-host") << int(QNetworkProxy::Socks5Proxy)
|
|
<< "invalid.test.qt-project.org" << 1080
|
|
<< int(QAbstractSocket::ProxyNotFoundError);
|
|
QTest::newRow("socks5-on-http") << int(QNetworkProxy::Socks5Proxy) << fluke << 3128
|
|
<< int(QAbstractSocket::SocketTimeoutError);
|
|
}
|
|
|
|
void tst_QTcpServer::invalidProxy()
|
|
{
|
|
QFETCH_GLOBAL(bool, setProxy);
|
|
if (setProxy)
|
|
return;
|
|
|
|
QFETCH(int, type);
|
|
QFETCH(QString, host);
|
|
QFETCH(int, port);
|
|
QNetworkProxy::ProxyType proxyType = QNetworkProxy::ProxyType(type);
|
|
QNetworkProxy proxy(proxyType, host, port);
|
|
|
|
QTcpServer server;
|
|
server.setProxy(proxy);
|
|
bool listenResult = server.listen();
|
|
|
|
QVERIFY(!listenResult);
|
|
QVERIFY(!server.errorString().isEmpty());
|
|
|
|
// note: the following test is not a hard failure.
|
|
// Sometimes, error codes change for the better
|
|
QTEST(int(server.serverError()), "expectedError");
|
|
}
|
|
|
|
// copied from tst_qnetworkreply.cpp
|
|
class MyProxyFactory: public QNetworkProxyFactory
|
|
{
|
|
public:
|
|
int callCount;
|
|
QList<QNetworkProxy> toReturn;
|
|
QNetworkProxyQuery lastQuery;
|
|
inline MyProxyFactory() { clear(); }
|
|
|
|
inline void clear()
|
|
{
|
|
callCount = 0;
|
|
toReturn = QList<QNetworkProxy>() << QNetworkProxy::DefaultProxy;
|
|
lastQuery = QNetworkProxyQuery();
|
|
}
|
|
|
|
virtual QList<QNetworkProxy> queryProxy(const QNetworkProxyQuery &query)
|
|
{
|
|
lastQuery = query;
|
|
++callCount;
|
|
return toReturn;
|
|
}
|
|
};
|
|
|
|
void tst_QTcpServer::proxyFactory_data()
|
|
{
|
|
QTest::addColumn<QList<QNetworkProxy> >("proxyList");
|
|
QTest::addColumn<QNetworkProxy>("proxyUsed");
|
|
QTest::addColumn<bool>("fails");
|
|
QTest::addColumn<int>("expectedError");
|
|
|
|
QList<QNetworkProxy> proxyList;
|
|
|
|
// tests that do get to listen
|
|
|
|
proxyList << QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1080);
|
|
QTest::newRow("socks5")
|
|
<< proxyList << proxyList.at(0)
|
|
<< false << int(QAbstractSocket::UnknownSocketError);
|
|
|
|
proxyList.clear();
|
|
proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3128)
|
|
<< QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1080);
|
|
QTest::newRow("cachinghttp+socks5")
|
|
<< proxyList << proxyList.at(1)
|
|
<< false << int(QAbstractSocket::UnknownSocketError);
|
|
|
|
proxyList.clear();
|
|
proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::serverName(), 2121)
|
|
<< QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3128)
|
|
<< QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1080);
|
|
QTest::newRow("ftp+cachinghttp+socks5")
|
|
<< proxyList << proxyList.at(2)
|
|
<< false << int(QAbstractSocket::UnknownSocketError);
|
|
|
|
// tests that fail to listen
|
|
proxyList.clear();
|
|
proxyList << QNetworkProxy(QNetworkProxy::HttpProxy, QtNetworkSettings::serverName(), 3128);
|
|
QTest::newRow("http")
|
|
<< proxyList << proxyList.at(0)
|
|
<< true << int(QAbstractSocket::UnsupportedSocketOperationError);
|
|
|
|
proxyList.clear();
|
|
proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3128);
|
|
QTest::newRow("cachinghttp")
|
|
<< proxyList << QNetworkProxy()
|
|
<< true << int(QAbstractSocket::UnsupportedSocketOperationError);
|
|
|
|
proxyList.clear();
|
|
proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::serverName(), 2121);
|
|
QTest::newRow("ftp")
|
|
<< proxyList << QNetworkProxy()
|
|
<< true << int(QAbstractSocket::UnsupportedSocketOperationError);
|
|
|
|
proxyList.clear();
|
|
proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::serverName(), 2121)
|
|
<< QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3128);
|
|
QTest::newRow("ftp+cachinghttp")
|
|
<< proxyList << QNetworkProxy()
|
|
<< true << int(QAbstractSocket::UnsupportedSocketOperationError);
|
|
}
|
|
|
|
void tst_QTcpServer::proxyFactory()
|
|
{
|
|
QFETCH_GLOBAL(bool, setProxy);
|
|
if (setProxy)
|
|
return;
|
|
|
|
QFETCH(QList<QNetworkProxy>, proxyList);
|
|
QFETCH(QNetworkProxy, proxyUsed);
|
|
QFETCH(bool, fails);
|
|
|
|
MyProxyFactory *factory = new MyProxyFactory;
|
|
factory->toReturn = proxyList;
|
|
QNetworkProxyFactory::setApplicationProxyFactory(factory);
|
|
|
|
QTcpServer server;
|
|
bool listenResult = server.listen();
|
|
|
|
// Verify that the factory was called properly
|
|
QCOMPARE(factory->callCount, 1);
|
|
QCOMPARE(factory->lastQuery, QNetworkProxyQuery(0, QString(), QNetworkProxyQuery::TcpServer));
|
|
|
|
QCOMPARE(listenResult, !fails);
|
|
QCOMPARE(server.errorString().isEmpty(), !fails);
|
|
|
|
// note: the following test is not a hard failure.
|
|
// Sometimes, error codes change for the better
|
|
QTEST(int(server.serverError()), "expectedError");
|
|
}
|
|
#endif // !QT_NO_NETWORKPROXY
|
|
|
|
class Qtbug14268Helper : public QObject
|
|
{
|
|
Q_OBJECT
|
|
public:
|
|
QByteArray lastDataPeeked;
|
|
public slots:
|
|
void newConnection() {
|
|
QTcpServer* server=static_cast<QTcpServer*>(sender());
|
|
QTcpSocket* s=server->nextPendingConnection();
|
|
connect(s,SIGNAL(readyRead()),this,SLOT(onServerReadyRead()));
|
|
}
|
|
void onServerReadyRead() {
|
|
QTcpSocket* clientSocket=static_cast<QTcpSocket*>(sender());
|
|
lastDataPeeked = clientSocket->peek(128*1024).toHex();
|
|
QTestEventLoop::instance().exitLoop();
|
|
}
|
|
};
|
|
|
|
// there is a similar test inside tst_qtcpsocket that uses the waitFor* functions instead
|
|
void tst_QTcpServer::qtbug14268_peek()
|
|
{
|
|
QFETCH_GLOBAL(bool, setProxy);
|
|
if (setProxy)
|
|
return;
|
|
|
|
QTcpServer server;
|
|
server.listen();
|
|
|
|
Qtbug14268Helper helper;
|
|
QObject::connect(&server, SIGNAL(newConnection()), &helper, SLOT(newConnection()));
|
|
|
|
QTcpSocket client;
|
|
client.connectToHost(QHostAddress::LocalHost, server.serverPort());
|
|
QVERIFY(client.waitForConnected(2000));
|
|
|
|
client.write("abc\n");
|
|
QTestEventLoop::instance().enterLoop(5);
|
|
QVERIFY(!QTestEventLoop::instance().timeout());
|
|
QCOMPARE(helper.lastDataPeeked, QByteArray("6162630a"));
|
|
|
|
client.write("def\n");
|
|
QTestEventLoop::instance().enterLoop(5);
|
|
QVERIFY(!QTestEventLoop::instance().timeout());
|
|
QCOMPARE(helper.lastDataPeeked, QByteArray("6162630a6465660a"));
|
|
|
|
client.write("ghi\n");
|
|
QTestEventLoop::instance().enterLoop(5);
|
|
QVERIFY(!QTestEventLoop::instance().timeout());
|
|
QCOMPARE(helper.lastDataPeeked, QByteArray("6162630a6465660a6768690a"));
|
|
}
|
|
|
|
void tst_QTcpServer::serverAddress_data()
|
|
{
|
|
QTest::addColumn<QHostAddress>("listenAddress");
|
|
QTest::addColumn<QHostAddress>("serverAddress");
|
|
#ifdef Q_OS_WIN
|
|
if (QSysInfo::windowsVersion() < QSysInfo::WV_6_0)
|
|
QTest::newRow("Any") << QHostAddress(QHostAddress::Any) << QHostAddress(QHostAddress::AnyIPv4); //windows XP doesn't support dual stack sockets
|
|
else
|
|
#endif
|
|
if (QtNetworkSettings::hasIPv6())
|
|
QTest::newRow("Any") << QHostAddress(QHostAddress::Any) << QHostAddress(QHostAddress::Any);
|
|
else
|
|
QTest::newRow("Any") << QHostAddress(QHostAddress::Any) << QHostAddress(QHostAddress::AnyIPv4);
|
|
QTest::newRow("AnyIPv4") << QHostAddress(QHostAddress::AnyIPv4) << QHostAddress(QHostAddress::AnyIPv4);
|
|
if (QtNetworkSettings::hasIPv6())
|
|
QTest::newRow("AnyIPv6") << QHostAddress(QHostAddress::AnyIPv6) << QHostAddress(QHostAddress::AnyIPv6);
|
|
foreach (const QNetworkInterface &iface, QNetworkInterface::allInterfaces()) {
|
|
if ((iface.flags() & QNetworkInterface::IsUp) == 0)
|
|
continue;
|
|
foreach (const QNetworkAddressEntry &entry, iface.addressEntries()) {
|
|
QTest::newRow(qPrintable(entry.ip().toString())) << entry.ip() << entry.ip();
|
|
}
|
|
}
|
|
}
|
|
|
|
void tst_QTcpServer::serverAddress()
|
|
{
|
|
QFETCH_GLOBAL(bool, setProxy);
|
|
if (setProxy)
|
|
return;
|
|
|
|
QFETCH(QHostAddress, listenAddress);
|
|
QFETCH(QHostAddress, serverAddress);
|
|
QTcpServer server;
|
|
|
|
// TODO: why does this QSKIP?
|
|
if (!server.listen(listenAddress))
|
|
QSKIP(qPrintable(server.errorString()));
|
|
QCOMPARE(server.serverAddress(), serverAddress);
|
|
}
|
|
|
|
// on OS X, calling listen() multiple times would succeed each time, which is
|
|
// most definitely not wanted.
|
|
void tst_QTcpServer::qtbug6305()
|
|
{
|
|
QFETCH_GLOBAL(bool, setProxy);
|
|
if (setProxy)
|
|
return;
|
|
|
|
QFETCH(QHostAddress, listenAddress);
|
|
|
|
QTcpServer server;
|
|
QVERIFY2(server.listen(listenAddress), qPrintable(server.errorString()));
|
|
|
|
QTcpServer server2;
|
|
QVERIFY(!server2.listen(listenAddress, server.serverPort())); // second listen should fail
|
|
}
|
|
|
|
void tst_QTcpServer::linkLocal()
|
|
{
|
|
QFETCH_GLOBAL(bool, setProxy);
|
|
if (setProxy)
|
|
return;
|
|
|
|
QList <QHostAddress> addresses;
|
|
QSet <QString> scopes;
|
|
QHostAddress localMaskv4("169.254.0.0");
|
|
QHostAddress localMaskv6("fe80::");
|
|
foreach (const QNetworkInterface& iface, QNetworkInterface::allInterfaces()) {
|
|
//Windows preallocates link local addresses to interfaces that are down.
|
|
//These may or may not work depending on network driver (they do not work for the Bluetooth PAN driver)
|
|
if (iface.flags() & QNetworkInterface::IsUp) {
|
|
// Do not connect to the Teredo Tunneling interface on Windows Xp.
|
|
if (iface.humanReadableName() == QString("Teredo Tunneling Pseudo-Interface"))
|
|
continue;
|
|
foreach (QNetworkAddressEntry addressEntry, iface.addressEntries()) {
|
|
QHostAddress addr = addressEntry.ip();
|
|
if (addr.isInSubnet(localMaskv4, 16)) {
|
|
addresses << addr;
|
|
qDebug() << addr;
|
|
}
|
|
else if (!addr.scopeId().isEmpty() && addr.isInSubnet(localMaskv6, 64)) {
|
|
scopes << addr.scopeId();
|
|
addresses << addr;
|
|
qDebug() << addr;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (addresses.isEmpty())
|
|
QSKIP("no link local addresses");
|
|
|
|
QList<QTcpServer*> servers;
|
|
quint16 port = 0;
|
|
foreach (const QHostAddress& addr, addresses) {
|
|
QTcpServer *server = new QTcpServer;
|
|
QVERIFY(server->listen(addr, port));
|
|
port = server->serverPort(); //listen to same port on different interfaces
|
|
servers << server;
|
|
}
|
|
|
|
QList<QTcpSocket*> clients;
|
|
foreach (const QHostAddress& addr, addresses) {
|
|
//unbound socket
|
|
QTcpSocket *socket = new QTcpSocket;
|
|
socket->connectToHost(addr, port);
|
|
QVERIFY(socket->waitForConnected(5000));
|
|
clients << socket;
|
|
//bound socket
|
|
socket = new QTcpSocket;
|
|
QVERIFY(socket->bind(addr));
|
|
socket->connectToHost(addr, port);
|
|
QVERIFY(socket->waitForConnected(5000));
|
|
clients << socket;
|
|
}
|
|
|
|
//each server should have two connections
|
|
foreach (QTcpServer* server, servers) {
|
|
QTcpSocket* remote;
|
|
//qDebug() << "checking for connections" << server->serverAddress() << ":" << server->serverPort();
|
|
QVERIFY(server->waitForNewConnection(5000));
|
|
QVERIFY(remote = server->nextPendingConnection());
|
|
remote->close();
|
|
delete remote;
|
|
if (!server->hasPendingConnections())
|
|
QVERIFY(server->waitForNewConnection(5000));
|
|
QVERIFY(remote = server->nextPendingConnection());
|
|
remote->close();
|
|
delete remote;
|
|
QVERIFY(!server->hasPendingConnections());
|
|
}
|
|
|
|
//Connecting to the same address with different scope should normally fail
|
|
//However it will pass if there are two interfaces connected to the same physical network,
|
|
//e.g. connected via wired and wireless interfaces, or two wired NICs.
|
|
//which is a reasonably common case.
|
|
//So this is not auto tested.
|
|
|
|
qDeleteAll(clients);
|
|
qDeleteAll(servers);
|
|
}
|
|
|
|
void tst_QTcpServer::eagainBlockingAccept()
|
|
{
|
|
QTcpServer server;
|
|
server.listen(QHostAddress::LocalHost, 7896);
|
|
|
|
// Receiving a new connection causes TemporaryError, but shouldn't pause accepting.
|
|
QTcpSocket s;
|
|
s.connectToHost(QHostAddress::LocalHost, 7896);
|
|
QSignalSpy spy(&server, SIGNAL(newConnection()));
|
|
QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 1, 500);
|
|
s.close();
|
|
|
|
// To test try again, should connect just fine.
|
|
s.connectToHost(QHostAddress::LocalHost, 7896);
|
|
QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 2, 500);
|
|
s.close();
|
|
server.close();
|
|
}
|
|
|
|
class NonListeningTcpServer : public QTcpServer
|
|
{
|
|
public:
|
|
void addSocketFromOutside(QTcpSocket* s)
|
|
{
|
|
addPendingConnection(s);
|
|
}
|
|
};
|
|
|
|
void tst_QTcpServer::qtbug51288()
|
|
{
|
|
NonListeningTcpServer server;
|
|
QTcpSocket socket;
|
|
server.addSocketFromOutside(&socket);
|
|
QCOMPARE(&socket, server.nextPendingConnection());
|
|
}
|
|
|
|
QTEST_MAIN(tst_QTcpServer)
|
|
#include "tst_qtcpserver.moc"
|