85136496bc
Adds support for binding "dual stack" sockets (via QUdpSocket or QTcpServer). A dual stack socket will accept incoming connections on either IPv4 or IPv6 interfaces. QHostAddress::Any - use this to bind a dual stack socket QHostAddress::AnyIPv6 - use this to bind a socket for IPv6 only QHostAddress::AnyIPv4 - use this to bind a socket for IPv4 only Binding to a specific address rather than one of the "any" addresses is restricting you to a protocol anyway so no behaviour change there. IPv6 sockets were previously dual stack on some OS and v6 only on others Any previously meant IPv4 only This commit implemented & tested on Windows 7, Linux (Ubuntu 10.04) and Mac OS 10.6.7. Windows XP and server 2003 do not support dual stack sockets, even though they can support IPv6. On those versions, QHostAddress::Any will still bind to IPv4 0.0.0.0 (which is also the behaviour anywhere QT_NO_IPV6 is defined) Autotests run: qudpsocket (includes a new test case) qtcpserver (includes a new test case) qtcpsocket qnetworkreply qhostaddress Task-number: QTBUG-17080 Change-Id: Id486677c4f832e18dc0ff1a86c5f5fc422c9eb4f Reviewed-on: http://codereview.qt.nokia.com/421 Reviewed-by: Qt Sanity Bot <qt_sanity_bot@ovi.com> Reviewed-by: Thiago Macieira <thiago.macieira@nokia.com> Reviewed-by: Markus Goetz
822 lines
25 KiB
C++
822 lines
25 KiB
C++
/****************************************************************************
|
|
**
|
|
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
|
** All rights reserved.
|
|
** Contact: Nokia Corporation (qt-info@nokia.com)
|
|
**
|
|
** 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$
|
|
**
|
|
****************************************************************************/
|
|
|
|
// Just to get Q_OS_SYMBIAN
|
|
#include <qglobal.h>
|
|
#if defined(_WIN32) && !defined(Q_OS_SYMBIAN)
|
|
#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"
|
|
|
|
//TESTED_CLASS=
|
|
//TESTED_FILES=
|
|
|
|
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();
|
|
void addressReusable();
|
|
void setNewSocketDescriptorBlocking();
|
|
void invalidProxy_data();
|
|
void invalidProxy();
|
|
void proxyFactory_data();
|
|
void proxyFactory();
|
|
|
|
void qtbug14268_peek();
|
|
|
|
private:
|
|
#ifndef QT_NO_BEARERMANAGEMENT
|
|
QNetworkSession *networkSession;
|
|
#endif
|
|
};
|
|
|
|
// 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()
|
|
{
|
|
Q_SET_DEFAULT_IAP
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
void tst_QTcpServer::initTestCase()
|
|
{
|
|
#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(), -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()
|
|
{
|
|
#ifdef QT_NO_IPV6
|
|
QSKIP("test requires IPv6 support", SkipAll);
|
|
#else
|
|
QFETCH_GLOBAL(bool, setProxy);
|
|
if (setProxy)
|
|
QSKIP("test server proxy doesn't support ipv6", SkipSingle);
|
|
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);
|
|
#endif
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------
|
|
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", SkipAll);
|
|
}
|
|
}
|
|
//### 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", SkipAll);
|
|
}
|
|
}
|
|
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", SkipAll);
|
|
}
|
|
}
|
|
|
|
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) || defined(Q_OS_SYMBIAN)
|
|
QVERIFY(server.waitForNewConnection(9000, &timeout));
|
|
#else
|
|
QVERIFY(server.waitForNewConnection(3000, &timeout));
|
|
#endif
|
|
QVERIFY(!timeout);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------
|
|
void tst_QTcpServer::setSocketDescriptor()
|
|
{
|
|
QTcpServer server;
|
|
#ifdef Q_OS_SYMBIAN
|
|
QTest::ignoreMessage(QtWarningMsg, "QSymbianSocketEngine::initialize - socket descriptor not found");
|
|
#endif
|
|
QVERIFY(!server.setSocketDescriptor(42));
|
|
QCOMPARE(server.serverError(), QAbstractSocket::UnsupportedSocketOperationError);
|
|
#ifndef Q_OS_SYMBIAN
|
|
//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
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------
|
|
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(int socketDescriptor)
|
|
{
|
|
// how a user woulddo it (qabstractsocketengine is not public)
|
|
unsigned long arg = 0;
|
|
#if defined(Q_OS_SYMBIAN)
|
|
arg = fcntl(socketDescriptor, F_GETFL, NULL);
|
|
arg &= (~O_NONBLOCK);
|
|
ok = ::fcntl(socketDescriptor, F_SETFL, arg) != -1;
|
|
#elif defined(Q_OS_WIN)
|
|
ok = ::ioctlsocket(socketDescriptor, FIONBIO, &arg) == 0;
|
|
::closesocket(socketDescriptor);
|
|
#else
|
|
ok = ::ioctl(socketDescriptor, FIONBIO, &arg) == 0;
|
|
::close(socketDescriptor);
|
|
#endif
|
|
}
|
|
};
|
|
|
|
void tst_QTcpServer::addressReusable()
|
|
{
|
|
#if defined(Q_OS_SYMBIAN) && defined(Q_CC_NOKIAX86)
|
|
QSKIP("Symbian: Emulator does not support process launching", SkipAll );
|
|
#endif
|
|
|
|
#if defined(QT_NO_PROCESS)
|
|
QSKIP("Qt was compiled with QT_NO_PROCESS", SkipAll);
|
|
#else
|
|
|
|
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", SkipAll);
|
|
}
|
|
}
|
|
#if defined(Q_OS_WINCE) || defined(Q_OS_SYMBIAN)
|
|
QString signalName = QString::fromLatin1("/test_signal.txt");
|
|
QFile::remove(signalName);
|
|
// The crashingServer process will crash once it gets a connection.
|
|
QProcess process;
|
|
process.start("crashingServer/crashingServer");
|
|
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;
|
|
process.start("crashingServer/crashingServer");
|
|
QVERIFY(process.waitForReadyRead(5000));
|
|
#endif
|
|
|
|
QTcpSocket socket;
|
|
socket.connectToHost(QHostAddress::LocalHost, 49199);
|
|
QVERIFY(socket.waitForConnected(5000));
|
|
|
|
QVERIFY(process.waitForFinished(5000));
|
|
|
|
// Give the system some time.
|
|
QTest::qSleep(10);
|
|
|
|
QTcpServer server;
|
|
QVERIFY(server.listen(QHostAddress::LocalHost, 49199));
|
|
#endif
|
|
}
|
|
|
|
void tst_QTcpServer::setNewSocketDescriptorBlocking()
|
|
{
|
|
#ifdef Q_OS_SYMBIAN
|
|
QSKIP("open C ioctls on Qt sockets not supported", SkipAll);
|
|
#else
|
|
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", SkipAll);
|
|
}
|
|
}
|
|
SeverWithBlockingSockets server;
|
|
QVERIFY(server.listen());
|
|
|
|
QTcpSocket socket;
|
|
socket.connectToHost(QHostAddress::LocalHost, server.serverPort());
|
|
QVERIFY(server.waitForNewConnection(5000));
|
|
QVERIFY(server.ok);
|
|
#endif
|
|
}
|
|
|
|
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"));
|
|
}
|
|
|
|
QTEST_MAIN(tst_QTcpServer)
|
|
#include "tst_qtcpserver.moc"
|