/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Copyright (C) 2017 Intel Corporation. ** 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 // To prevent windows system header files from re-defining min/max #define NOMINMAX 1 #if defined(_WIN32) #include #else #include #include #include #include #define SOCKET int #define INVALID_SOCKET -1 #endif #include #include #include #include #include #include #include #include #include #include #include #if QT_CONFIG(process) # include #endif #include #include #include #include #ifndef QT_NO_SSL #include #endif #include #include #include #include #include // RVCT compiles also unused inline methods # include #include #ifdef Q_OS_LINUX #include #include #include #include #endif #include #ifdef Q_OS_LINUX #include "private/qnativesocketengine_p.h" #endif // Q_OS_LINUX #include "private/qhostinfo_p.h" #include "../../../network-settings.h" QT_FORWARD_DECLARE_CLASS(QTcpSocket) class SocketPair; class tst_QTcpSocket : public QObject { Q_OBJECT public: tst_QTcpSocket(); static void enterLoop(int secs) { ++loopLevel; QTestEventLoop::instance().enterLoop(secs); --loopLevel; } static void exitLoop() { // Safe exit - if we aren't in an event loop, don't // exit one. if (loopLevel > 0) QTestEventLoop::instance().exitLoop(); } static bool timeout() { return QTestEventLoop::instance().timeout(); } public slots: void initTestCase_data(); void initTestCase(); void init(); void cleanup(); private slots: void socketsConstructedBeforeEventLoop(); void constructing(); void bind_data(); void bind(); void bindThenResolveHost_data(); void bindThenResolveHost(); void setInvalidSocketDescriptor(); void setSocketDescriptor(); void socketDescriptor(); void blockingIMAP(); void nonBlockingIMAP(); void hostNotFound(); void timeoutConnect_data(); void timeoutConnect(); void delayedClose(); void partialRead(); void unget(); void readAllAfterClose(); void openCloseOpenClose(); void connectDisconnectConnectDisconnect(); void disconnectWhileConnecting_data(); void disconnectWhileConnecting(); void disconnectWhileConnectingNoEventLoop_data(); void disconnectWhileConnectingNoEventLoop(); void disconnectWhileLookingUp_data(); void disconnectWhileLookingUp(); void downloadBigFile(); void readLine(); void readLineString(); void readChunks(); void waitForBytesWritten(); void waitForBytesWrittenMinusOne(); void waitForReadyRead(); void waitForReadyReadMinusOne(); void flush(); void synchronousApi(); void dontCloseOnTimeout(); void recursiveReadyRead(); void atEnd(); void socketInAThread(); void socketsInThreads(); void waitForReadyReadInASlot(); void remoteCloseError(); void nestedEventLoopInErrorSlot(); void connectToHostError_data(); void connectToHostError(); void waitForConnectedInHostLookupSlot(); void waitForConnectedInHostLookupSlot2(); void readyReadSignalsAfterWaitForReadyRead(); #ifdef Q_OS_LINUX void linuxKernelBugLocalSocket(); #endif void abortiveClose(); void localAddressEmptyOnBSD(); void zeroAndMinusOneReturns(); void connectionRefused(); void suddenRemoteDisconnect_data(); void suddenRemoteDisconnect(); void connectToMultiIP(); void moveToThread0(); void increaseReadBufferSize(); void increaseReadBufferSizeFromSlot(); void taskQtBug5799ConnectionErrorWaitForConnected(); void taskQtBug5799ConnectionErrorEventLoop(); void taskQtBug7054TimeoutErrorResetting(); #ifndef QT_NO_NETWORKPROXY void invalidProxy_data(); void invalidProxy(); void proxyFactory_data(); void proxyFactory(); #endif // !QT_NO_NETWORKPROXY void qtbug14268_peek(); void setSocketOption(); void clientSendDataOnDelayedDisconnect(); void serverDisconnectWithBuffered(); void socketDiscardDataInWriteMode(); void writeOnReadBufferOverflow(); void readNotificationsAfterBind(); protected slots: void nonBlockingIMAP_hostFound(); void nonBlockingIMAP_connected(); void nonBlockingIMAP_closed(); void nonBlockingIMAP_readyRead(); void nonBlockingIMAP_bytesWritten(qint64); void readRegularFile_readyRead(); void exitLoopSlot(); void downloadBigFileSlot(); void recursiveReadyReadSlot(); void waitForReadyReadInASlotSlot(); void enterLoopSlot(); void hostLookupSlot(); void abortiveClose_abortSlot(); void remoteCloseErrorSlot(); #ifndef QT_NO_NETWORKPROXY void proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *auth); #endif void earlySocketBytesSent(qint64 bytes); void earlySocketReadyRead(); void slotIncreaseReadBufferSizeReadyRead(); private: QByteArray expectedReplyIMAP(); void fetchExpectedReplyIMAP(); QTcpSocket *newSocket() const; QTcpSocket *nonBlockingIMAP_socket; QStringList nonBlockingIMAP_data; qint64 nonBlockingIMAP_totalWritten; QTcpSocket *tmpSocket; qint64 bytesAvailable; qint64 expectedLength; bool readingBody; QByteArray expectedReplyIMAP_cached; mutable int proxyAuthCalled; static int loopLevel; SocketPair *earlyConstructedSockets; int earlyBytesWrittenCount; int earlyReadyReadCount; QString stressTestDir; QString firstFailName; QHostInfo firstFailInfo; }; enum ProxyTests { NoProxy = 0x00, Socks5Proxy = 0x01, HttpProxy = 0x02, TypeMask = 0x0f, NoAuth = 0x00, AuthBasic = 0x10, AuthNtlm = 0x20, AuthMask = 0xf0 }; int tst_QTcpSocket::loopLevel = 0; class SocketPair: public QObject { Q_OBJECT public: QTcpSocket *endPoints[2]; SocketPair(QObject *parent = nullptr) : QObject(parent) { endPoints[0] = endPoints[1] = 0; } bool create() { QTcpServer server; server.listen(); QTcpSocket *active = new QTcpSocket(this); active->connectToHost("127.0.0.1", server.serverPort()); if (!active->waitForConnected(1000)) return false; if (!server.waitForNewConnection(1000)) return false; QTcpSocket *passive = server.nextPendingConnection(); passive->setParent(this); endPoints[0] = active; endPoints[1] = passive; return true; } }; tst_QTcpSocket::tst_QTcpSocket() : firstFailName("qt-test-server-first-fail") { tmpSocket = 0; //This code relates to the socketsConstructedBeforeEventLoop test case earlyConstructedSockets = new SocketPair; QVERIFY(earlyConstructedSockets->create()); earlyBytesWrittenCount = 0; earlyReadyReadCount = 0; connect(earlyConstructedSockets->endPoints[0], SIGNAL(readyRead()), this, SLOT(earlySocketReadyRead())); connect(earlyConstructedSockets->endPoints[1], SIGNAL(bytesWritten(qint64)), this, SLOT(earlySocketBytesSent(qint64))); earlyConstructedSockets->endPoints[1]->write("hello work"); firstFailInfo.setAddresses(QList() << QHostAddress("224.0.0.0") << QtNetworkSettings::httpServerIp()); } void tst_QTcpSocket::initTestCase_data() { QTest::addColumn("setProxy"); QTest::addColumn("proxyType"); QTest::addColumn("ssl"); QTest::newRow("WithoutProxy") << false << 0 << false; QTest::newRow("WithSocks5Proxy") << true << int(Socks5Proxy) << false; QTest::newRow("WithSocks5ProxyAuth") << true << int(Socks5Proxy | AuthBasic) << false; QTest::newRow("WithHttpProxy") << true << int(HttpProxy) << false; QTest::newRow("WithHttpProxyBasicAuth") << true << int(HttpProxy | AuthBasic) << false; // QTest::newRow("WithHttpProxyNtlmAuth") << true << int(HttpProxy | AuthNtlm) << false; #ifndef QT_NO_SSL QTest::newRow("WithoutProxy SSL") << false << 0 << true; QTest::newRow("WithSocks5Proxy SSL") << true << int(Socks5Proxy) << true; QTest::newRow("WithSocks5AuthProxy SSL") << true << int(Socks5Proxy | AuthBasic) << true; QTest::newRow("WithHttpProxy SSL") << true << int(HttpProxy) << true; QTest::newRow("WithHttpProxyBasicAuth SSL") << true << int(HttpProxy | AuthBasic) << true; // QTest::newRow("WithHttpProxyNtlmAuth SSL") << true << int(HttpProxy | AuthNtlm) << true; #endif stressTestDir = QFINDTESTDATA("stressTest"); QVERIFY2(!stressTestDir.isEmpty(), qPrintable( QString::fromLatin1("Couldn't find stressTest dir starting from %1.").arg(QDir::currentPath()))); } void tst_QTcpSocket::initTestCase() { #ifdef QT_TEST_SERVER QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::httpServerName(), 80)); QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::httpProxyServerName(), 3128)); QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::imapServerName(), 143)); //QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::firewallServerName(), 1357)); QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::socksProxyServerName(), 1080)); QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::ftpServerName(), 21)); QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::ftpProxyServerName(), 2121)); #else if (!QtNetworkSettings::verifyTestNetworkSettings()) QSKIP("No network test server available"); #endif } void tst_QTcpSocket::init() { QFETCH_GLOBAL(bool, setProxy); if (setProxy) { #ifndef QT_NO_NETWORKPROXY QFETCH_GLOBAL(int, proxyType); QList socks5Addresses = QHostInfo::fromName(QtNetworkSettings::socksProxyServerName()).addresses(); QList httpProxyAddresses = QHostInfo::fromName(QtNetworkSettings::httpProxyServerName()).addresses(); QVERIFY2(socks5Addresses.count() > 0, "failed to get ip address for SOCKS5 proxy server"); QVERIFY2(httpProxyAddresses.count() > 0, "failed to get ip address for HTTP proxy server"); QString socks5Address = socks5Addresses.first().toString(); QString httpProxyAddress = httpProxyAddresses.first().toString(); QNetworkProxy proxy; switch (proxyType) { case Socks5Proxy: proxy = QNetworkProxy(QNetworkProxy::Socks5Proxy, socks5Address, 1080); break; case Socks5Proxy | AuthBasic: proxy = QNetworkProxy(QNetworkProxy::Socks5Proxy, socks5Address, 1081); break; case HttpProxy | NoAuth: proxy = QNetworkProxy(QNetworkProxy::HttpProxy, httpProxyAddress, 3128); break; case HttpProxy | AuthBasic: proxy = QNetworkProxy(QNetworkProxy::HttpProxy, httpProxyAddress, 3129); break; case HttpProxy | AuthNtlm: proxy = QNetworkProxy(QNetworkProxy::HttpProxy, httpProxyAddress, 3130); break; } QNetworkProxy::setApplicationProxy(proxy); #else // !QT_NO_NETWORKPROXY QSKIP("No proxy support"); #endif // QT_NO_NETWORKPROXY } qt_qhostinfo_clear_cache(); qt_qhostinfo_cache_inject(firstFailName, firstFailInfo); } QTcpSocket *tst_QTcpSocket::newSocket() const { QTcpSocket *socket; #ifndef QT_NO_SSL QFETCH_GLOBAL(bool, ssl); socket = ssl ? new QSslSocket : new QTcpSocket; #else socket = new QTcpSocket; #endif proxyAuthCalled = 0; connect(socket, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), Qt::DirectConnection); return socket; } void tst_QTcpSocket::cleanup() { #ifndef QT_NO_NETWORKPROXY QNetworkProxy::setApplicationProxy(QNetworkProxy::DefaultProxy); #endif } #ifndef QT_NO_NETWORKPROXY void tst_QTcpSocket::proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *auth) { ++proxyAuthCalled; auth->setUser("qsockstest"); auth->setPassword("password"); } #endif // !QT_NO_NETWORKPROXY //---------------------------------------------------------------------------------- void tst_QTcpSocket::socketsConstructedBeforeEventLoop() { QFETCH_GLOBAL(bool, setProxy); QFETCH_GLOBAL(bool, ssl); if (setProxy || ssl) return; //This test checks that sockets constructed before QCoreApplication::exec() still emit signals //see construction code in the tst_QTcpSocket constructor enterLoop(3); QCOMPARE(earlyBytesWrittenCount, 1); QCOMPARE(earlyReadyReadCount, 1); earlyConstructedSockets->endPoints[0]->close(); earlyConstructedSockets->endPoints[1]->close(); } void tst_QTcpSocket::earlySocketBytesSent(qint64 /* bytes */) { earlyBytesWrittenCount++; } void tst_QTcpSocket::earlySocketReadyRead() { earlyReadyReadCount++; } //---------------------------------------------------------------------------------- void tst_QTcpSocket::constructing() { QTcpSocket *socket = newSocket(); // Check the initial state of the QTcpSocket. QCOMPARE(socket->state(), QTcpSocket::UnconnectedState); QVERIFY(socket->isSequential()); QVERIFY(!socket->isOpen()); QVERIFY(!socket->isValid()); QCOMPARE(socket->socketType(), QTcpSocket::TcpSocket); char c; QCOMPARE(socket->getChar(&c), false); QCOMPARE((int) socket->bytesAvailable(), 0); QCOMPARE(socket->canReadLine(), false); QCOMPARE(socket->readLine(), QByteArray()); QCOMPARE(socket->socketDescriptor(), (qintptr)-1); QCOMPARE((int) socket->localPort(), 0); QCOMPARE(socket->localAddress(), QHostAddress()); QCOMPARE((int) socket->peerPort(), 0); QCOMPARE(socket->peerAddress(), QHostAddress()); QCOMPARE(socket->readChannelCount(), 0); QCOMPARE(socket->writeChannelCount(), 0); QCOMPARE(socket->error(), QTcpSocket::UnknownSocketError); QCOMPARE(socket->errorString(), QString("Unknown error")); // Check the state of the socket layer? delete socket; } //---------------------------------------------------------------------------------- void tst_QTcpSocket::bind_data() { QTest::addColumn("stringAddr"); QTest::addColumn("port"); QTest::addColumn("successExpected"); QTest::addColumn("stringExpectedLocalAddress"); bool testIpv6 = false; // iterate all interfaces, add all addresses on them as test data QList interfaces = QNetworkInterface::allInterfaces(); foreach (const QNetworkInterface &netinterface, interfaces) { if (!netinterface.isValid()) continue; foreach (const QNetworkAddressEntry &entry, netinterface.addressEntries()) { if (entry.ip().isInSubnet(QHostAddress::parseSubnet("fe80::/10")) || entry.ip().isInSubnet(QHostAddress::parseSubnet("169.254/16"))) continue; // link-local bind will fail, at least on Linux, so skip it. QString ip(entry.ip().toString()); QTest::addRow("%s:0", ip.toLatin1().constData()) << ip << 0 << true << ip; if (!testIpv6 && entry.ip().protocol() == QAbstractSocket::IPv6Protocol) testIpv6 = true; } } // test binding to localhost QTest::newRow("0.0.0.0:0") << "0.0.0.0" << 0 << true << "0.0.0.0"; if (testIpv6) QTest::newRow("[::]:0") << "::" << 0 << true << "::"; // and binding with a port number... // Since we want to test that we got the port number we asked for, we need a random port number. // We use random in case a previous run of the test left the port lingering open. // -1 indicates "random port" QTest::newRow("0.0.0.0:randomport") << "0.0.0.0" << -1 << true << "0.0.0.0"; if (testIpv6) QTest::newRow("[::]:randomport") << "::" << -1 << true << "::"; // additionally, try bind to known-bad addresses, and make sure this doesn't work // these ranges are guaranteed to be reserved for 'documentation purposes', // and thus, should be unused in the real world. Not that I'm assuming the // world is full of competent administrators, or anything. QStringList knownBad; knownBad << "198.51.100.1"; knownBad << "2001:0DB8::1"; foreach (const QString &badAddress, knownBad) { QTest::addRow("%s:0", badAddress.toLatin1().constData()) << badAddress << 0 << false << QString(); } // try to bind to a privileged ports // we should fail if we're not root (unless the ports are in use!) #ifdef Q_OS_DARWIN // Alas, some quirk (starting from macOS 10.14): bind with port number 1 // fails with IPv4 (not IPv6 though, see below). QTest::newRow("127.0.0.1:1") << "127.0.0.1" << 1 << false << QString(); #else QTest::newRow("127.0.0.1:1") << "127.0.0.1" << 1 << QtNetworkSettings::canBindToLowPorts() << (QtNetworkSettings::canBindToLowPorts() ? "127.0.0.1" : QString()); #endif // Q_OS_DARWIN if (testIpv6) QTest::newRow("[::]:1") << "::" << 1 << QtNetworkSettings::canBindToLowPorts() << (QtNetworkSettings::canBindToLowPorts() ? "::" : QString()); } void tst_QTcpSocket::bind() { QFETCH_GLOBAL(bool, setProxy); if (setProxy) return; // QTBUG-22964 for proxies, QTBUG-29972 for QSKIP QFETCH(QString, stringAddr); QFETCH(int, port); QFETCH(bool, successExpected); QFETCH(QString, stringExpectedLocalAddress); QHostAddress addr(stringAddr); QHostAddress expectedLocalAddress(stringExpectedLocalAddress); QTcpSocket dummySocket; // used only to "use up" a file descriptor dummySocket.bind(); std::unique_ptr socket(newSocket()); quint16 boundPort; qintptr fd; if (successExpected) { bool randomPort = port == -1; int attemptsLeft = 5; // only used with randomPort or Windows do { if (randomPort) { // try to get a random port number // we do this to ensure we're not trying to bind to the same port as we've just used in // a previous run - race condition with the OS actually freeing the port port = QRandomGenerator::global()->generate() & USHRT_MAX; if (port < 1024) continue; } bool bindSuccess = socket->bind(addr, port); if (!bindSuccess && randomPort && socket->error() == QTcpSocket::AddressInUseError) { // we may have been unlucky and hit an already open port, so try another --attemptsLeft; continue; } QVERIFY2(bindSuccess, qPrintable(socket->errorString() + ", tried port " + QString::number(port))); break; } while (randomPort && attemptsLeft); QCOMPARE(socket->state(), QAbstractSocket::BoundState); QCOMPARE(socket->readChannelCount(), 0); QCOMPARE(socket->writeChannelCount(), 0); boundPort = socket->localPort(); if (port) QCOMPARE(int(boundPort), port); fd = socket->socketDescriptor(); QVERIFY(fd != qintptr(INVALID_SOCKET)); } else { QVERIFY(!socket->bind(addr, port)); QCOMPARE(socket->localPort(), quint16(0)); } QCOMPARE(socket->localAddress(), expectedLocalAddress); if (successExpected) { // try to use the socket and expect it to remain working QTcpServer server; QVERIFY(server.listen(addr)); // free up the file descriptor dummySocket.close(); QHostAddress remoteAddr = addr; if (addr == QHostAddress::AnyIPv4) remoteAddr = QHostAddress::LocalHost; else if (addr == QHostAddress::AnyIPv6) remoteAddr = QHostAddress::LocalHostIPv6; socket->connectToHost(remoteAddr, server.serverPort()); QVERIFY2(socket->waitForConnected(2000), socket->errorString().toLocal8Bit()); QVERIFY(server.waitForNewConnection(2000)); QTcpSocket *acceptedSocket = server.nextPendingConnection(); QCOMPARE(socket->localPort(), boundPort); QCOMPARE(acceptedSocket->peerPort(), boundPort); QCOMPARE(socket->localAddress(), remoteAddr); QCOMPARE(socket->socketDescriptor(), fd); #ifdef Q_OS_DARWIN // Normally, we don't see this problem: macOS sometimes does not // allow us to immediately re-use a port, thinking connection is // still alive. With fixed port 1 (we testing starting from // macOS 10.14), this problem shows, making the test flaky: // we run this 'bind' with port 1 several times (different // test cases) and the problem manifests itself as // "The bound address is already in use, tried port 1". QTestEventLoop cleanupHelper; auto client = socket.get(); connect(client, &QTcpSocket::disconnected, [&cleanupHelper, client](){ client->close(); cleanupHelper.exitLoop(); }); acceptedSocket->close(); cleanupHelper.enterLoopMSecs(100); #endif // Q_OS_DARWIN } } //---------------------------------------------------------------------------------- void tst_QTcpSocket::bindThenResolveHost_data() { QTest::addColumn("hostName"); QTest::newRow("ip-literal") << QtNetworkSettings::httpServerIp().toString(); QTest::newRow("name") << QtNetworkSettings::httpServerName(); QTest::newRow("first-fail") << firstFailName; } // similar to the previous test, but we'll connect to a host name that needs resolving void tst_QTcpSocket::bindThenResolveHost() { QFETCH_GLOBAL(bool, setProxy); if (setProxy) return; // doesn't make sense to test binding locally with proxies QFETCH(QString, hostName); QTcpSocket dummySocket; // used only to "use up" a file descriptor dummySocket.bind(); QTcpSocket *socket = newSocket(); QVERIFY2(socket->bind(QHostAddress(QHostAddress::AnyIPv4), 0), socket->errorString().toLocal8Bit()); QCOMPARE(socket->state(), QAbstractSocket::BoundState); quint16 boundPort = socket->localPort(); qintptr fd = socket->socketDescriptor(); QVERIFY(fd != quint16(INVALID_SOCKET)); dummySocket.close(); const quint16 port = 80; socket->connectToHost(hostName, port); // Additionally, initiate a delayed close before the socket connects // to ensure that we don't lose the socket engine in HostLookupState. // After a connection has been established, socket should send all // the pending data and close the socket engine automatically. QVERIFY(socket->putChar(0)); socket->close(); QVERIFY2(socket->waitForConnected(), (hostName.toLocal8Bit() + ": " + QByteArray::number(port) + ' ' + QtNetworkSettings::msgSocketError(*socket)).constData()); QCOMPARE(socket->localPort(), boundPort); QCOMPARE(socket->socketDescriptor(), fd); delete socket; } //---------------------------------------------------------------------------------- void tst_QTcpSocket::setInvalidSocketDescriptor() { QTcpSocket *socket = newSocket(); QCOMPARE(socket->socketDescriptor(), (qintptr)-1); QVERIFY(!socket->setSocketDescriptor(-5, QTcpSocket::UnconnectedState)); QCOMPARE(socket->socketDescriptor(), (qintptr)-1); QCOMPARE(socket->error(), QTcpSocket::UnsupportedSocketOperationError); delete socket; } //---------------------------------------------------------------------------------- void tst_QTcpSocket::setSocketDescriptor() { QFETCH_GLOBAL(bool, setProxy); if (setProxy) return; // this test doesn't make sense with proxies #ifdef Q_OS_WIN // need the dummy to ensure winsock is started QTcpSocket *dummy = newSocket(); dummy->connectToHost(QtNetworkSettings::imapServerName(), 143); QVERIFY(dummy->waitForConnected()); SOCKET sock = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (sock == INVALID_SOCKET) { qErrnoWarning(WSAGetLastError(), "INVALID_SOCKET"); } #else SOCKET sock = ::socket(AF_INET, SOCK_STREAM, 0); // artificially increase the value of sock SOCKET sock2 = ::fcntl(sock, F_DUPFD, sock + 50); ::close(sock); sock = sock2; #endif QVERIFY(sock != INVALID_SOCKET); QTcpSocket *socket = newSocket(); QVERIFY(socket->setSocketDescriptor(sock, QTcpSocket::UnconnectedState)); QCOMPARE(socket->socketDescriptor(), (qintptr)sock); qt_qhostinfo_clear_cache(); //avoid the HostLookupState being skipped due to address being in cache from previous test. socket->connectToHost(QtNetworkSettings::httpServerName(), 80); QCOMPARE(socket->state(), QTcpSocket::HostLookupState); QCOMPARE(socket->socketDescriptor(), (qintptr)sock); QVERIFY(socket->waitForConnected(10000)); QCOMPARE(socket->socketDescriptor(), (qintptr)sock); QCOMPARE(socket->readChannelCount(), 1); QCOMPARE(socket->writeChannelCount(), 1); delete socket; #ifdef Q_OS_WIN delete dummy; #endif } //---------------------------------------------------------------------------------- void tst_QTcpSocket::socketDescriptor() { QTcpSocket *socket = newSocket(); QCOMPARE(socket->socketDescriptor(), (qintptr)-1); socket->connectToHost(QtNetworkSettings::imapServerName(), 143); QVERIFY(socket->state() == QAbstractSocket::HostLookupState || socket->state() == QAbstractSocket::ConnectingState); QVERIFY(socket->waitForConnected(10000)); QCOMPARE(socket->state(), QAbstractSocket::ConnectedState); QVERIFY(socket->socketDescriptor() != -1); delete socket; } //---------------------------------------------------------------------------------- void tst_QTcpSocket::blockingIMAP() { QTcpSocket *socket = newSocket(); // Connect socket->connectToHost(QtNetworkSettings::imapServerName(), 143); QVERIFY(socket->waitForConnected(10000)); QCOMPARE(socket->state(), QTcpSocket::ConnectedState); QVERIFY(socket->isValid()); QCOMPARE(socket->readChannelCount(), 1); QCOMPARE(socket->writeChannelCount(), 1); // Read greeting QVERIFY(socket->waitForReadyRead(5000)); QString s = socket->readLine(); // only test if an OK was returned, to make the test compatible between different // IMAP server versions QCOMPARE(s.left(4).toLatin1().constData(), "* OK"); // Write NOOP QCOMPARE((int) socket->write("1 NOOP\r\n", 8), 8); QCOMPARE((int) socket->write("2 NOOP\r\n", 8), 8); if (!socket->canReadLine()) QVERIFY(socket->waitForReadyRead(5000)); // Read response s = socket->readLine(); QCOMPARE(s.toLatin1().constData(), "1 OK Completed\r\n"); // Write a third NOOP to verify that write doesn't clear the read buffer QCOMPARE((int) socket->write("3 NOOP\r\n", 8), 8); // Read second response if (!socket->canReadLine()) QVERIFY(socket->waitForReadyRead(5000)); s = socket->readLine(); QCOMPARE(s.toLatin1().constData(), "2 OK Completed\r\n"); // Read third response if (!socket->canReadLine()) QVERIFY(socket->waitForReadyRead(5000)); s = socket->readLine(); QCOMPARE(s.toLatin1().constData(), "3 OK Completed\r\n"); // Write LOGOUT QCOMPARE((int) socket->write("4 LOGOUT\r\n", 10), 10); if (!socket->canReadLine()) QVERIFY(socket->waitForReadyRead(5000)); // Read two lines of respose s = socket->readLine(); QCOMPARE(s.toLatin1().constData(), "* BYE LOGOUT received\r\n"); if (!socket->canReadLine()) QVERIFY(socket->waitForReadyRead(5000)); s = socket->readLine(); QCOMPARE(s.toLatin1().constData(), "4 OK Completed\r\n"); // Close the socket socket->close(); // Check that it's closed QCOMPARE(socket->state(), QTcpSocket::UnconnectedState); QCOMPARE(socket->readChannelCount(), 0); QCOMPARE(socket->writeChannelCount(), 0); delete socket; } //---------------------------------------------------------------------------------- void tst_QTcpSocket::hostNotFound() { QTcpSocket *socket = newSocket(); socket->connectToHost("nosuchserver.qt-project.org", 80); QVERIFY(!socket->waitForConnected()); QCOMPARE(socket->state(), QTcpSocket::UnconnectedState); #ifdef QT_TEST_SERVER QFETCH_GLOBAL(bool, setProxy); QFETCH_GLOBAL(int, proxyType); if (setProxy && (proxyType & HttpProxy) == HttpProxy) { QEXPECT_FAIL("", "QTBUG-73953: The version of Squid in the docker container behaves " "differently to the one in the network testing server, returning 503 " "when we expect 404", Continue); } #endif QCOMPARE(int(socket->error()), int(QTcpSocket::HostNotFoundError)); delete socket; } //---------------------------------------------------------------------------------- void tst_QTcpSocket::timeoutConnect_data() { QTest::addColumn("address"); QTest::newRow("host") << QtNetworkSettings::firewallServerName(); QTest::newRow("ip") << QtNetworkSettings::firewallServerIp().toString(); } void tst_QTcpSocket::timeoutConnect() { QFETCH(QString, address); QTcpSocket *socket = newSocket(); QElapsedTimer timer; timer.start(); // Port 1357 is configured to drop packets on the test server socket->connectToHost(address, 1357); QVERIFY(timer.elapsed() < 150); QVERIFY(!socket->waitForConnected(1000)); //200ms is too short when using SOCKS proxy authentication QCOMPARE(socket->state(), QTcpSocket::UnconnectedState); QCOMPARE(int(socket->error()), int(QTcpSocket::SocketTimeoutError)); QCOMPARE(socket->readChannelCount(), 0); QCOMPARE(socket->writeChannelCount(), 0); timer.start(); socket->connectToHost(address, 1357); QVERIFY(timer.elapsed() < 150); QTimer::singleShot(50, &QTestEventLoop::instance(), SLOT(exitLoop())); QTestEventLoop::instance().enterLoop(5); QVERIFY(!QTestEventLoop::instance().timeout()); QVERIFY(socket->state() == QTcpSocket::ConnectingState || socket->state() == QTcpSocket::HostLookupState); socket->abort(); QCOMPARE(socket->state(), QTcpSocket::UnconnectedState); QCOMPARE(socket->openMode(), QIODevice::NotOpen); delete socket; } //---------------------------------------------------------------------------------- void tst_QTcpSocket::nonBlockingIMAP() { QTcpSocket *socket = newSocket(); connect(socket, SIGNAL(hostFound()), SLOT(nonBlockingIMAP_hostFound())); connect(socket, SIGNAL(connected()), SLOT(nonBlockingIMAP_connected())); connect(socket, SIGNAL(disconnected()), SLOT(nonBlockingIMAP_closed())); connect(socket, SIGNAL(bytesWritten(qint64)), SLOT(nonBlockingIMAP_bytesWritten(qint64))); connect(socket, SIGNAL(readyRead()), SLOT(nonBlockingIMAP_readyRead())); nonBlockingIMAP_socket = socket; // Connect socket->connectToHost(QtNetworkSettings::imapServerName(), 143); QVERIFY(socket->state() == QTcpSocket::HostLookupState || socket->state() == QTcpSocket::ConnectingState); enterLoop(30); if (timeout()) { QFAIL("Timed out"); } if (socket->state() == QTcpSocket::ConnectingState) { enterLoop(30); if (timeout()) { QFAIL("Timed out"); } } QCOMPARE(socket->state(), QTcpSocket::ConnectedState); QCOMPARE(socket->readChannelCount(), 1); QCOMPARE(socket->writeChannelCount(), 1); enterLoop(30); if (timeout()) { QFAIL("Timed out"); } // Read greeting QVERIFY(!nonBlockingIMAP_data.isEmpty()); QCOMPARE(nonBlockingIMAP_data.at(0).left(4).toLatin1().constData(), "* OK"); nonBlockingIMAP_data.clear(); nonBlockingIMAP_totalWritten = 0; // Write NOOP QCOMPARE((int) socket->write("1 NOOP\r\n", 8), 8); enterLoop(30); if (timeout()) { QFAIL("Timed out"); } QCOMPARE(nonBlockingIMAP_totalWritten, 8); enterLoop(30); if (timeout()) { QFAIL("Timed out"); } // Read response QVERIFY(!nonBlockingIMAP_data.isEmpty()); QCOMPARE(nonBlockingIMAP_data.at(0).toLatin1().constData(), "1 OK Completed\r\n"); nonBlockingIMAP_data.clear(); nonBlockingIMAP_totalWritten = 0; // Write LOGOUT QCOMPARE((int) socket->write("2 LOGOUT\r\n", 10), 10); enterLoop(30); if (timeout()) { QFAIL("Timed out"); } QCOMPARE(nonBlockingIMAP_totalWritten, 10); // Wait for greeting enterLoop(30); if (timeout()) { QFAIL("Timed out"); } // Read two lines of respose QCOMPARE(nonBlockingIMAP_data.at(0).toLatin1().constData(), "* BYE LOGOUT received\r\n"); QCOMPARE(nonBlockingIMAP_data.at(1).toLatin1().constData(), "2 OK Completed\r\n"); nonBlockingIMAP_data.clear(); // Close the socket socket->close(); // Check that it's closed QCOMPARE(socket->state(), QTcpSocket::UnconnectedState); QCOMPARE(socket->readChannelCount(), 0); QCOMPARE(socket->writeChannelCount(), 0); delete socket; } void tst_QTcpSocket::nonBlockingIMAP_hostFound() { exitLoop(); } void tst_QTcpSocket::nonBlockingIMAP_connected() { exitLoop(); } void tst_QTcpSocket::nonBlockingIMAP_readyRead() { while (nonBlockingIMAP_socket->canReadLine()) nonBlockingIMAP_data.append(nonBlockingIMAP_socket->readLine()); exitLoop(); } void tst_QTcpSocket::nonBlockingIMAP_bytesWritten(qint64 written) { nonBlockingIMAP_totalWritten += written; exitLoop(); } void tst_QTcpSocket::nonBlockingIMAP_closed() { } //---------------------------------------------------------------------------------- void tst_QTcpSocket::delayedClose() { QTcpSocket *socket = newSocket(); connect(socket, SIGNAL(connected()), SLOT(nonBlockingIMAP_connected())); connect(socket, SIGNAL(disconnected()), SLOT(exitLoopSlot())); socket->connectToHost(QtNetworkSettings::imapServerName(), 143); enterLoop(30); if (timeout()) QFAIL("Timed out"); QCOMPARE(socket->state(), QTcpSocket::ConnectedState); QCOMPARE((int) socket->write("1 LOGOUT\r\n", 10), 10); // Add a huge bulk of data to be written after the logout // command. The server will shut down after receiving the LOGOUT, // so this data will not be read. But our close call should // schedule a delayed close because all the data can not be // written in one go. QCOMPARE((int) socket->write(QByteArray(100000, '\n'), 100000), 100000); socket->close(); QCOMPARE((int) socket->state(), (int) QTcpSocket::ClosingState); enterLoop(10); if (timeout()) QFAIL("Timed out"); QCOMPARE(socket->state(), QTcpSocket::UnconnectedState); delete socket; } //---------------------------------------------------------------------------------- QByteArray tst_QTcpSocket::expectedReplyIMAP() { if (expectedReplyIMAP_cached.isEmpty()) { fetchExpectedReplyIMAP(); } return expectedReplyIMAP_cached; } // Figure out how the current IMAP server responds void tst_QTcpSocket::fetchExpectedReplyIMAP() { QTcpSocket *socket = newSocket(); socket->connectToHost(QtNetworkSettings::imapServerName(), 143); QVERIFY2(socket->waitForConnected(10000), qPrintable(socket->errorString())); QVERIFY2(socket->state() == QTcpSocket::ConnectedState, qPrintable(socket->errorString())); QTRY_VERIFY(socket->canReadLine()); QByteArray greeting = socket->readLine(); delete socket; QVERIFY2(QtNetworkSettings::compareReplyIMAP(greeting), greeting.constData()); expectedReplyIMAP_cached = greeting; } //---------------------------------------------------------------------------------- void tst_QTcpSocket::partialRead() { QTcpSocket *socket = newSocket(); socket->connectToHost(QtNetworkSettings::imapServerName(), 143); QVERIFY(socket->waitForConnected(10000)); QCOMPARE(socket->state(), QTcpSocket::ConnectedState); char buf[512]; QByteArray greeting = expectedReplyIMAP(); QVERIFY(!greeting.isEmpty()); for (int i = 0; i < 10; i += 2) { while (socket->bytesAvailable() < 2) QVERIFY(socket->waitForReadyRead(5000)); QVERIFY(socket->read(buf, 2) == 2); buf[2] = '\0'; QCOMPARE((char *)buf, greeting.mid(i, 2).data()); } delete socket; } //---------------------------------------------------------------------------------- void tst_QTcpSocket::unget() { QTcpSocket *socket = newSocket(); socket->connectToHost(QtNetworkSettings::imapServerName(), 143); QVERIFY(socket->waitForConnected(10000)); QCOMPARE(socket->state(), QTcpSocket::ConnectedState); char buf[512]; QByteArray greeting = expectedReplyIMAP(); QVERIFY(!greeting.isEmpty()); for (int i = 0; i < 10; i += 2) { while (socket->bytesAvailable() < 2) QVERIFY(socket->waitForReadyRead(10000)); int bA = socket->bytesAvailable(); QVERIFY(socket->read(buf, 2) == 2); buf[2] = '\0'; QCOMPARE((char *)buf, greeting.mid(i, 2).data()); QCOMPARE((int)socket->bytesAvailable(), bA - 2); socket->ungetChar(buf[1]); socket->ungetChar(buf[0]); QCOMPARE((int)socket->bytesAvailable(), bA); QVERIFY(socket->read(buf, 2) == 2); buf[2] = '\0'; QCOMPARE((char *)buf, greeting.mid(i, 2).data()); } delete socket; } //---------------------------------------------------------------------------------- void tst_QTcpSocket::readRegularFile_readyRead() { exitLoop(); } //---------------------------------------------------------------------------------- void tst_QTcpSocket::readAllAfterClose() { QTcpSocket *socket = newSocket(); socket->connectToHost(QtNetworkSettings::imapServerName(), 143); connect(socket, SIGNAL(readyRead()), SLOT(readRegularFile_readyRead())); enterLoop(10); if (timeout()) QFAIL("Network operation timed out"); socket->close(); QByteArray array = socket->readAll(); QCOMPARE(array.size(), 0); delete socket; } //---------------------------------------------------------------------------------- void tst_QTcpSocket::openCloseOpenClose() { QTcpSocket *socket = newSocket(); for (int i = 0; i < 3; ++i) { QCOMPARE(socket->state(), QTcpSocket::UnconnectedState); QCOMPARE(int(socket->openMode()), int(QIODevice::NotOpen)); QVERIFY(socket->isSequential()); QVERIFY(!socket->isOpen()); QCOMPARE(socket->socketType(), QTcpSocket::TcpSocket); char c; QCOMPARE(socket->getChar(&c), false); QCOMPARE((int) socket->bytesAvailable(), 0); QCOMPARE(socket->canReadLine(), false); QCOMPARE(socket->readLine(), QByteArray()); QCOMPARE(socket->socketDescriptor(), (qintptr)-1); QCOMPARE((int) socket->localPort(), 0); QCOMPARE(socket->localAddress(), QHostAddress()); QCOMPARE((int) socket->peerPort(), 0); QCOMPARE(socket->peerAddress(), QHostAddress()); QCOMPARE(socket->error(), QTcpSocket::UnknownSocketError); QCOMPARE(socket->errorString(), QString("Unknown error")); QCOMPARE(socket->state(), QTcpSocket::UnconnectedState); socket->connectToHost(QtNetworkSettings::imapServerName(), 143); QVERIFY(socket->waitForConnected(10000)); socket->close(); } delete socket; } //---------------------------------------------------------------------------------- void tst_QTcpSocket::connectDisconnectConnectDisconnect() { QTcpSocket *socket = newSocket(); for (int i = 0; i < 3; ++i) { QCOMPARE(socket->state(), QTcpSocket::UnconnectedState); QCOMPARE(socket->socketType(), QTcpSocket::TcpSocket); QCOMPARE(socket->socketDescriptor(), qintptr(-1)); QCOMPARE(int(socket->localPort()), 0); QCOMPARE(socket->localAddress(), QHostAddress()); QCOMPARE(int(socket->peerPort()), 0); QCOMPARE(socket->peerAddress(), QHostAddress()); socket->connectToHost(QtNetworkSettings::imapServerName(), 143); QVERIFY(socket->waitForReadyRead(10000)); QCOMPARE(QString::fromLatin1(socket->read(4)), QString("* OK")); socket->disconnectFromHost(); if (socket->state() != QTcpSocket::UnconnectedState) QVERIFY(socket->waitForDisconnected(10000)); QCOMPARE(int(socket->openMode()), int(QIODevice::ReadWrite)); } delete socket; } //---------------------------------------------------------------------------------- void tst_QTcpSocket::disconnectWhileConnecting_data() { QTest::addColumn("data"); QTest::addColumn("closeDirectly"); QTest::newRow("without-data") << QByteArray() << false; QTest::newRow("without-data+close") << QByteArray() << true; QTest::newRow("with-data") << QByteArray("Hello, world!") << false; QTest::newRow("with-data+close") << QByteArray("Hello, world!") << true; QByteArray bigData(1024*1024, '@'); QTest::newRow("with-big-data") << bigData << false; QTest::newRow("with-big-data+close") << bigData << true; } void tst_QTcpSocket::disconnectWhileConnecting() { QFETCH(QByteArray, data); QFETCH_GLOBAL(bool, setProxy); if (setProxy) return; //proxy not useful for localhost test case QTcpServer server; QVERIFY(server.listen(QHostAddress::LocalHost)); // proceed to the connect-write-disconnect QTcpSocket *socket = newSocket(); socket->connectToHost("127.0.0.1", server.serverPort()); if (!data.isEmpty()) socket->write(data); if (socket->state() == QAbstractSocket::ConnectedState) QSKIP("localhost connections are immediate, test case is invalid"); QFETCH(bool, closeDirectly); if (closeDirectly) { socket->close(); QCOMPARE(int(socket->openMode()), int(QIODevice::NotOpen)); } else { socket->disconnectFromHost(); } connect(socket, SIGNAL(disconnected()), SLOT(exitLoopSlot())); enterLoop(10); QVERIFY2(!timeout(), "Network timeout"); QCOMPARE(socket->state(), QAbstractSocket::UnconnectedState); if (!closeDirectly) { QCOMPARE(int(socket->openMode()), int(QIODevice::ReadWrite)); socket->close(); } QCOMPARE(int(socket->openMode()), int(QIODevice::NotOpen)); // accept the other side and verify that it was sent properly: QVERIFY(server.hasPendingConnections() || server.waitForNewConnection(0)); QTcpSocket *othersocket = server.nextPendingConnection(); if (othersocket->state() != QAbstractSocket::UnconnectedState) QVERIFY2(othersocket->waitForDisconnected(10000), "Network timeout"); QCOMPARE(othersocket->state(), QAbstractSocket::UnconnectedState); QCOMPARE(othersocket->readAll(), data); delete socket; delete othersocket; } //---------------------------------------------------------------------------------- class ReceiverThread: public QThread { QTcpServer *server; public: int serverPort; bool ok; QByteArray receivedData; volatile bool quit; ReceiverThread() : server(0), ok(false), quit(false) { } ~ReceiverThread() { } bool listen() { server = new QTcpServer; if (!server->listen(QHostAddress::LocalHost)) return false; serverPort = server->serverPort(); server->moveToThread(this); return true; } static void cleanup(void *ptr) { ReceiverThread* self = reinterpret_cast(ptr); self->quit = true; self->wait(30000); delete self; } protected: void run() override { bool timedOut = false; while (!quit) { if (server->waitForNewConnection(500, &timedOut)) break; if (!timedOut) return; } QTcpSocket *socket = server->nextPendingConnection(); while (!quit) { if (socket->waitForDisconnected(500)) break; if (socket->error() != QAbstractSocket::SocketTimeoutError) return; } if (!quit) { receivedData = socket->readAll(); ok = true; } delete socket; delete server; server = 0; } }; void tst_QTcpSocket::disconnectWhileConnectingNoEventLoop_data() { disconnectWhileConnecting_data(); } void tst_QTcpSocket::disconnectWhileConnectingNoEventLoop() { QFETCH(QByteArray, data); QFETCH_GLOBAL(bool, setProxy); if (setProxy) return; //proxy not useful for localhost test case QScopedPointer thread (new ReceiverThread); QVERIFY(thread->listen()); thread->start(); // proceed to the connect-write-disconnect QTcpSocket *socket = newSocket(); socket->connectToHost("127.0.0.1", thread->serverPort); if (!data.isEmpty()) socket->write(data); if (socket->state() == QAbstractSocket::ConnectedState) QSKIP("localhost connections are immediate, test case is invalid"); QFETCH(bool, closeDirectly); if (closeDirectly) { socket->close(); QCOMPARE(int(socket->openMode()), int(QIODevice::NotOpen)); } else { socket->disconnectFromHost(); } QVERIFY2(socket->waitForDisconnected(10000), "Network timeout"); QCOMPARE(socket->state(), QAbstractSocket::UnconnectedState); if (!closeDirectly) { QCOMPARE(int(socket->openMode()), int(QIODevice::ReadWrite)); socket->close(); } QCOMPARE(int(socket->openMode()), int(QIODevice::NotOpen)); delete socket; // check if the other side received everything ok QVERIFY(thread->wait(30000)); QVERIFY(thread->ok); QCOMPARE(thread->receivedData, data); } //---------------------------------------------------------------------------------- void tst_QTcpSocket::disconnectWhileLookingUp_data() { QTest::addColumn("doClose"); QTest::newRow("disconnect") << false; QTest::newRow("close") << true; } void tst_QTcpSocket::disconnectWhileLookingUp() { QFETCH_GLOBAL(bool, setProxy); if (setProxy) return; // we let the proxies do the lookup now // just connect and disconnect, then make sure nothing weird happened QTcpSocket *socket = newSocket(); socket->connectToHost(QtNetworkSettings::ftpServerName(), 21); // check that connect is in progress QVERIFY(socket->state() != QAbstractSocket::UnconnectedState); QFETCH(bool, doClose); if (doClose) { socket->close(); QCOMPARE(socket->openMode(), QIODevice::NotOpen); } else { socket->disconnectFromHost(); QCOMPARE(socket->openMode(), QIODevice::ReadWrite); QVERIFY(socket->waitForDisconnected(5000)); } // let anything queued happen QEventLoop loop; // If 'doClose' is false then we called '::waitForDisconnected' earlier, meaning // we are already 'Unconnected'. So we don't need to wait for any potentially slow host lookups. QTimer::singleShot(doClose ? 4000 : 50, &loop, SLOT(quit())); connect(socket, &QTcpSocket::stateChanged, [&loop](QAbstractSocket::SocketState state) { if (state == QAbstractSocket::UnconnectedState) loop.exit(); // we don't need to wait for the timer to expire; we're done. }); loop.exec(); // recheck if (doClose) { QCOMPARE(socket->openMode(), QIODevice::NotOpen); } else { QCOMPARE(socket->openMode(), QIODevice::ReadWrite); } QCOMPARE(socket->state(), QAbstractSocket::UnconnectedState); } //---------------------------------------------------------------------------------- void tst_QTcpSocket::downloadBigFile() { if (tmpSocket) delete tmpSocket; tmpSocket = newSocket(); connect(tmpSocket, SIGNAL(connected()), SLOT(exitLoopSlot())); connect(tmpSocket, SIGNAL(readyRead()), SLOT(downloadBigFileSlot())); connect(tmpSocket, SIGNAL(disconnected()), SLOT(exitLoopSlot())); tmpSocket->connectToHost(QtNetworkSettings::httpServerName(), 80); enterLoop(30); if (timeout()) { delete tmpSocket; tmpSocket = 0; QFAIL("Network operation timed out"); } QByteArray hostName = QtNetworkSettings::httpServerName().toLatin1(); QCOMPARE(tmpSocket->state(), QAbstractSocket::ConnectedState); QVERIFY(tmpSocket->write("GET /qtest/mediumfile HTTP/1.0\r\n") > 0); QVERIFY(tmpSocket->write("HOST: ") > 0); QVERIFY(tmpSocket->write(hostName.data()) > 0); QVERIFY(tmpSocket->write("\r\n") > 0); QVERIFY(tmpSocket->write("\r\n") > 0); bytesAvailable = 0; expectedLength = 0; readingBody = false; QElapsedTimer stopWatch; stopWatch.start(); enterLoop(600); if (timeout()) { delete tmpSocket; tmpSocket = 0; if (bytesAvailable > 0) qDebug("Slow Connection, only downloaded %ld of %d", long(bytesAvailable), 10000281); QFAIL("Network operation timed out"); } QCOMPARE(bytesAvailable, expectedLength); qDebug("\t\t%.1fMB/%.1fs: %.1fMB/s", bytesAvailable / (1024.0 * 1024.0), stopWatch.elapsed() / 1024.0, (bytesAvailable / (stopWatch.elapsed() / 1000.0)) / (1024 * 1024)); delete tmpSocket; tmpSocket = 0; } //---------------------------------------------------------------------------------- void tst_QTcpSocket::exitLoopSlot() { exitLoop(); } //---------------------------------------------------------------------------------- void tst_QTcpSocket::downloadBigFileSlot() { if (!readingBody) { while (tmpSocket->canReadLine()) { QByteArray array = tmpSocket->readLine(); if (array.startsWith("Content-Length")) expectedLength = array.simplified().split(' ').at(1).toInt(); if (array == "\r\n") { readingBody = true; break; } } } if (readingBody) { bytesAvailable += tmpSocket->readAll().size(); if (bytesAvailable == expectedLength) exitLoop(); } } //---------------------------------------------------------------------------------- void tst_QTcpSocket::readLine() { QTcpSocket *socket = newSocket(); socket->connectToHost(QtNetworkSettings::imapServerName(), 143); QVERIFY(socket->waitForConnected(5000)); while (!socket->canReadLine()) QVERIFY(socket->waitForReadyRead(10000)); char buffer[1024]; qint64 linelen = socket->readLine(buffer, sizeof(buffer)); QVERIFY(linelen >= 3); QVERIFY(linelen < 1024); QByteArray reply = QByteArray::fromRawData(buffer, linelen); QCOMPARE((int) buffer[linelen-2], (int) '\r'); QCOMPARE((int) buffer[linelen-1], (int) '\n'); QCOMPARE((int) buffer[linelen], (int) '\0'); QVERIFY2(QtNetworkSettings::compareReplyIMAP(reply), reply.constData()); QCOMPARE(socket->write("1 NOOP\r\n"), qint64(8)); while (socket->bytesAvailable() < 10) QVERIFY(socket->waitForReadyRead(10000)); QCOMPARE(socket->readLine(buffer, 11), qint64(10)); QCOMPARE((const char *)buffer, "1 OK Compl"); while (socket->bytesAvailable() < 6) QVERIFY(socket->waitForReadyRead(10000)); QCOMPARE(socket->readLine(buffer, 11), qint64(6)); QCOMPARE((const char *)buffer, "eted\r\n"); QVERIFY(!socket->waitForReadyRead(100)); QCOMPARE(socket->readLine(buffer, sizeof(buffer)), qint64(0)); QVERIFY(socket->error() == QAbstractSocket::SocketTimeoutError || socket->error() == QAbstractSocket::RemoteHostClosedError); QCOMPARE(socket->bytesAvailable(), qint64(0)); socket->close(); QCOMPARE(socket->readLine(buffer, sizeof(buffer)), qint64(-1)); delete socket; } //---------------------------------------------------------------------------------- void tst_QTcpSocket::readLineString() { QTcpSocket *socket = newSocket(); socket->connectToHost(QtNetworkSettings::imapServerName(), 143); QVERIFY(socket->waitForReadyRead(10000)); QByteArray arr = socket->readLine(); QVERIFY2(QtNetworkSettings::compareReplyIMAP(arr), arr.constData()); delete socket; } //---------------------------------------------------------------------------------- void tst_QTcpSocket::readChunks() { QTcpSocket *socket = newSocket(); socket->connectToHost(QtNetworkSettings::imapServerName(), 143); QVERIFY(socket->waitForConnected(10000)); QVERIFY(socket->waitForReadyRead(5000)); char buf[4096]; memset(buf, '@', sizeof(buf)); qint64 dataLength = socket->read(buf, sizeof(buf)); QVERIFY(dataLength > 0); QCOMPARE(buf[dataLength - 2], '\r'); QCOMPARE(buf[dataLength - 1], '\n'); QCOMPARE(buf[dataLength], '@'); delete socket; } //---------------------------------------------------------------------------------- void tst_QTcpSocket::waitForBytesWritten() { QTcpSocket *socket = newSocket(); socket->connectToHost(QtNetworkSettings::httpServerName(), 80); QVERIFY(socket->waitForConnected(10000)); socket->write("GET / HTTP/1.0\r\n\r\n"); qint64 toWrite = socket->bytesToWrite(); QVERIFY(socket->waitForBytesWritten(5000)); QVERIFY(toWrite > socket->bytesToWrite()); delete socket; } //---------------------------------------------------------------------------------- void tst_QTcpSocket::waitForBytesWrittenMinusOne() { #ifdef Q_OS_WIN QSKIP("QTBUG-24451 - indefinite wait may hang"); #endif QTcpSocket *socket = newSocket(); socket->connectToHost(QtNetworkSettings::httpServerName(), 80); QVERIFY(socket->waitForConnected(10000)); socket->write("GET / HTTP/1.0\r\n\r\n"); qint64 toWrite = socket->bytesToWrite(); QVERIFY(socket->waitForBytesWritten(-1)); QVERIFY(toWrite > socket->bytesToWrite()); delete socket; } //---------------------------------------------------------------------------------- void tst_QTcpSocket::waitForReadyRead() { QTcpSocket *socket = newSocket(); socket->connectToHost(QtNetworkSettings::httpServerName(), 80); socket->write("GET / HTTP/1.0\r\n\r\n"); QVERIFY(socket->waitForReadyRead(5000)); delete socket; } //---------------------------------------------------------------------------------- void tst_QTcpSocket::waitForReadyReadMinusOne() { #ifdef Q_OS_WIN QSKIP("QTBUG-24451 - indefinite wait may hang"); #endif QTcpSocket *socket = newSocket(); socket->connectToHost(QtNetworkSettings::httpServerName(), 80); socket->write("GET / HTTP/1.0\r\n\r\n"); QVERIFY(socket->waitForReadyRead(-1)); delete socket; } //---------------------------------------------------------------------------------- void tst_QTcpSocket::flush() { QTcpSocket *socket = newSocket(); socket->flush(); connect(socket, SIGNAL(connected()), SLOT(exitLoopSlot())); socket->connectToHost(QtNetworkSettings::imapServerName(), 143); enterLoop(60); QVERIFY(socket->isOpen()); socket->write("1 LOGOUT\r\n"); QCOMPARE(socket->bytesToWrite(), qint64(10)); socket->flush(); QCOMPARE(socket->bytesToWrite(), qint64(0)); socket->close(); delete socket; } //---------------------------------------------------------------------------------- void tst_QTcpSocket::synchronousApi() { QTcpSocket *ftpSocket = newSocket(); ftpSocket->connectToHost(QtNetworkSettings::ftpServerName(), 21); ftpSocket->write("QUIT\r\n"); QVERIFY(ftpSocket->waitForDisconnected(10000)); QVERIFY(ftpSocket->bytesAvailable() > 0); QByteArray arr = ftpSocket->readAll(); QVERIFY(arr.size() > 0); delete ftpSocket; } //---------------------------------------------------------------------------------- void tst_QTcpSocket::dontCloseOnTimeout() { QTcpServer server; #ifndef QT_NO_NETWORKPROXY server.setProxy(QNetworkProxy(QNetworkProxy::NoProxy)); #endif QVERIFY(server.listen()); QHostAddress serverAddress = QHostAddress::LocalHost; if (!(server.serverAddress() == QHostAddress::AnyIPv4) && !(server.serverAddress() == QHostAddress::AnyIPv6) && !(server.serverAddress() == QHostAddress::Any)) serverAddress = server.serverAddress(); QTcpSocket *socket = newSocket(); socket->connectToHost(serverAddress, server.serverPort()); QVERIFY(!socket->waitForReadyRead(100)); QCOMPARE(socket->error(), QTcpSocket::SocketTimeoutError); QVERIFY(socket->isOpen()); QVERIFY(!socket->waitForDisconnected(100)); QCOMPARE(socket->error(), QTcpSocket::SocketTimeoutError); QVERIFY(socket->isOpen()); delete socket; } //---------------------------------------------------------------------------------- void tst_QTcpSocket::recursiveReadyRead() { QTcpSocket *testSocket = newSocket(); connect(testSocket, SIGNAL(connected()), SLOT(exitLoopSlot())); connect(testSocket, SIGNAL(readyRead()), SLOT(recursiveReadyReadSlot())); tmpSocket = testSocket; QSignalSpy spy(testSocket, SIGNAL(readyRead())); testSocket->connectToHost(QtNetworkSettings::imapServerName(), 143); enterLoop(30); QVERIFY2(!timeout(), "Timed out when connecting to QtNetworkSettings::imapServerName()."); enterLoop(30); QVERIFY2(!timeout(), "Timed out when waiting for the readyRead() signal."); QCOMPARE(spy.count(), 1); delete testSocket; } void tst_QTcpSocket::recursiveReadyReadSlot() { // make sure the server spits out more data tmpSocket->write("NOOP\r\n"); tmpSocket->flush(); // indiscriminately enter the event loop and start processing // events again. but oops! future socket notifications will cause // undesired recursive behavior. Unless QTcpSocket is smart, which // it of course is. :-) QEventLoop loop; for (int i = 0; i < 100; ++i) loop.processEvents(); // all we really wanted to do was process some events, then exit // the loop exitLoop(); } //---------------------------------------------------------------------------------- void tst_QTcpSocket::atEnd() { QTcpSocket *socket = newSocket(); socket->connectToHost(QtNetworkSettings::ftpServerName(), 21); QVERIFY(socket->waitForReadyRead(15000)); QTextStream stream(socket); QVERIFY(!stream.atEnd()); QString greeting = stream.readLine(); QVERIFY(stream.atEnd()); #ifdef QT_TEST_SERVER // Test server must use some vsFTPd 3.x.x version QVERIFY2(greeting.length() == sizeof("220 (vsFTPd 3.x.x)")-1, qPrintable(greeting)); QVERIFY2(greeting.startsWith("220 (vsFTPd 3."), qPrintable(greeting)); #else // Test server must use some vsFTPd 2.x.x version QVERIFY2(greeting.length() == sizeof("220 (vsFTPd 2.x.x)")-1, qPrintable(greeting)); QVERIFY2(greeting.startsWith("220 (vsFTPd 2."), qPrintable(greeting)); #endif QVERIFY2(greeting.endsWith(QLatin1Char(')')), qPrintable(greeting)); delete socket; } class TestThread : public QThread { Q_OBJECT public: inline QByteArray data() const { return socketData; } protected: inline void run() override { #ifndef QT_NO_SSL QFETCH_GLOBAL(bool, ssl); if (ssl) socket = new QSslSocket; else #endif socket = new QTcpSocket; connect(socket, SIGNAL(readyRead()), this, SLOT(getData()), Qt::DirectConnection); connect(socket, SIGNAL(disconnected()), this, SLOT(closed()), Qt::DirectConnection); connect(socket, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), Qt::DirectConnection); socket->connectToHost(QtNetworkSettings::ftpServerName(), 21); socket->write("QUIT\r\n"); exec(); delete socket; } private slots: inline void getData() { socketData += socket->readAll(); } inline void closed() { quit(); } #ifndef QT_NO_NETWORKPROXY inline void proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *auth) { auth->setUser("qsockstest"); auth->setPassword("password"); } #endif // !QT_NO_NETWORKPROXY private: QTcpSocket *socket; QByteArray socketData; }; //---------------------------------------------------------------------------------- void tst_QTcpSocket::socketInAThread() { for (int i = 0; i < 3; ++i) { TestThread thread; thread.start(); QVERIFY(thread.wait(15000)); QByteArray data = thread.data(); QVERIFY2(QtNetworkSettings::compareReplyFtp(data), data.constData()); } } //---------------------------------------------------------------------------------- void tst_QTcpSocket::socketsInThreads() { for (int i = 0; i < 3; ++i) { TestThread thread1; TestThread thread2; TestThread thread3; thread1.start(); thread2.start(); thread3.start(); QVERIFY(thread2.wait(15000)); QVERIFY(thread3.wait(15000)); QVERIFY(thread1.wait(15000)); QByteArray data1 = thread1.data(); QByteArray data2 = thread2.data(); QByteArray data3 = thread3.data(); QVERIFY2(QtNetworkSettings::compareReplyFtp(data1), data1.constData()); QVERIFY2(QtNetworkSettings::compareReplyFtp(data2), data2.constData()); QVERIFY2(QtNetworkSettings::compareReplyFtp(data3), data3.constData()); } } //---------------------------------------------------------------------------------- void tst_QTcpSocket::waitForReadyReadInASlot() { QTcpSocket *socket = newSocket(); tmpSocket = socket; connect(socket, SIGNAL(connected()), this, SLOT(waitForReadyReadInASlotSlot())); socket->connectToHost(QtNetworkSettings::httpServerName(), 80); socket->write("GET / HTTP/1.0\r\n\r\n"); enterLoop(30); QVERIFY(!timeout()); delete socket; } void tst_QTcpSocket::waitForReadyReadInASlotSlot() { QVERIFY(tmpSocket->waitForReadyRead(10000)); exitLoop(); } class RemoteCloseErrorServer : public QTcpServer { Q_OBJECT public: RemoteCloseErrorServer() { connect(this, SIGNAL(newConnection()), this, SLOT(getConnection())); } private slots: void getConnection() { tst_QTcpSocket::exitLoop(); } }; //---------------------------------------------------------------------------------- void tst_QTcpSocket::remoteCloseError() { QFETCH_GLOBAL(bool, setProxy); if (setProxy) return; //proxy not useful for localhost test case RemoteCloseErrorServer server; QVERIFY(server.listen(QHostAddress::LocalHost)); QCoreApplication::instance()->processEvents(); QTcpSocket *clientSocket = newSocket(); connect(clientSocket, SIGNAL(readyRead()), this, SLOT(exitLoopSlot())); clientSocket->connectToHost(server.serverAddress(), server.serverPort()); enterLoop(30); QVERIFY(!timeout()); QVERIFY(server.hasPendingConnections()); QTcpSocket *serverSocket = server.nextPendingConnection(); connect(clientSocket, SIGNAL(disconnected()), this, SLOT(exitLoopSlot())); serverSocket->write("Hello"); enterLoop(30); QVERIFY(!timeout()); QCOMPARE(clientSocket->bytesAvailable(), qint64(5)); qRegisterMetaType("QAbstractSocket::SocketError"); QSignalSpy errorSpy(clientSocket, SIGNAL(errorOccurred(QAbstractSocket::SocketError))); QSignalSpy disconnectedSpy(clientSocket, SIGNAL(disconnected())); clientSocket->write("World"); serverSocket->disconnectFromHost(); tmpSocket = clientSocket; connect(clientSocket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), this, SLOT(remoteCloseErrorSlot())); enterLoop(30); QVERIFY(!timeout()); QCOMPARE(disconnectedSpy.count(), 1); QCOMPARE(errorSpy.count(), 1); QCOMPARE(clientSocket->error(), QAbstractSocket::RemoteHostClosedError); delete serverSocket; clientSocket->connectToHost(server.serverAddress(), server.serverPort()); enterLoop(30); QVERIFY(!timeout()); QVERIFY(server.hasPendingConnections()); serverSocket = server.nextPendingConnection(); serverSocket->disconnectFromHost(); enterLoop(30); QVERIFY(!timeout()); QCOMPARE(clientSocket->state(), QAbstractSocket::UnconnectedState); delete clientSocket; } void tst_QTcpSocket::remoteCloseErrorSlot() { QCOMPARE(tmpSocket->state(), QAbstractSocket::ConnectedState); static_cast(sender())->close(); } void tst_QTcpSocket::enterLoopSlot() { QTcpSocket *socket = qobject_cast(sender()); socket->deleteLater(); // enter nested event loop QEventLoop loop; QTimer::singleShot(100, &loop, SLOT(quit())); loop.exec(); // Fire a non-0 singleshot to leave time for the delete QTimer::singleShot(250, this, SLOT(exitLoopSlot())); } //---------------------------------------------------------------------------------- void tst_QTcpSocket::nestedEventLoopInErrorSlot() { QTcpSocket *socket = newSocket(); QPointer p(socket); connect(socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), this, SLOT(enterLoopSlot())); socket->connectToHost("hostnotfoundhostnotfound.qt-project.org", 9999); enterLoop(30); QVERIFY(!p); } //---------------------------------------------------------------------------------- void tst_QTcpSocket::connectToHostError_data() { QTest::addColumn("host"); QTest::addColumn("port"); QTest::addColumn("expectedError"); QTest::newRow("localhost no service") << QStringLiteral("localhost") << quint16(31415) << QAbstractSocket::ConnectionRefusedError; QTest::newRow("unreachable") << QStringLiteral("0.0.0.1") << quint16(65000) << QAbstractSocket::NetworkError; } void tst_QTcpSocket::connectToHostError() { // We are aware of at least one OS in our CI, that would fail // the test due to timeout - it's Ubuntu 20.04 and 'connect' // to 0.0.0.1 there return EINPROGRESS, with no other error // ever received, so only our own internal 30 s. timer can // detect a connection timeout. std::unique_ptr socket(newSocket()); QAbstractSocket::SocketError error = QAbstractSocket::UnknownSocketError; QFETCH(const QString, host); QFETCH(const quint16, port); QFETCH(QAbstractSocket::SocketError, expectedError); QTestEventLoop eventLoop; connect(socket.get(), &QAbstractSocket::errorOccurred, socket.get(), [&](QAbstractSocket::SocketError socketError) { error = socketError; QTimer::singleShot(0, &eventLoop, [&]{eventLoop.exitLoop();}); }); socket->connectToHost(host, port); eventLoop.enterLoopMSecs(10'000); if (eventLoop.timeout()) { // Let's at least verify it's not in connected state: QVERIFY(socket->state() != QAbstractSocket::ConnectedState); QSKIP("Connection to unreachable host timed out, skipping the rest of the test"); } QCOMPARE(socket->state(), QTcpSocket::UnconnectedState); if (error != expectedError && error == QAbstractSocket::ConnectionRefusedError) QEXPECT_FAIL("unreachable", "CI firewall interfers with this test", Continue); QCOMPARE(error, expectedError); } //---------------------------------------------------------------------------------- void tst_QTcpSocket::waitForConnectedInHostLookupSlot() { // This test tries to reproduce the problem where waitForConnected() is // called at a point where the host lookup is already done. QTcpSocket // will try to abort the "pending lookup", but since it's already done and // the queued signal is already underway, we will receive the signal after // waitForConnected() has returned, and control goes back to the event // loop. When the signal has been received, the connection is torn down, // then reopened. Yikes. If we reproduce this by calling // waitForConnected() inside hostLookupSlot(), it will even crash. tmpSocket = newSocket(); QEventLoop loop; connect(tmpSocket, SIGNAL(connected()), &loop, SLOT(quit())); QTimer timer; connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit())); QSignalSpy timerSpy(&timer, SIGNAL(timeout())); timer.start(15000); connect(tmpSocket, SIGNAL(hostFound()), this, SLOT(hostLookupSlot())); tmpSocket->connectToHost(QtNetworkSettings::imapServerName(), 143); // only execute the loop if not already connected if (tmpSocket->state() != QAbstractSocket::ConnectedState) loop.exec(); QCOMPARE(timerSpy.count(), 0); delete tmpSocket; } void tst_QTcpSocket::hostLookupSlot() { // This will fail to cancel the pending signal QVERIFY(tmpSocket->waitForConnected(10000)); } class Foo : public QObject { Q_OBJECT QTcpSocket *sock; public: bool attemptedToConnect; bool networkTimeout; int count; inline Foo(QObject *parent = nullptr) : QObject(parent) { attemptedToConnect = false; networkTimeout = false; count = 0; #ifndef QT_NO_SSL QFETCH_GLOBAL(bool, ssl); if (ssl) sock = new QSslSocket; else #endif sock = new QTcpSocket; connect(sock, SIGNAL(connected()), this, SLOT(connectedToIt())); connect(sock, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); } inline ~Foo() { delete sock; } public slots: inline void connectedToIt() { count++; } inline void doIt() { attemptedToConnect = true; sock->connectToHost(QtNetworkSettings::httpServerName(), 80); #if defined(Q_OS_MAC) pthread_yield_np(); #elif defined Q_OS_LINUX && !defined Q_OS_ANDROID pthread_yield(); #endif if (!sock->waitForConnected()) { networkTimeout = true; } tst_QTcpSocket::exitLoop(); } inline void exitLoop() { tst_QTcpSocket::exitLoop(); } #ifndef QT_NO_NETWORKPROXY inline void proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *auth) { auth->setUser("qsockstest"); auth->setPassword("password"); } #endif // !QT_NO_NETWORKPROXY }; //---------------------------------------------------------------------------------- void tst_QTcpSocket::waitForConnectedInHostLookupSlot2() { Foo foo; QTimer::singleShot(100, &foo, SLOT(doIt())); QTimer::singleShot(5000, &foo, SLOT(exitLoop())); enterLoop(30); if (timeout() || foo.networkTimeout) QFAIL("Network timeout"); QVERIFY(foo.attemptedToConnect); QCOMPARE(foo.count, 1); } //---------------------------------------------------------------------------------- void tst_QTcpSocket::readyReadSignalsAfterWaitForReadyRead() { QTcpSocket *socket = newSocket(); QSignalSpy readyReadSpy(socket, SIGNAL(readyRead())); // Connect socket->connectToHost(QtNetworkSettings::imapServerName(), 143); // Wait for the read QVERIFY(socket->waitForReadyRead(10000)); QCOMPARE(readyReadSpy.count(), 1); QString s = socket->readLine(); QVERIFY2(QtNetworkSettings::compareReplyIMAP(s.toLatin1()), s.toLatin1().constData()); QCOMPARE(socket->bytesAvailable(), qint64(0)); QCoreApplication::instance()->processEvents(); QCOMPARE(socket->bytesAvailable(), qint64(0)); QCOMPARE(readyReadSpy.count(), 1); delete socket; } class TestThread2 : public QThread { Q_OBJECT public: void run() override { QFile fileWriter("fifo"); QVERIFY(fileWriter.open(QFile::WriteOnly)); QCOMPARE(fileWriter.write(QByteArray(32, '@')), qint64(32)); QCOMPARE(fileWriter.write(QByteArray(32, '@')), qint64(32)); QCOMPARE(fileWriter.write(QByteArray(32, '@')), qint64(32)); QCOMPARE(fileWriter.write(QByteArray(32, '@')), qint64(32)); } }; //---------------------------------------------------------------------------------- #ifdef Q_OS_LINUX void tst_QTcpSocket::linuxKernelBugLocalSocket() { QFile::remove("fifo"); mkfifo("fifo", 0666); TestThread2 test; test.start(); QFile fileReader("fifo"); QVERIFY(fileReader.open(QFile::ReadOnly)); test.wait(); QTcpSocket *socket = newSocket(); socket->setSocketDescriptor(fileReader.handle()); QVERIFY(socket->waitForReadyRead(5000)); QCOMPARE(socket->bytesAvailable(), qint64(128)); QFile::remove("fifo"); delete socket; } #endif //---------------------------------------------------------------------------------- void tst_QTcpSocket::abortiveClose() { QFETCH_GLOBAL(bool, setProxy); if (setProxy) return; //proxy not useful for localhost test case QTcpServer server; QVERIFY(server.listen(QHostAddress::LocalHost)); connect(&server, SIGNAL(newConnection()), this, SLOT(exitLoopSlot())); QTcpSocket *clientSocket = newSocket(); clientSocket->connectToHost(server.serverAddress(), server.serverPort()); enterLoop(10); QVERIFY(server.hasPendingConnections()); tmpSocket = server.nextPendingConnection(); QVERIFY(tmpSocket != nullptr); qRegisterMetaType("QAbstractSocket::SocketError"); QSignalSpy readyReadSpy(clientSocket, SIGNAL(readyRead())); QSignalSpy errorSpy(clientSocket, SIGNAL(errorOccurred(QAbstractSocket::SocketError))); connect(clientSocket, SIGNAL(disconnected()), this, SLOT(exitLoopSlot())); QTimer::singleShot(0, this, SLOT(abortiveClose_abortSlot())); enterLoop(5); QCOMPARE(readyReadSpy.count(), 0); QCOMPARE(errorSpy.count(), 1); QCOMPARE(*static_cast(errorSpy.at(0).at(0).constData()), int(QAbstractSocket::RemoteHostClosedError)); delete clientSocket; } void tst_QTcpSocket::abortiveClose_abortSlot() { tmpSocket->abort(); } //---------------------------------------------------------------------------------- void tst_QTcpSocket::localAddressEmptyOnBSD() { #ifdef Q_OS_WIN QSKIP("QTBUG-24451 - indefinite wait may hang"); #endif QFETCH_GLOBAL(bool, setProxy); if (setProxy) return; //proxy not useful for localhost test case QTcpServer server; QVERIFY(server.listen(QHostAddress::LocalHost)); QTcpSocket *tcpSocket = 0; // we try 10 times, but note that this doesn't always provoke the bug for (int i = 0; i < 10; ++i) { delete tcpSocket; tcpSocket = newSocket(); tcpSocket->connectToHost(QHostAddress::LocalHost, server.serverPort()); if (!tcpSocket->waitForConnected(0)) { // to provoke the bug, we need a local socket that connects immediately // --i; tcpSocket->abort(); if (tcpSocket->state() != QTcpSocket::UnconnectedState) QVERIFY(tcpSocket->waitForDisconnected(-1)); continue; } QCOMPARE(tcpSocket->localAddress(), QHostAddress(QHostAddress::LocalHost)); } delete tcpSocket; } //---------------------------------------------------------------------------------- void tst_QTcpSocket::zeroAndMinusOneReturns() { QTcpSocket *socket = newSocket(); socket->connectToHost(QtNetworkSettings::httpServerName(), 80); socket->write("GET / HTTP/1.0\r\nConnection: keep-alive\r\n\r\n"); QVERIFY(socket->waitForReadyRead(15000)); char c[16]; QVERIFY(socket->getChar(c)); QCOMPARE(socket->read(c, 16), qint64(16)); QVERIFY(socket->readLine(c, 16) > 0); QVERIFY(!socket->readAll().isEmpty()); // the last operation emptied the read buffer // all read operations from this point on should fail // with return 0 because the socket is still open QVERIFY(socket->readAll().isEmpty()); QCOMPARE(socket->read(c, 16), qint64(0)); QCOMPARE(socket->readLine(c, 16), qint64(0)); QVERIFY(!socket->getChar(c)); socket->write("GET / HTTP/1.0\r\n\r\n"); QVERIFY(socket->waitForDisconnected(15000)); QCOMPARE(socket->error(), QAbstractSocket::RemoteHostClosedError); QCOMPARE(socket->write("BLUBBER"), qint64(-1)); QVERIFY(socket->getChar(c)); QCOMPARE(socket->read(c, 16), qint64(16)); QVERIFY(socket->readLine(c, 16) > 0); QVERIFY(!socket->readAll().isEmpty()); // the last operation emptied the read buffer // all read operations from this point on should fail // with return -1 because the socket is not connected QVERIFY(socket->readAll().isEmpty()); QCOMPARE(socket->read(c, 16), qint64(-1)); QCOMPARE(socket->readLine(c, 16), qint64(-1)); QVERIFY(!socket->getChar(c)); QVERIFY(!socket->putChar('a')); socket->close(); // now the QIODevice is closed, which means getChar complains QCOMPARE(socket->write("BLUBBER"), qint64(-1)); QCOMPARE(socket->read(c, 16), qint64(-1)); QCOMPARE(socket->readLine(c, 16), qint64(-1)); QVERIFY(!socket->getChar(c)); QVERIFY(!socket->putChar('a')); delete socket; } //---------------------------------------------------------------------------------- void tst_QTcpSocket::connectionRefused() { qRegisterMetaType("QAbstractSocket::SocketError"); qRegisterMetaType("QAbstractSocket::SocketState"); QTcpSocket *socket = newSocket(); QSignalSpy stateSpy(socket, SIGNAL(stateChanged(QAbstractSocket::SocketState))); QSignalSpy errorSpy(socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError))); connect(socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), &QTestEventLoop::instance(), SLOT(exitLoop())); socket->connectToHost(QtNetworkSettings::httpServerName(), 144); enterLoop(10); disconnect(socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), &QTestEventLoop::instance(), SLOT(exitLoop())); QVERIFY2(!timeout(), "Network timeout"); QCOMPARE(socket->state(), QAbstractSocket::UnconnectedState); QCOMPARE(socket->error(), QAbstractSocket::ConnectionRefusedError); QCOMPARE(stateSpy.count(), 3); QCOMPARE(qvariant_cast(stateSpy.at(0).at(0)), QAbstractSocket::HostLookupState); QCOMPARE(qvariant_cast(stateSpy.at(1).at(0)), QAbstractSocket::ConnectingState); QCOMPARE(qvariant_cast(stateSpy.at(2).at(0)), QAbstractSocket::UnconnectedState); QCOMPARE(errorSpy.count(), 1); delete socket; } //---------------------------------------------------------------------------------- void tst_QTcpSocket::suddenRemoteDisconnect_data() { QTest::addColumn("client"); QTest::addColumn("server"); QTest::newRow("Qt4 Client <-> Qt4 Server") << QString::fromLatin1("qt4client") << QString::fromLatin1("qt4server"); } void tst_QTcpSocket::suddenRemoteDisconnect() { #if !QT_CONFIG(process) QSKIP("This test requires QProcess support"); #else QFETCH(QString, client); QFETCH(QString, server); QFETCH_GLOBAL(bool, setProxy); if (setProxy) return; QFETCH_GLOBAL(bool, ssl); if (ssl) return; QString processExe = stressTestDir + "/stressTest"; // Start server QProcess serverProcess; serverProcess.setReadChannel(QProcess::StandardError); serverProcess.start(processExe, QStringList(server), QIODevice::ReadWrite | QIODevice::Text); QVERIFY2(serverProcess.waitForStarted(), qPrintable( QString::fromLatin1("Could not start %1: %2").arg(processExe, serverProcess.errorString()))); while (!serverProcess.canReadLine()) QVERIFY(serverProcess.waitForReadyRead(10000)); QByteArray line = serverProcess.readLine(); // Ignore following print, happens on Qemu: if (line == "getsockopt level=41 optname=26 not yet supported\n") { while (!serverProcess.canReadLine()) QVERIFY(serverProcess.waitForReadyRead(10000)); line = serverProcess.readLine(); } QCOMPARE(line.data(), QByteArray(server.toLatin1() + "\n").data()); // Start client QProcess clientProcess; clientProcess.setReadChannel(QProcess::StandardError); clientProcess.start(processExe, QStringList(client), QIODevice::ReadWrite | QIODevice::Text); QVERIFY2(clientProcess.waitForStarted(), qPrintable( QString::fromLatin1("Could not start %1: %2").arg(processExe, clientProcess.errorString()))); while (!clientProcess.canReadLine()) QVERIFY(clientProcess.waitForReadyRead(10000)); QCOMPARE(clientProcess.readLine().data(), QByteArray(client.toLatin1() + "\n").data()); // Let them play for a while qDebug("Running stress test for 5 seconds"); QEventLoop loop; connect(&serverProcess, SIGNAL(finished(int)), &loop, SLOT(quit())); connect(&clientProcess, SIGNAL(finished(int)), &loop, SLOT(quit())); QElapsedTimer stopWatch; stopWatch.start(); QTimer::singleShot(20000, &loop, SLOT(quit())); while ((serverProcess.state() == QProcess::Running || clientProcess.state() == QProcess::Running) && stopWatch.elapsed() < 20000) loop.exec(); QVERIFY(stopWatch.elapsed() < 20000); // Check that both exited normally. #if defined(UBUNTU_ONEIRIC) && defined(__x86_64__) QEXPECT_FAIL("", "Fails on this platform", Abort); #endif QCOMPARE(clientProcess.readAll().constData(), "SUCCESS\n"); QCOMPARE(serverProcess.readAll().constData(), "SUCCESS\n"); #endif // QT_CONFIG(process) } //---------------------------------------------------------------------------------- void tst_QTcpSocket::connectToMultiIP() { QSKIP("TODO: setup DNS in the new network"); #if defined(Q_OS_VXWORKS) QSKIP("VxSim in standard config doesn't even run a DNS resolver"); #else QFETCH_GLOBAL(bool, ssl); if (ssl) return; QFETCH_GLOBAL(bool, setProxy); if (setProxy) QSKIP("This test takes too long if we also add the proxies."); qDebug("Please wait, this test can take a while..."); QTcpSocket *socket = newSocket(); // rationale: this domain resolves to 3 A-records, 2 of them are // invalid. QTcpSocket should never spend more than 30 seconds per IP, and // 30s*2 = 60s. QElapsedTimer stopWatch; stopWatch.start(); socket->connectToHost("multi.dev.qt-project.org", 80); QVERIFY(socket->waitForConnected(60500)); QVERIFY(stopWatch.elapsed() < 70000); socket->abort(); stopWatch.restart(); socket->connectToHost("multi.dev.qt-project.org", 81); QVERIFY(!socket->waitForConnected(2000)); QVERIFY(stopWatch.elapsed() < 2000); QCOMPARE(socket->error(), QAbstractSocket::SocketTimeoutError); delete socket; #endif } //---------------------------------------------------------------------------------- void tst_QTcpSocket::moveToThread0() { QFETCH_GLOBAL(int, proxyType); if (proxyType & AuthMask) return; { // Case 1: Moved after connecting, before waiting for connection. QTcpSocket *socket = newSocket();; socket->connectToHost(QtNetworkSettings::imapServerName(), 143); socket->moveToThread(0); QVERIFY(socket->waitForConnected(5000)); socket->write("XXX LOGOUT\r\n"); QVERIFY(socket->waitForBytesWritten(5000)); QVERIFY(socket->waitForDisconnected()); delete socket; } { // Case 2: Moved before connecting QTcpSocket *socket = newSocket(); socket->moveToThread(0); socket->connectToHost(QtNetworkSettings::imapServerName(), 143); QVERIFY(socket->waitForConnected(5000)); socket->write("XXX LOGOUT\r\n"); QVERIFY(socket->waitForBytesWritten(5000)); QVERIFY(socket->waitForDisconnected()); delete socket; } { // Case 3: Moved after writing, while waiting for bytes to be written. QTcpSocket *socket = newSocket(); socket->connectToHost(QtNetworkSettings::imapServerName(), 143); QVERIFY(socket->waitForConnected(5000)); socket->write("XXX LOGOUT\r\n"); socket->moveToThread(0); QVERIFY(socket->waitForBytesWritten(5000)); QVERIFY(socket->waitForDisconnected()); delete socket; } { // Case 4: Moved after writing, while waiting for response. QTcpSocket *socket = newSocket(); socket->connectToHost(QtNetworkSettings::imapServerName(), 143); QVERIFY(socket->waitForConnected(5000)); socket->write("XXX LOGOUT\r\n"); QVERIFY(socket->waitForBytesWritten(5000)); socket->moveToThread(0); QVERIFY(socket->waitForDisconnected()); delete socket; } } void tst_QTcpSocket::increaseReadBufferSize() { QFETCH_GLOBAL(bool, setProxy); if (setProxy) return; //proxy not useful for localhost test case QTcpServer server; QTcpSocket *active = newSocket(); connect(active, SIGNAL(readyRead()), SLOT(exitLoopSlot())); // connect two sockets to each other: QVERIFY(server.listen(QHostAddress::LocalHost)); active->connectToHost("127.0.0.1", server.serverPort()); QVERIFY(active->waitForConnected(5000)); QVERIFY(server.waitForNewConnection(5000)); QTcpSocket *passive = server.nextPendingConnection(); QVERIFY(passive); // now write 512 bytes of data on one end QByteArray data(512, 'a'); passive->write(data); QVERIFY2(passive->waitForBytesWritten(5000), "Network timeout"); // set the read buffer size to less than what was written and iterate: active->setReadBufferSize(256); enterLoop(10); QVERIFY2(!timeout(), "Network timeout"); QCOMPARE(active->bytesAvailable(), active->readBufferSize()); // increase the buffer size and iterate again: active->setReadBufferSize(384); enterLoop(10); QVERIFY2(!timeout(), "Network timeout"); QCOMPARE(active->bytesAvailable(), active->readBufferSize()); // once more, but now it should read everything there was to read active->setReadBufferSize(1024); enterLoop(10); QVERIFY2(!timeout(), "Network timeout"); QCOMPARE(active->bytesAvailable(), qint64(data.size())); // drain it and compare QCOMPARE(active->readAll(), data); // now one more test by setting the buffer size to unlimited: passive->write(data); QVERIFY2(passive->waitForBytesWritten(5000), "Network timeout"); active->setReadBufferSize(256); enterLoop(10); QVERIFY2(!timeout(), "Network timeout"); QCOMPARE(active->bytesAvailable(), active->readBufferSize()); active->setReadBufferSize(0); enterLoop(10); QVERIFY2(!timeout(), "Network timeout"); QCOMPARE(active->bytesAvailable(), qint64(data.size())); QCOMPARE(active->readAll(), data); delete active; } void tst_QTcpSocket::increaseReadBufferSizeFromSlot() // like KIO's socketconnectionbackend { QFETCH_GLOBAL(bool, setProxy); if (setProxy) return; //proxy not useful for localhost test case QTcpServer server; QTcpSocket *active = newSocket(); connect(active, SIGNAL(readyRead()), SLOT(slotIncreaseReadBufferSizeReadyRead())); // connect two sockets to each other: QVERIFY(server.listen(QHostAddress::LocalHost)); active->connectToHost("127.0.0.1", server.serverPort()); QVERIFY(active->waitForConnected(5000)); QVERIFY(server.waitForNewConnection(5000)); QTcpSocket *passive = server.nextPendingConnection(); QVERIFY(passive); // now write 512 bytes of data on one end QByteArray data(512, 'a'); passive->write(data); QVERIFY2(passive->waitForBytesWritten(5000), "Network timeout"); // set the read buffer size to less than what was written, // and increase it from the slot, first to 384 then to 512. active->setReadBufferSize(256); enterLoop(10); QVERIFY2(!timeout(), "Network timeout"); QCOMPARE(active->bytesAvailable(), qint64(data.size())); // drain it and compare QCOMPARE(active->readAll(), data); delete active; } void tst_QTcpSocket::slotIncreaseReadBufferSizeReadyRead() { QTcpSocket *socket = qobject_cast(sender()); const int currentBufferSize = socket->readBufferSize(); QCOMPARE(currentBufferSize, socket->bytesAvailable()); if (currentBufferSize == 256) socket->setReadBufferSize(384); else if (currentBufferSize == 384) socket->setReadBufferSize(512); else if (currentBufferSize == 512) exitLoopSlot(); else // should not happen qFatal("buffer size was %d", currentBufferSize); } void tst_QTcpSocket::taskQtBug5799ConnectionErrorWaitForConnected() { QFETCH_GLOBAL(bool, setProxy); if (setProxy) return; // check that we get a proper error connecting to port 12346 // use waitForConnected, e.g. this should use a synchronous select() on the OS level QTcpSocket socket; socket.connectToHost(QtNetworkSettings::httpServerName(), 12346); QElapsedTimer timer; timer.start(); socket.waitForConnected(10000); QVERIFY2(timer.elapsed() < 9900, "Connection to closed port timed out instead of refusing, something is wrong"); QVERIFY2(socket.state() == QAbstractSocket::UnconnectedState, "Socket connected unexpectedly!"); QVERIFY2(socket.error() == QAbstractSocket::ConnectionRefusedError, QString("Could not reach server: %1").arg(socket.errorString()).toLocal8Bit()); } void tst_QTcpSocket::taskQtBug5799ConnectionErrorEventLoop() { QFETCH_GLOBAL(bool, setProxy); if (setProxy) return; // check that we get a proper error connecting to port 12346 // This testcase uses an event loop QTcpSocket socket; connect(&socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), &QTestEventLoop::instance(), SLOT(exitLoop())); socket.connectToHost(QtNetworkSettings::httpServerName(), 12346); QTestEventLoop::instance().enterLoop(10); QVERIFY2(!QTestEventLoop::instance().timeout(), "Connection to closed port timed out instead of refusing, something is wrong"); QVERIFY2(socket.state() == QAbstractSocket::UnconnectedState, "Socket connected unexpectedly!"); QVERIFY2(socket.error() == QAbstractSocket::ConnectionRefusedError, QString("Could not reach server: %1").arg(socket.errorString()).toLocal8Bit()); } void tst_QTcpSocket::taskQtBug7054TimeoutErrorResetting() { QTcpSocket *socket = newSocket(); socket->connectToHost(QtNetworkSettings::httpServerName(), 443); QVERIFY(socket->waitForConnected(5*1000)); QCOMPARE(socket->error(), QAbstractSocket::UnknownSocketError); // We connected to the HTTPS port. Wait two seconds to receive data. We will receive // nothing because we would need to start the SSL handshake QVERIFY(!socket->waitForReadyRead(2*1000)); QCOMPARE(socket->error(), QAbstractSocket::SocketTimeoutError); // Now write some crap to make the server disconnect us. 4 lines are enough. socket->write("a\r\nb\r\nc\r\nd\r\n"); socket->waitForBytesWritten(2*1000); // we try to waitForReadyRead another time, but this time instead of a timeout we // should get a better error since the server disconnected us QVERIFY(!socket->waitForReadyRead(2*1000)); // It must NOT be the SocketTimeoutError that had been set before QCOMPARE(socket->error(), QAbstractSocket::RemoteHostClosedError); } #ifndef QT_NO_NETWORKPROXY void tst_QTcpSocket::invalidProxy_data() { QTest::addColumn("type"); QTest::addColumn("host"); QTest::addColumn("port"); QTest::addColumn("failsAtConnect"); QTest::addColumn("expectedError"); const QString ftpAddress = QtNetworkSettings::ftpServerIp().toString(); const QString httpProxyAddress = QtNetworkSettings::httpProxyServerIp().toString(); const QString socksProxyAddress = QtNetworkSettings::socksProxyServerIp().toString(); QTest::newRow("ftp-proxy") << int(QNetworkProxy::FtpCachingProxy) << ftpAddress << 21 << true << int(QAbstractSocket::UnsupportedSocketOperationError); QTest::newRow("http-caching-proxy") << int(QNetworkProxy::HttpCachingProxy) << httpProxyAddress << 3128 << true << int(QAbstractSocket::UnsupportedSocketOperationError); QTest::newRow("no-such-host-socks5") << int(QNetworkProxy::Socks5Proxy) << "this-host-will-never-exist.qt-project.org" << 1080 << false << int(QAbstractSocket::ProxyNotFoundError); QTest::newRow("no-such-host-http") << int(QNetworkProxy::HttpProxy) << "this-host-will-never-exist.qt-project.org" << 3128 << false << int(QAbstractSocket::ProxyNotFoundError); QTest::newRow("http-on-socks5") << int(QNetworkProxy::HttpProxy) << socksProxyAddress << 1080 << false << int(QAbstractSocket::ProxyConnectionClosedError); QTest::newRow("socks5-on-http") << int(QNetworkProxy::Socks5Proxy) << httpProxyAddress << 3128 << false << int(QAbstractSocket::SocketTimeoutError); } void tst_QTcpSocket::invalidProxy() { QFETCH_GLOBAL(bool, setProxy); if (setProxy) return; QFETCH(int, type); QFETCH(QString, host); QFETCH(int, port); QFETCH(bool, failsAtConnect); QNetworkProxy::ProxyType proxyType = QNetworkProxy::ProxyType(type); QNetworkProxy proxy(proxyType, host, port); QTcpSocket *socket = newSocket(); socket->setProxy(proxy); socket->connectToHost(QtNetworkSettings::httpServerIp().toString(), 80); if (failsAtConnect) { QCOMPARE(socket->state(), QAbstractSocket::UnconnectedState); } else { QCOMPARE(socket->state(), QAbstractSocket::ConnectingState); QVERIFY(!socket->waitForConnected(5000)); } QVERIFY(!socket->errorString().isEmpty()); // note: the following test is not a hard failure. // Sometimes, error codes change for the better QTEST(int(socket->error()), "expectedError"); delete socket; } // copied from tst_qnetworkreply.cpp class MyProxyFactory: public QNetworkProxyFactory { public: int callCount; QList toReturn; QNetworkProxyQuery lastQuery; inline MyProxyFactory() { clear(); } inline void clear() { callCount = 0; toReturn = QList() << QNetworkProxy::DefaultProxy; lastQuery = QNetworkProxyQuery(); } virtual QList queryProxy(const QNetworkProxyQuery &query) override { lastQuery = query; ++callCount; return toReturn; } }; void tst_QTcpSocket::proxyFactory_data() { QTest::addColumn >("proxyList"); QTest::addColumn("proxyUsed"); QTest::addColumn("failsAtConnect"); QTest::addColumn("expectedError"); QList proxyList; // tests that do connect proxyList << QNetworkProxy(QNetworkProxy::HttpProxy, QtNetworkSettings::httpProxyServerName(), 3129); QTest::newRow("http") << proxyList << proxyList.at(0) << false << int(QAbstractSocket::UnknownSocketError); proxyList.clear(); proxyList << QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::socksProxyServerName(), 1081); QTest::newRow("socks5") << proxyList << proxyList.at(0) << false << int(QAbstractSocket::UnknownSocketError); proxyList.clear(); proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::httpProxyServerName(), 3129) << QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::socksProxyServerName(), 1081); QTest::newRow("cachinghttp+socks5") << proxyList << proxyList.at(1) << false << int(QAbstractSocket::UnknownSocketError); proxyList.clear(); proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::ftpProxyServerName(), 2121) << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::httpProxyServerName(), 3129) << QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::socksProxyServerName(), 1081); QTest::newRow("ftp+cachinghttp+socks5") << proxyList << proxyList.at(2) << false << int(QAbstractSocket::UnknownSocketError); // tests that fail to connect proxyList.clear(); proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::httpProxyServerName(), 3129); QTest::newRow("cachinghttp") << proxyList << QNetworkProxy() << true << int(QAbstractSocket::UnsupportedSocketOperationError); proxyList.clear(); proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::ftpProxyServerName(), 2121); QTest::newRow("ftp") << proxyList << QNetworkProxy() << true << int(QAbstractSocket::UnsupportedSocketOperationError); proxyList.clear(); proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::ftpProxyServerName(), 2121) << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::httpProxyServerName(), 3129); QTest::newRow("ftp+cachinghttp") << proxyList << QNetworkProxy() << true << int(QAbstractSocket::UnsupportedSocketOperationError); } void tst_QTcpSocket::proxyFactory() { QFETCH_GLOBAL(bool, setProxy); if (setProxy) return; QFETCH(QList, proxyList); QFETCH(QNetworkProxy, proxyUsed); QFETCH(bool, failsAtConnect); MyProxyFactory *factory = new MyProxyFactory; factory->toReturn = proxyList; QNetworkProxyFactory::setApplicationProxyFactory(factory); QTcpSocket *socket = newSocket(); QString host = QtNetworkSettings::httpServerName(); socket->connectToHost(host, 80); // Verify that the factory was called properly QCOMPARE(factory->callCount, 1); QCOMPARE(factory->lastQuery, QNetworkProxyQuery(host, 80)); if (failsAtConnect) { QCOMPARE(socket->state(), QAbstractSocket::UnconnectedState); } else { QCOMPARE(socket->state(), QAbstractSocket::ConnectingState); QVERIFY(socket->waitForConnected(5000)); QCOMPARE(proxyAuthCalled, 1); } QVERIFY(!socket->errorString().isEmpty()); // note: the following test is not a hard failure. // Sometimes, error codes change for the better QTEST(int(socket->error()), "expectedError"); delete socket; } #endif // !QT_NO_NETWORKPROXY // there is a similar test inside tst_qtcpserver that uses the event loop instead void tst_QTcpSocket::qtbug14268_peek() { QFETCH_GLOBAL(bool, setProxy); if (setProxy) return; SocketPair socketPair; QVERIFY(socketPair.create()); QTcpSocket *outgoing = socketPair.endPoints[0]; QTcpSocket *incoming = socketPair.endPoints[1]; QCOMPARE(incoming->state(), QTcpSocket::ConnectedState); QCOMPARE(outgoing->state(), QTcpSocket::ConnectedState); outgoing->write("abc\n"); QVERIFY(outgoing->waitForBytesWritten(2000)); QVERIFY(incoming->waitForReadyRead(2000)); QCOMPARE(incoming->peek(128*1024), QByteArray("abc\n")); outgoing->write("def\n"); QVERIFY(outgoing->waitForBytesWritten(2000)); QVERIFY(incoming->waitForReadyRead(2000)); QCOMPARE(incoming->peek(128*1024), QByteArray("abc\ndef\n")); outgoing->write("ghi\n"); QVERIFY(outgoing->waitForBytesWritten(2000)); QVERIFY(incoming->waitForReadyRead(2000)); QCOMPARE(incoming->peek(128*1024), QByteArray("abc\ndef\nghi\n")); QCOMPARE(incoming->read(128*1024), QByteArray("abc\ndef\nghi\n")); } void tst_QTcpSocket::setSocketOption() { QFETCH_GLOBAL(bool, setProxy); if (setProxy) return; SocketPair socketPair; QVERIFY(socketPair.create()); QTcpSocket *outgoing = socketPair.endPoints[0]; QTcpSocket *incoming = socketPair.endPoints[1]; QCOMPARE(incoming->state(), QTcpSocket::ConnectedState); QCOMPARE(outgoing->state(), QTcpSocket::ConnectedState); outgoing->setSocketOption(QAbstractSocket::LowDelayOption, true); QVariant v = outgoing->socketOption(QAbstractSocket::LowDelayOption); QVERIFY(v.isValid() && v.toBool()); outgoing->setSocketOption(QAbstractSocket::KeepAliveOption, true); v = outgoing->socketOption(QAbstractSocket::KeepAliveOption); QVERIFY(v.isValid() && v.toBool()); outgoing->setSocketOption(QAbstractSocket::LowDelayOption, false); v = outgoing->socketOption(QAbstractSocket::LowDelayOption); QVERIFY(v.isValid() && !v.toBool()); outgoing->setSocketOption(QAbstractSocket::KeepAliveOption, false); v = outgoing->socketOption(QAbstractSocket::KeepAliveOption); QVERIFY(v.isValid() && !v.toBool()); #ifdef Q_OS_WIN QEXPECT_FAIL("", "QTBUG-23323", Abort); #endif outgoing->setSocketOption(QAbstractSocket::TypeOfServiceOption, 32); //high priority v = outgoing->socketOption(QAbstractSocket::TypeOfServiceOption); QVERIFY(v.isValid() && v.toInt() == 32); } // Test buffered socket properly send data on delayed disconnect void tst_QTcpSocket::clientSendDataOnDelayedDisconnect() { QFETCH_GLOBAL(bool, setProxy); if (setProxy) return; QTcpServer server; QTcpSocket *socket = newSocket(); QVERIFY(server.listen(QHostAddress::LocalHost)); // Connect to server, write data and close socket const QByteArray sendData("GET /\r\n"); socket->connectToHost(server.serverAddress(), server.serverPort()); QVERIFY(socket->waitForConnected(5000)); // ready for write QCOMPARE(socket->write(sendData), sendData.size()); socket->close(); QVERIFY(socket->waitForDisconnected(5000)); // flush buffer // Check data on server side QByteArray recData; QVERIFY(server.waitForNewConnection(5000)); QTcpSocket *newConnection = server.nextPendingConnection(); QVERIFY(newConnection != NULL); while (newConnection->waitForReadyRead(5000)) // have data to read recData += newConnection->readAll(); QCOMPARE(sendData, recData); delete socket; } // Test buffered socket being properly closed on remote disconnect void tst_QTcpSocket::serverDisconnectWithBuffered() { QFETCH_GLOBAL(bool, setProxy); if (setProxy) return; qRegisterMetaType("QAbstractSocket::SocketState"); QTcpServer tcpServer; QTcpSocket *socket = newSocket(); QVERIFY(tcpServer.listen(QHostAddress::LocalHost)); socket->connectToHost(tcpServer.serverAddress(), tcpServer.serverPort()); // Accept connection on server side QVERIFY(tcpServer.waitForNewConnection(5000)); QTcpSocket *newConnection = tcpServer.nextPendingConnection(); // Send one char and drop link QVERIFY(newConnection != NULL); QVERIFY(newConnection->putChar(0)); QVERIFY(newConnection->flush()); delete newConnection; QVERIFY(socket->waitForConnected(5000)); // ready for write QCOMPARE(socket->state(), QAbstractSocket::ConnectedState); QSignalSpy spyStateChanged(socket, SIGNAL(stateChanged(QAbstractSocket::SocketState))); QSignalSpy spyDisconnected(socket, SIGNAL(disconnected())); QVERIFY(socket->waitForReadyRead(5000)); // have one char already in internal buffer char buf[128]; QCOMPARE(socket->read(buf, sizeof(buf)), Q_INT64_C(1)); if (socket->state() != QAbstractSocket::UnconnectedState) { QVERIFY(socket->waitForDisconnected(5000)); QCOMPARE(socket->state(), QAbstractSocket::UnconnectedState); } // Test signal emitting QCOMPARE(spyDisconnected.count(), 1); QVERIFY(spyStateChanged.count() > 0); QVERIFY(qvariant_cast(spyStateChanged.last().first()) == QAbstractSocket::UnconnectedState); delete socket; } // Test buffered sockets discard input when opened in WriteOnly mode void tst_QTcpSocket::socketDiscardDataInWriteMode() { QFETCH_GLOBAL(bool, setProxy); if (setProxy) return; QTcpServer tcpServer; QTcpSocket *socket = newSocket(); QVERIFY(tcpServer.listen(QHostAddress::LocalHost)); socket->connectToHost(tcpServer.serverAddress(), tcpServer.serverPort(), QIODevice::WriteOnly); QVERIFY(socket->waitForConnected(5000)); // ready for write QCOMPARE(socket->state(), QAbstractSocket::ConnectedState); // Accept connection on server side QVERIFY2(tcpServer.waitForNewConnection(5000), "Network timeout"); QTcpSocket *newConnection = tcpServer.nextPendingConnection(); // Send one char and drop link QVERIFY(newConnection != NULL); QVERIFY(newConnection->putChar(0)); QVERIFY(newConnection->flush()); delete newConnection; QVERIFY(socket->waitForReadyRead(5000)); // discard input QVERIFY(socket->atEnd()); delete socket; } // Test waitForBytesWritten() does not fail on read buffer overflow void tst_QTcpSocket::writeOnReadBufferOverflow() { QFETCH_GLOBAL(bool, setProxy); if (setProxy) return; QTcpServer tcpServer; QTcpSocket *socket = newSocket(); QVERIFY(tcpServer.listen(QHostAddress::LocalHost)); socket->setReadBufferSize(1); socket->connectToHost(tcpServer.serverAddress(), tcpServer.serverPort()); QVERIFY(socket->waitForConnected(5000)); QCOMPARE(socket->state(), QAbstractSocket::ConnectedState); // Accept connection on server side QVERIFY2(tcpServer.waitForNewConnection(5000), "Network timeout"); QTcpSocket *newConnection = tcpServer.nextPendingConnection(); QVERIFY(newConnection != nullptr); QCOMPARE(newConnection->write("1", 2), Q_INT64_C(2)); QVERIFY(newConnection->flush()); // Wait for buffer overflow QVERIFY(socket->waitForReadyRead(5000)); QCOMPARE(socket->bytesAvailable(), Q_INT64_C(1)); // Write data and wait for successful send QVERIFY(socket->putChar(0)); QVERIFY(socket->waitForBytesWritten(5000)); delete newConnection; delete socket; } // Test that the socket does not enable the read notifications in bind() void tst_QTcpSocket::readNotificationsAfterBind() { QFETCH_GLOBAL(bool, setProxy); if (setProxy) return; QAbstractSocket socket(QAbstractSocket::TcpSocket, nullptr); QVERIFY2(socket.bind(), "Bind error!"); connect(&socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), &QTestEventLoop::instance(), SLOT(exitLoop())); QSignalSpy spyReadyRead(&socket, SIGNAL(readyRead())); socket.connectToHost(QtNetworkSettings::serverName(), 12346); QTestEventLoop::instance().enterLoop(10); QVERIFY2(!QTestEventLoop::instance().timeout(), "Connection to closed port timed out instead of refusing, something is wrong"); QVERIFY2(socket.state() == QAbstractSocket::UnconnectedState, "Socket connected unexpectedly!"); QCOMPARE(spyReadyRead.count(), 0); } QTEST_MAIN(tst_QTcpSocket) #include "tst_qtcpsocket.moc"