diff options
Diffstat (limited to 'tests/auto/network/access')
-rw-r--r-- | tests/auto/network/access/hpack/tst_hpack.cpp | 12 | ||||
-rw-r--r-- | tests/auto/network/access/hsts/tst_qhsts.cpp | 72 | ||||
-rw-r--r-- | tests/auto/network/access/http2/http2srv.cpp | 217 | ||||
-rw-r--r-- | tests/auto/network/access/http2/http2srv.h | 46 | ||||
-rw-r--r-- | tests/auto/network/access/http2/tst_http2.cpp | 94 | ||||
-rw-r--r-- | tests/auto/network/access/qftp/tst_qftp.cpp | 3 | ||||
-rw-r--r-- | tests/auto/network/access/qnetworkdiskcache/tst_qnetworkdiskcache.cpp | 15 | ||||
-rw-r--r-- | tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp | 10 |
8 files changed, 360 insertions, 109 deletions
diff --git a/tests/auto/network/access/hpack/tst_hpack.cpp b/tests/auto/network/access/hpack/tst_hpack.cpp index bd337c9f5f..810745a065 100644 --- a/tests/auto/network/access/hpack/tst_hpack.cpp +++ b/tests/auto/network/access/hpack/tst_hpack.cpp @@ -273,13 +273,13 @@ void tst_Hpack::bitstreamCompression() std::vector<uchar> buffer; BitOStream out(buffer); for (unsigned i = 0; i < nValues; ++i) { - const bool isString = std::rand() % 1000 > 500; + const bool isString = QRandomGenerator::global()->bounded(1000) > 500; isA.push_back(isString); if (!isString) { - integers.push_back(std::rand() % 1000); + integers.push_back(QRandomGenerator::global()->bounded(1000u)); out.write(integers.back()); } else { - const auto start = std::rand() % (bytes.length() / 2); + const auto start = QRandomGenerator::global()->bounded(uint(bytes.length()) / 2); auto end = start * 2; if (!end) end = bytes.length() / 2; @@ -287,7 +287,7 @@ void tst_Hpack::bitstreamCompression() const auto &s = strings.back(); totalStringBytes += s.size(); QByteArray data(s.c_str(), int(s.size())); - const bool compressed(std::rand() % 1000 > 500); + const bool compressed(QRandomGenerator::global()->bounded(1000) > 500); out.write(data, compressed); } } @@ -442,8 +442,8 @@ void tst_Hpack::lookupTableDynamic() // Strings are repeating way too often, I want to // have at least some items really evicted and not found, // therefore these weird dances with start/len. - const quint32 start = std::rand() % (dataSize - 10); - quint32 len = std::rand() % (dataSize - start); + const quint32 start = QRandomGenerator::global()->bounded(dataSize - 10); + quint32 len = QRandomGenerator::global()->bounded(dataSize - start); if (!len) len = 1; diff --git a/tests/auto/network/access/hsts/tst_qhsts.cpp b/tests/auto/network/access/hsts/tst_qhsts.cpp index 656516f46b..d72991a2eb 100644 --- a/tests/auto/network/access/hsts/tst_qhsts.cpp +++ b/tests/auto/network/access/hsts/tst_qhsts.cpp @@ -32,7 +32,9 @@ #include <QtCore/qvector.h> #include <QtCore/qpair.h> #include <QtCore/qurl.h> +#include <QtCore/qdir.h> +#include <QtNetwork/private/qhstsstore_p.h> #include <QtNetwork/private/qhsts_p.h> QT_USE_NAMESPACE @@ -46,6 +48,7 @@ private Q_SLOTS: void testMultilpeKnownHosts(); void testPolicyExpiration(); void testSTSHeaderParser(); + void testStore(); }; void tst_QHsts::testSingleKnownHost_data() @@ -313,6 +316,75 @@ void tst_QHsts::testSTSHeaderParser() QVERIFY(!parser.expirationDate().isValid()); } +const QLatin1String storeDir("."); + +struct TestStoreDeleter +{ + ~TestStoreDeleter() + { + QDir cwd; + if (!cwd.remove(QHstsStore::absoluteFilePath(storeDir))) + qWarning() << "tst_QHsts::testStore: failed to remove the hsts store file"; + } +}; + +void tst_QHsts::testStore() +{ + // Delete the store's file after we finish the test. + TestStoreDeleter cleaner; + + const QUrl exampleCom(QStringLiteral("http://example.com")); + const QUrl subDomain(QStringLiteral("http://subdomain.example.com")); + const QDateTime validDate(QDateTime::currentDateTimeUtc().addDays(1)); + + { + // We start from an empty cache and empty store: + QHstsCache cache; + QHstsStore store(storeDir); + cache.setStore(&store); + QVERIFY(!cache.isKnownHost(exampleCom)); + QVERIFY(!cache.isKnownHost(subDomain)); + // (1) This will also store the policy: + cache.updateKnownHost(exampleCom, validDate, true); + QVERIFY(cache.isKnownHost(exampleCom)); + QVERIFY(cache.isKnownHost(subDomain)); + } + { + // Test the policy stored at (1): + QHstsCache cache; + QHstsStore store(storeDir); + cache.setStore(&store); + QVERIFY(cache.isKnownHost(exampleCom)); + QVERIFY(cache.isKnownHost(subDomain)); + // (2) Remove subdomains: + cache.updateKnownHost(exampleCom, validDate, false); + QVERIFY(!cache.isKnownHost(subDomain)); + } + { + // Test the previous update (2): + QHstsCache cache; + QHstsStore store(storeDir); + cache.setStore(&store); + QVERIFY(cache.isKnownHost(exampleCom)); + QVERIFY(!cache.isKnownHost(subDomain)); + } + { + QHstsCache cache; + cache.updateKnownHost(subDomain, validDate, false); + QVERIFY(cache.isKnownHost(subDomain)); + QHstsStore store(storeDir); + // (3) This should store policy from cache, over old policy from store: + cache.setStore(&store); + } + { + // Test that (3) was stored: + QHstsCache cache; + QHstsStore store(storeDir); + cache.setStore(&store); + QVERIFY(cache.isKnownHost(subDomain)); + } +} + QTEST_MAIN(tst_QHsts) #include "tst_qhsts.moc" diff --git a/tests/auto/network/access/http2/http2srv.cpp b/tests/auto/network/access/http2/http2srv.cpp index d0686eb01c..69e480b164 100644 --- a/tests/auto/network/access/http2/http2srv.cpp +++ b/tests/auto/network/access/http2/http2srv.cpp @@ -76,13 +76,11 @@ void fill_push_header(const HttpHeader &originalRequest, HttpHeader &promisedReq } -Http2Server::Http2Server(bool h2c, const Http2Settings &ss, const Http2Settings &cs) +Http2Server::Http2Server(bool h2c, const Http2::RawSettings &ss, const Http2::RawSettings &cs) : serverSettings(ss), + expectedClientSettings(cs), clearTextHTTP2(h2c) { - for (const auto &s : cs) - expectedClientSettings[quint16(s.identifier)] = s.value; - responseBody = "<html>\n" "<head>\n" "<title>Sample \"Hello, World\" Application</title>\n" @@ -132,8 +130,23 @@ void Http2Server::startServer() if (!clearTextHTTP2) return; #endif - if (listen()) + if (listen()) { + if (clearTextHTTP2) + authority = QStringLiteral("127.0.0.1:%1").arg(serverPort()).toLatin1(); emit serverStarted(serverPort()); + } +} + +bool Http2Server::sendProtocolSwitchReply() +{ + Q_ASSERT(socket); + Q_ASSERT(clearTextHTTP2 && upgradeProtocol); + // The first and the last HTTP/1.1 response we send: + const char response[] = "HTTP/1.1 101 Switching Protocols\r\n" + "Connection: Upgrade\r\n" + "Upgrade: h2c\r\n\r\n"; + const qint64 size = sizeof response - 1; + return socket->write(response, size) == size; } void Http2Server::sendServerSettings() @@ -144,11 +157,11 @@ void Http2Server::sendServerSettings() return; writer.start(FrameType::SETTINGS, FrameFlag::EMPTY, connectionStreamID); - for (const auto &s : serverSettings) { - writer.append(s.identifier); - writer.append(s.value); - if (s.identifier == Settings::INITIAL_WINDOW_SIZE_ID) - streamRecvWindowSize = s.value; + for (auto it = serverSettings.cbegin(); it != serverSettings.cend(); ++it) { + writer.append(it.key()); + writer.append(it.value()); + if (it.key() == Settings::INITIAL_WINDOW_SIZE_ID) + streamRecvWindowSize = it.value(); } writer.write(*socket); // Now, let's update our peer on a session recv window size: @@ -232,6 +245,7 @@ void Http2Server::incomingConnection(qintptr socketDescriptor) Q_ASSERT(set); // Stop listening: close(); + upgradeProtocol = true; QMetaObject::invokeMethod(this, "connectionEstablished", Qt::QueuedConnection); } else { @@ -269,25 +283,83 @@ void Http2Server::incomingConnection(qintptr socketDescriptor) quint32 Http2Server::clientSetting(Http2::Settings identifier, quint32 defaultValue) { - const auto it = expectedClientSettings.find(quint16(identifier)); + const auto it = expectedClientSettings.find(identifier); if (it != expectedClientSettings.end()) - return it->second; + return it.value(); return defaultValue; } +bool Http2Server::readMethodLine() +{ + // We know for sure that Qt did the right thing sending us the correct + // Request-line with CRLF at the end ... + // We're overly simplistic here but all we need to know - the method. + while (socket->bytesAvailable()) { + char c = 0; + if (socket->read(&c, 1) != 1) + return false; + if (c == '\n' && requestLine.endsWith('\r')) { + if (requestLine.startsWith("GET")) + requestType = QHttpNetworkRequest::Get; + else if (requestLine.startsWith("POST")) + requestType = QHttpNetworkRequest::Post; + else + requestType = QHttpNetworkRequest::Custom; // 'invalid'. + requestLine.clear(); + + return true; + } else { + requestLine.append(c); + } + } + + return false; +} + +bool Http2Server::verifyProtocolUpgradeRequest() +{ + Q_ASSERT(protocolUpgradeHandler.data()); + + bool connectionOk = false; + bool upgradeOk = false; + bool settingsOk = false; + + QHttpNetworkReplyPrivate *firstRequestReader = protocolUpgradeHandler->d_func(); + + // That's how we append them, that's what I expect to find: + for (const auto &header : firstRequestReader->fields) { + if (header.first == "Connection") + connectionOk = header.second.contains("Upgrade, HTTP2-Settings"); + else if (header.first == "Upgrade") + upgradeOk = header.second.contains("h2c"); + else if (header.first == "HTTP2-Settings") + settingsOk = true; + } + + return connectionOk && upgradeOk && settingsOk; +} + +void Http2Server::triggerGOAWAYEmulation() +{ + Q_ASSERT(testingGOAWAY); + auto timer = new QTimer(this); + timer->setSingleShot(true); + connect(timer, &QTimer::timeout, [this]() { + sendGOAWAY(quint32(connectionStreamID), quint32(INTERNAL_ERROR), 0); + }); + timer->start(goawayTimeout); +} + void Http2Server::connectionEstablished() { using namespace Http2; - if (testingGOAWAY) { - auto timer = new QTimer(this); - timer->setSingleShot(true); - connect(timer, &QTimer::timeout, [this]() { - sendGOAWAY(quint32(connectionStreamID), quint32(INTERNAL_ERROR), 0); - }); - timer->start(goawayTimeout); - return; - } + if (testingGOAWAY && !clearTextHTTP2) + return triggerGOAWAYEmulation(); + + // For clearTextHTTP2 we first have to respond with 'protocol switch' + // and then continue with whatever logic we have (testingGOAWAY or not), + // otherwise our 'peer' cannot process HTTP/2 frames yet. connect(socket.data(), SIGNAL(readyRead()), this, SLOT(readReady())); @@ -296,9 +368,17 @@ void Http2Server::connectionEstablished() waitingClientAck = false; waitingClientSettings = false; settingsSent = false; - // We immediately send our settings so that our client - // can use flow control correctly. - sendServerSettings(); + + if (clearTextHTTP2) { + requestLine.clear(); + // Now we have to handle HTTP/1.1 request. We use Get/Post in our test, + // so set requestType to something unsupported: + requestType = QHttpNetworkRequest::Options; + } else { + // We immediately send our settings so that our client + // can use flow control correctly. + sendServerSettings(); + } if (socket->bytesAvailable()) readReady(); @@ -328,7 +408,9 @@ void Http2Server::readReady() if (connectionError) return; - if (waitingClientPreface) { + if (upgradeProtocol) { + handleProtocolUpgrade(); + } else if (waitingClientPreface) { handleConnectionPreface(); } else { const auto status = reader.read(*socket); @@ -348,6 +430,79 @@ void Http2Server::readReady() QMetaObject::invokeMethod(this, "readReady", Qt::QueuedConnection); } +void Http2Server::handleProtocolUpgrade() +{ + using ReplyPrivate = QHttpNetworkReplyPrivate; + Q_ASSERT(upgradeProtocol); + + if (!protocolUpgradeHandler.data()) + protocolUpgradeHandler.reset(new Http11Reply); + + QHttpNetworkReplyPrivate *firstRequestReader = protocolUpgradeHandler->d_func(); + + // QHttpNetworkReplyPrivate parses ... reply. It will, unfortunately, fail + // on the first line ... which is a part of request. So we read this line + // and extract the method first. + if (firstRequestReader->state == ReplyPrivate::NothingDoneState) { + if (!readMethodLine()) + return; + + if (requestType != QHttpNetworkRequest::Get && requestType != QHttpNetworkRequest::Post) { + emit invalidRequest(1); + return; + } + + firstRequestReader->state = ReplyPrivate::ReadingHeaderState; + } + + if (!socket->bytesAvailable()) + return; + + if (firstRequestReader->state == ReplyPrivate::ReadingHeaderState) + firstRequestReader->readHeader(socket.data()); + else if (firstRequestReader->state == ReplyPrivate::ReadingDataState) + firstRequestReader->readBodyFast(socket.data(), &firstRequestReader->responseData); + + switch (firstRequestReader->state) { + case ReplyPrivate::ReadingHeaderState: + return; + case ReplyPrivate::ReadingDataState: + if (requestType == QHttpNetworkRequest::Post) + return; + break; + case ReplyPrivate::AllDoneState: + break; + default: + socket->close(); + return; + } + + if (!verifyProtocolUpgradeRequest() || !sendProtocolSwitchReply()) { + socket->close(); + return; + } + + upgradeProtocol = false; + protocolUpgradeHandler.reset(nullptr); + + if (testingGOAWAY) + return triggerGOAWAYEmulation(); + + // HTTP/1.1 'fields' we have in firstRequestRead are useless (they are not + // even allowed in HTTP/2 header). Let's pretend we have received + // valid HTTP/2 headers and can extract fields we need: + HttpHeader h2header; + h2header.push_back(HeaderField(":scheme", "http")); // we are in clearTextHTTP2 mode. + h2header.push_back(HeaderField(":authority", authority)); + activeRequests[1] = std::move(h2header); + // After protocol switch we immediately send our SETTINGS. + sendServerSettings(); + if (requestType == QHttpNetworkRequest::Get) + emit receivedRequest(1); + else + emit receivedData(1); +} + void Http2Server::handleConnectionPreface() { Q_ASSERT(waitingClientPreface); @@ -382,6 +537,16 @@ void Http2Server::handleIncomingFrame() // 7. RST_STREAM // 8. GOAWAY + if (testingGOAWAY) { + // GOAWAY test is simplistic for now: after HTTP/2 was + // negotiated (via ALPN/NPN or a protocol switch), send + // a GOAWAY frame after some (probably non-zero) timeout. + // We do not handle any frames, but timeout gives QNAM + // more time to initiate more streams and thus make the + // test more interesting/complex (on a client side). + return; + } + inboundFrame = std::move(reader.inboundFrame()); if (continuedRequest.size()) { @@ -456,7 +621,7 @@ void Http2Server::handleSETTINGS() const auto notFound = expectedClientSettings.end(); while (src != end) { - const auto id = qFromBigEndian<quint16>(src); + const auto id = Http2::Settings(qFromBigEndian<quint16>(src)); const auto value = qFromBigEndian<quint32>(src + 2); if (expectedClientSettings.find(id) == notFound || expectedClientSettings[id] != value) { diff --git a/tests/auto/network/access/http2/http2srv.h b/tests/auto/network/access/http2/http2srv.h index 63a4a4c8e9..ff4a1319e2 100644 --- a/tests/auto/network/access/http2/http2srv.h +++ b/tests/auto/network/access/http2/http2srv.h @@ -29,11 +29,14 @@ #ifndef HTTP2SRV_H #define HTTP2SRV_H +#include <QtNetwork/private/qhttpnetworkrequest_p.h> +#include <QtNetwork/private/qhttpnetworkreply_p.h> #include <QtNetwork/private/http2protocol_p.h> #include <QtNetwork/private/http2frames_p.h> #include <QtNetwork/private/hpack_p.h> #include <QtNetwork/qabstractsocket.h> +#include <QtCore/qsharedpointer.h> #include <QtCore/qscopedpointer.h> #include <QtNetwork/qtcpserver.h> #include <QtCore/qbytearray.h> @@ -45,25 +48,25 @@ QT_BEGIN_NAMESPACE -struct Http2Setting +// At the moment we do not have any public API parsing HTTP headers. Even worse - +// the code that can do this exists only in QHttpNetworkReplyPrivate class. +// To be able to access reply's d_func() we have these classes: +class Http11ReplyPrivate : public QHttpNetworkReplyPrivate { - Http2::Settings identifier; - quint32 value = 0; - - Http2Setting(Http2::Settings ident, quint32 v) - : identifier(ident), - value(v) - {} }; -using Http2Settings = std::vector<Http2Setting>; +class Http11Reply : public QHttpNetworkReply +{ +public: + Q_DECLARE_PRIVATE(Http11Reply) +}; class Http2Server : public QTcpServer { Q_OBJECT public: - Http2Server(bool clearText, const Http2Settings &serverSettings, - const Http2Settings &clientSettings); + Http2Server(bool clearText, const Http2::RawSettings &serverSettings, + const Http2::RawSettings &clientSettings); ~Http2Server(); @@ -75,6 +78,7 @@ public: // Invokables, since we can call them from the main thread, // but server (can) work on its own thread. Q_INVOKABLE void startServer(); + bool sendProtocolSwitchReply(); Q_INVOKABLE void sendServerSettings(); Q_INVOKABLE void sendGOAWAY(quint32 streamID, quint32 error, quint32 lastStreamID); @@ -82,6 +86,7 @@ public: Q_INVOKABLE void sendDATA(quint32 streamID, quint32 windowSize); Q_INVOKABLE void sendWINDOW_UPDATE(quint32 streamID, quint32 delta); + Q_INVOKABLE void handleProtocolUpgrade(); Q_INVOKABLE void handleConnectionPreface(); Q_INVOKABLE void handleIncomingFrame(); Q_INVOKABLE void handleSETTINGS(); @@ -114,6 +119,9 @@ private: void incomingConnection(qintptr socketDescriptor) Q_DECL_OVERRIDE; quint32 clientSetting(Http2::Settings identifier, quint32 defaultValue); + bool readMethodLine(); + bool verifyProtocolUpgradeRequest(); + void triggerGOAWAYEmulation(); QScopedPointer<QAbstractSocket> socket; @@ -123,8 +131,8 @@ private: bool settingsSent = false; bool waitingClientAck = false; - Http2Settings serverSettings; - std::map<quint16, quint32> expectedClientSettings; + Http2::RawSettings serverSettings; + Http2::RawSettings expectedClientSettings; bool connectionError = false; @@ -166,6 +174,18 @@ private: bool testingGOAWAY = false; int goawayTimeout = 0; + // Clear text HTTP/2, we have to deal with the protocol upgrade request + // from the initial HTTP/1.1 request. + bool upgradeProtocol = false; + QByteArray requestLine; + QHttpNetworkRequest::Operation requestType; + // We need QHttpNetworkReply (actually its private d-object) to handle the + // first HTTP/1.1 request. QHttpNetworkReplyPrivate does parsing + in case + // of POST it is also reading the body for us. + QScopedPointer<Http11Reply> protocolUpgradeHandler; + // We need it for PUSH_PROMISE, with the correct port number appended, + // when replying to essentially 1.1 request. + QByteArray authority; protected slots: void ignoreErrorSlot(); }; diff --git a/tests/auto/network/access/http2/tst_http2.cpp b/tests/auto/network/access/http2/tst_http2.cpp index d7a57f5e26..51e1849512 100644 --- a/tests/auto/network/access/http2/tst_http2.cpp +++ b/tests/auto/network/access/http2/tst_http2.cpp @@ -30,6 +30,7 @@ #include "http2srv.h" +#include <QtNetwork/private/http2protocol_p.h> #include <QtNetwork/qnetworkaccessmanager.h> #include <QtNetwork/qnetworkrequest.h> #include <QtNetwork/qnetworkreply.h> @@ -47,10 +48,12 @@ #include <cstdlib> #include <string> -// At the moment our HTTP/2 imlpementation requires ALPN and this means OpenSSL. #if !defined(QT_NO_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10002000L && !defined(OPENSSL_NO_TLSEXT) +// HTTP/2 over TLS requires ALPN/NPN to negotiate the protocol version. const bool clearTextHTTP2 = false; #else +// No ALPN/NPN support to negotiate HTTP/2, we'll use cleartext 'h2c' with +// a protocol upgrade procedure. const bool clearTextHTTP2 = true; #endif @@ -94,8 +97,8 @@ private: // small payload. void runEventLoop(int ms = 5000); void stopEventLoop(); - Http2Server *newServer(const Http2Settings &serverSettings, - const Http2Settings &clientSettings = defaultClientSettings); + Http2Server *newServer(const Http2::RawSettings &serverSettings, + const Http2::ProtocolParameters &clientSettings = {}); // Send a get or post request, depending on a payload (empty or not). void sendRequest(int streamNumber, QNetworkRequest::Priority priority = QNetworkRequest::NormalPriority, @@ -116,13 +119,10 @@ private: bool prefaceOK = false; bool serverGotSettingsACK = false; - static const Http2Settings defaultServerSettings; - static const Http2Settings defaultClientSettings; + static const Http2::RawSettings defaultServerSettings; }; -const Http2Settings tst_Http2::defaultServerSettings{{Http2::Settings::MAX_CONCURRENT_STREAMS_ID, 100}}; -const Http2Settings tst_Http2::defaultClientSettings{{Http2::Settings::MAX_FRAME_SIZE_ID, quint32(Http2::maxFrameSize)}, - {Http2::Settings::ENABLE_PUSH_ID, quint32(0)}}; +const Http2::RawSettings tst_Http2::defaultServerSettings{{Http2::Settings::MAX_CONCURRENT_STREAMS_ID, 100}}; namespace { @@ -139,27 +139,6 @@ struct ServerDeleter using ServerPtr = QScopedPointer<Http2Server, ServerDeleter>; -struct EnvVarGuard -{ - EnvVarGuard(const char *name, const QByteArray &value) - : varName(name), - prevValue(qgetenv(name)) - { - Q_ASSERT(name); - qputenv(name, value); - } - ~EnvVarGuard() - { - if (prevValue.size()) - qputenv(varName.c_str(), prevValue); - else - qunsetenv(varName.c_str()); - } - - const std::string varName; - const QByteArray prevValue; -}; - } // unnamed namespace tst_Http2::tst_Http2() @@ -238,12 +217,14 @@ void tst_Http2::multipleRequests() // Just to make the order a bit more interesting // we'll index this randomly: - QNetworkRequest::Priority priorities[] = {QNetworkRequest::HighPriority, - QNetworkRequest::NormalPriority, - QNetworkRequest::LowPriority}; + const QNetworkRequest::Priority priorities[] = { + QNetworkRequest::HighPriority, + QNetworkRequest::NormalPriority, + QNetworkRequest::LowPriority + }; for (int i = 0; i < nRequests; ++i) - sendRequest(i, priorities[std::rand() % 3]); + sendRequest(i, priorities[QRandomGenerator::global()->bounded(3)]); runEventLoop(); @@ -255,11 +236,11 @@ void tst_Http2::multipleRequests() void tst_Http2::flowControlClientSide() { // Create a server but impose limits: - // 1. Small MAX frame size, so we test CONTINUATION frames. - // 2. Small client windows so server responses cause client streams - // to suspend and server sends WINDOW_UPDATE frames. - // 3. Few concurrent streams, to test protocol handler can resume - // suspended requests. + // 1. Small client receive windows so server's responses cause client + // streams to suspend and protocol handler has to send WINDOW_UPDATE + // frames. + // 2. Few concurrent streams supported by the server, to test protocol + // handler in the client can suspend and then resume streams. using namespace Http2; clearHTTP2State(); @@ -268,11 +249,20 @@ void tst_Http2::flowControlClientSide() nRequests = 10; windowUpdates = 0; - const Http2Settings serverSettings = {{Settings::MAX_CONCURRENT_STREAMS_ID, 3}}; + Http2::ProtocolParameters params; + // A small window size for a session, and even a smaller one per stream - + // this will result in WINDOW_UPDATE frames both on connection stream and + // per stream. + params.maxSessionReceiveWindowSize = Http2::defaultSessionWindowSize * 5; + params.settingsFrameData[Settings::INITIAL_WINDOW_SIZE_ID] = Http2::defaultSessionWindowSize; + // Inform our manager about non-default settings: + manager.setProperty(Http2::http2ParametersPropertyName, QVariant::fromValue(params)); - ServerPtr srv(newServer(serverSettings)); + const Http2::RawSettings serverSettings = {{Settings::MAX_CONCURRENT_STREAMS_ID, quint32(3)}}; + ServerPtr srv(newServer(serverSettings, params)); - const QByteArray respond(int(Http2::defaultSessionWindowSize * 50), 'x'); + + const QByteArray respond(int(Http2::defaultSessionWindowSize * 10), 'x'); srv->setResponseBody(respond); QMetaObject::invokeMethod(srv.data(), "startServer", Qt::QueuedConnection); @@ -306,7 +296,7 @@ void tst_Http2::flowControlServerSide() serverPort = 0; nRequests = 30; - const Http2Settings serverSettings = {{Settings::MAX_CONCURRENT_STREAMS_ID, 7}}; + const Http2::RawSettings serverSettings = {{Settings::MAX_CONCURRENT_STREAMS_ID, 7}}; ServerPtr srv(newServer(serverSettings)); @@ -338,11 +328,12 @@ void tst_Http2::pushPromise() serverPort = 0; nRequests = 1; - const EnvVarGuard env("QT_HTTP2_ENABLE_PUSH_PROMISE", "1"); - const Http2Settings clientSettings{{Settings::MAX_FRAME_SIZE_ID, quint32(Http2::maxFrameSize)}, - {Settings::ENABLE_PUSH_ID, quint32(1)}}; + Http2::ProtocolParameters params; + // Defaults are good, except ENABLE_PUSH: + params.settingsFrameData[Settings::ENABLE_PUSH_ID] = 1; + manager.setProperty(Http2::http2ParametersPropertyName, QVariant::fromValue(params)); - ServerPtr srv(newServer(defaultServerSettings, clientSettings)); + ServerPtr srv(newServer(defaultServerSettings, params)); srv->enablePushPromise(true, QByteArray("/script.js")); QMetaObject::invokeMethod(srv.data(), "startServer", Qt::QueuedConnection); @@ -416,7 +407,7 @@ void tst_Http2::goaway() serverPort = 0; nRequests = 3; - ServerPtr srv(newServer(defaultServerSettings, defaultClientSettings)); + ServerPtr srv(newServer(defaultServerSettings)); srv->emulateGOAWAY(responseTimeoutMS); QMetaObject::invokeMethod(srv.data(), "startServer", Qt::QueuedConnection); runEventLoop(); @@ -459,6 +450,7 @@ void tst_Http2::clearHTTP2State() windowUpdates = 0; prefaceOK = false; serverGotSettingsACK = false; + manager.setProperty(Http2::http2ParametersPropertyName, QVariant()); } void tst_Http2::runEventLoop(int ms) @@ -474,11 +466,12 @@ void tst_Http2::stopEventLoop() eventLoop.quit(); } -Http2Server *tst_Http2::newServer(const Http2Settings &serverSettings, - const Http2Settings &clientSettings) +Http2Server *tst_Http2::newServer(const Http2::RawSettings &serverSettings, + const Http2::ProtocolParameters &clientSettings) { using namespace Http2; - auto srv = new Http2Server(clearTextHTTP2, serverSettings, clientSettings); + auto srv = new Http2Server(clearTextHTTP2, serverSettings, + clientSettings.settingsFrameData); using Srv = Http2Server; using Cl = tst_Http2; @@ -507,6 +500,7 @@ void tst_Http2::sendRequest(int streamNumber, QNetworkRequest request(url); request.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, QVariant(true)); + request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("text/plain")); request.setPriority(priority); QNetworkReply *reply = nullptr; diff --git a/tests/auto/network/access/qftp/tst_qftp.cpp b/tests/auto/network/access/qftp/tst_qftp.cpp index fba0508f04..4bc43f068c 100644 --- a/tests/auto/network/access/qftp/tst_qftp.cpp +++ b/tests/auto/network/access/qftp/tst_qftp.cpp @@ -276,8 +276,7 @@ void tst_QFtp::init() inFileDirExistsFunction = false; - srand(time(0)); - uniqueExtension = QString::number((quintptr)this) + QString::number(rand()) + uniqueExtension = QString::number((quintptr)this) + QString::number(QRandomGenerator::global()->generate()) + QString::number((qulonglong)time(0)); } diff --git a/tests/auto/network/access/qnetworkdiskcache/tst_qnetworkdiskcache.cpp b/tests/auto/network/access/qnetworkdiskcache/tst_qnetworkdiskcache.cpp index af6f2d0e10..856033fb63 100644 --- a/tests/auto/network/access/qnetworkdiskcache/tst_qnetworkdiskcache.cpp +++ b/tests/auto/network/access/qnetworkdiskcache/tst_qnetworkdiskcache.cpp @@ -30,6 +30,7 @@ #include <QtTest/QtTest> #include <QtNetwork/QtNetwork> #include <qnetworkdiskcache.h> +#include <qrandom.h> #include <algorithm> @@ -281,6 +282,7 @@ void tst_QNetworkDiskCache::clear() // don't delete files that it didn't create QTemporaryFile file(cacheDirectory + "/XXXXXX"); if (file.open()) { + file.fileName(); // make sure it exists with a name QCOMPARE(countFiles(cacheDirectory).count(), NUM_SUBDIRECTORIES + 3); cache.clear(); QCOMPARE(countFiles(cacheDirectory).count(), NUM_SUBDIRECTORIES + 3); @@ -692,25 +694,25 @@ public: if (write) { QNetworkCacheMetaData m; - if (qrand() % 2 == 0) + if (QRandomGenerator::global()->bounded(2) == 0) m = metaData; else m = metaData2; - if (qrand() % 20 == 1) { + if (QRandomGenerator::global()->bounded(20) == 1) { //qDebug() << "write update"; cache.updateMetaData(m); continue; } QIODevice *device = cache.prepare(m); - if (qrand() % 20 == 1) { + if (QRandomGenerator::global()->bounded(20) == 1) { //qDebug() << "write remove"; cache.remove(url); continue; } QVERIFY(device); - if (qrand() % 2 == 0) + if (QRandomGenerator::global()->bounded(2) == 0) device->write(longString); else device->write(longString2); @@ -739,9 +741,9 @@ public: delete d; } } - if (qrand() % 5 == 1) + if (QRandomGenerator::global()->bounded(5) == 1) cache.remove(url); - if (qrand() % 5 == 1) + if (QRandomGenerator::global()->bounded(5) == 1) cache.clear(); sleep(0); } @@ -790,7 +792,6 @@ void tst_QNetworkDiskCache::sync() return; QTime midnight(0, 0, 0); - qsrand(midnight.secsTo(QTime::currentTime())); Runner reader(tempDir.path()); reader.dt = QDateTime::currentDateTime(); reader.write = false; diff --git a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp index 3a752c0748..fffe853c14 100644 --- a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp +++ b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp @@ -33,6 +33,7 @@ #include <QtCore/QUrl> #include <QtCore/QEventLoop> #include <QtCore/QFile> +#include <QtCore/QRandomGenerator> #include <QtCore/QSharedPointer> #include <QtCore/QScopedPointer> #include <QtCore/QTemporaryFile> @@ -120,12 +121,11 @@ class tst_QNetworkReply: public QObject static QString createUniqueExtension() { if (!seedCreated) { - qsrand(QTime(0,0,0).msecsTo(QTime::currentTime()) + QCoreApplication::applicationPid()); seedCreated = true; // not thread-safe, but who cares } return QString::number(QTime(0, 0, 0).msecsTo(QTime::currentTime())) + QLatin1Char('-') + QString::number(QCoreApplication::applicationPid()) - + QLatin1Char('-') + QString::number(qrand()); + + QLatin1Char('-') + QString::number(QRandomGenerator::global()->generate()); } static QString tempRedirectReplyStr() { @@ -4863,7 +4863,7 @@ void tst_QNetworkReply::ioPostToHttpsUploadProgress() // server send the data much faster than expected. // So better provide random data that cannot be compressed. for (int i = 0; i < wantedSize; ++i) - sourceFile += (char)qrand(); + sourceFile += (char)QRandomGenerator::global()->generate(); // emulate a minimal https server SslServer server; @@ -4943,7 +4943,7 @@ void tst_QNetworkReply::ioGetFromBuiltinHttp() // server send the data much faster than expected. // So better provide random data that cannot be compressed. for (int i = 0; i < wantedSize; ++i) - testData += (char)qrand(); + testData += (char)QRandomGenerator::global()->generate(); QByteArray httpResponse = QByteArray("HTTP/1.0 200 OK\r\nContent-Length: "); httpResponse += QByteArray::number(testData.size()); @@ -8801,7 +8801,7 @@ public slots: m_receivedData += data; if (!m_parsedHeaders && m_receivedData.contains("\r\n\r\n")) { m_parsedHeaders = true; - QTimer::singleShot(qrand()%60, this, SLOT(closeDelayed())); // simulate random network latency + QTimer::singleShot(QRandomGenerator::global()->bounded(60), this, SLOT(closeDelayed())); // simulate random network latency // This server simulates a web server connection closing, e.g. because of Apaches MaxKeepAliveRequests or KeepAliveTimeout // In this case QNAM needs to re-send the upload data but it had a bug which then corrupts the upload // This test catches that. |