qt5base-lts/tests/auto/network/socket/qtcpserver/tst_qtcpserver.cpp
Robin Burchell a84b42e619 Fix QTcpServer::listen() returning true when the port is in use on OS X.
Way back in the mists of time, someone added SO_REUSEPORT to socket binding,
which was great, because otherwise it meant that multiple UDP sockets couldn't
share the same port on OS X (as platforms with SO_REUSEPORT apparently don't
support rebinding with SO_REUSEADDR).

However: SO_REUSEPORT also means that *any* bind on a port will succeed, which
is most definitely not wanted in the case of TCP sockets, so check the socket
type before performing the actual bind.

Also test that multiple listens don't take effect.

Change-Id: I2f8d450bcfb8a7f3abd8918a4e789a850281dd13
Done-with: Thiago Macieira
Done-with: Shane Kearns
Task-number: QTBUG-6305
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Reviewed-by: Shane Kearns <shane.kearns@accenture.com>
2012-04-02 14:52:48 +02:00

860 lines
27 KiB
C++

/****************************************************************************
**
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this
** file. Please review the following information to ensure the GNU Lesser
** General Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <qglobal.h>
#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>
Q_DECLARE_METATYPE(QNetworkProxy)
Q_DECLARE_METATYPE(QList<QNetworkProxy>)
#include <QNetworkSession>
#include <QNetworkConfiguration>
#include <QNetworkConfigurationManager>
#include "../../../network-settings.h"
class tst_QTcpServer : public QObject
{
Q_OBJECT
public:
tst_QTcpServer();
virtual ~tst_QTcpServer();
public slots:
void initTestCase_data();
void initTestCase();
void init();
void cleanup();
private slots:
void getSetCheck();
void constructing();
void clientServerLoop();
void ipv6Server();
void dualStack_data();
void dualStack();
void ipv6ServerMapped();
void crashTests();
void maxPendingConnections();
void listenError();
void waitForConnectionTest();
void setSocketDescriptor();
void listenWhileListening();
#ifndef QT_NO_PROCESS
void addressReusable();
#endif
void setNewSocketDescriptorBlocking();
void invalidProxy_data();
void invalidProxy();
void proxyFactory_data();
void proxyFactory();
void qtbug14268_peek();
void serverAddress_data();
void serverAddress();
void qtbug6305_data() { serverAddress_data(); }
void qtbug6305();
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());
}
tst_QTcpServer::tst_QTcpServer()
{
}
tst_QTcpServer::~tst_QTcpServer()
{
}
void tst_QTcpServer::initTestCase_data()
{
QTest::addColumn<bool>("setProxy");
QTest::addColumn<int>("proxyType");
QTest::newRow("WithoutProxy") << false << 0;
QTest::newRow("WithSocks5Proxy") << true << int(QNetworkProxy::Socks5Proxy);
crashingServerDir = QFINDTESTDATA("crashingServer");
QVERIFY2(!crashingServerDir.isEmpty(), qPrintable(
QString::fromLatin1("Couldn't find crashingServer dir starting from %1.").arg(QDir::currentPath())));
}
void tst_QTcpServer::initTestCase()
{
QVERIFY(QtNetworkSettings::verifyTestNetworkSettings());
#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) {
QFETCH_GLOBAL(int, proxyType);
if (proxyType == QNetworkProxy::Socks5Proxy) {
QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1080));
}
}
}
void tst_QTcpServer::cleanup()
{
QNetworkProxy::setApplicationProxy(QNetworkProxy::DefaultProxy);
}
//----------------------------------------------------------------------------------
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))
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()
{
//### need to enter the event loop for the server to get the connection ?? ( windows)
QTcpServer server;
if (!server.listen(QHostAddress::LocalHostIPv6, 8944)) {
QVERIFY(server.serverError() == QAbstractSocket::UnsupportedSocketOperationError);
return;
}
QVERIFY(server.serverPort() == 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");
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();
// 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) {
QFETCH_GLOBAL(int, proxyType);
if (proxyType == QNetworkProxy::Socks5Proxy)
QSKIP("With socks5 only 1 connection is allowed ever");
}
//### 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) {
QFETCH_GLOBAL(int, proxyType);
if (proxyType == QNetworkProxy::Socks5Proxy)
QSKIP("With socks5 we can not make hard requirements on the address or port");
}
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) {
QFETCH_GLOBAL(int, proxyType);
if (proxyType == QNetworkProxy::Socks5Proxy)
QSKIP("Localhost servers don't work well with SOCKS5");
}
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);
}
//----------------------------------------------------------------------------------
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
}
//----------------------------------------------------------------------------------
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:
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
}
};
#ifndef QT_NO_PROCESS
void tst_QTcpServer::addressReusable()
{
QFETCH_GLOBAL(bool, setProxy);
if (setProxy) {
QFETCH_GLOBAL(int, proxyType);
if (proxyType == QNetworkProxy::Socks5Proxy)
QSKIP("With socks5 this test does not make senans at the momment");
}
#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) {
QFETCH_GLOBAL(int, proxyType);
if (proxyType == QNetworkProxy::Socks5Proxy)
QSKIP("With socks5 we can not make the socket descripter blocking");
}
SeverWithBlockingSockets server;
QVERIFY(server.listen());
QTcpSocket socket;
socket.connectToHost(QHostAddress::LocalHost, server.serverPort());
QVERIFY(server.waitForNewConnection(5000));
QVERIFY(server.ok);
}
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)
<< "this-host-will-never-exist.troll.no" << 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");
}
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());
QVERIFY(helper.lastDataPeeked == QByteArray("6162630a"));
client.write("def\n");
QTestEventLoop::instance().enterLoop(5);
QVERIFY(!QTestEventLoop::instance().timeout());
QVERIFY(helper.lastDataPeeked == QByteArray("6162630a6465660a"));
client.write("ghi\n");
QTestEventLoop::instance().enterLoop(5);
QVERIFY(!QTestEventLoop::instance().timeout());
QVERIFY(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
QTest::newRow("Any") << QHostAddress(QHostAddress::Any) << QHostAddress(QHostAddress::Any);
QTest::newRow("AnyIPv4") << QHostAddress(QHostAddress::AnyIPv4) << QHostAddress(QHostAddress::AnyIPv4);
QTest::newRow("AnyIPv6") << QHostAddress(QHostAddress::AnyIPv6) << QHostAddress(QHostAddress::AnyIPv6);
foreach (const QHostAddress& addr, QNetworkInterface::allAddresses()) {
if (addr.isInSubnet(QHostAddress::parseSubnet("fe80::/10"))
|| addr.isInSubnet(QHostAddress::parseSubnet("169.254/16")))
continue; //cannot bind on link local addresses
QTest::newRow(qPrintable(addr.toString())) << addr << addr;
}
}
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
}
QTEST_MAIN(tst_QTcpServer)
#include "tst_qtcpserver.moc"