From 13e0a36626bd75e631dd9536e795a494432b1945 Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Tue, 10 Sep 2019 09:04:17 +0200 Subject: Retire SPDY protocol implementation (Qt6) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As it was superseded by HTTP/2. Bye, Speedy. Since it's Qt 6, we also fix the attribute's enumerator to fit our coding convention with HTTP2AllowedAttribute becoming Http2AllowedAttribute, and the same for HTTP2WasUsedAttribute. tst_qnetworkreply in 'benchmark' directory of qtbase/tests was updated - we have the logic they tested in preConnectEncrypted in tst_http2 now. Manual qnetworkreply test was updated (instead of SPDY in NPN failure we can use H2, the second test was deleted - again, auto-tested in tst_http2). Change-Id: I559c140c333ddf72664911c6e275b1d0d2b980a9 Task-number: QTBUG-78255 Reviewed-by: MÃ¥rten Nordheim --- src/network/access/access.pri | 7 - src/network/access/qhttp2protocolhandler.cpp | 12 +- src/network/access/qhttpnetworkconnection.cpp | 54 +- src/network/access/qhttpnetworkconnection_p.h | 2 - .../access/qhttpnetworkconnectionchannel.cpp | 76 +- .../access/qhttpnetworkconnectionchannel_p.h | 7 +- src/network/access/qhttpnetworkreply.cpp | 14 +- src/network/access/qhttpnetworkreply_p.h | 12 +- src/network/access/qhttpnetworkrequest.cpp | 14 +- src/network/access/qhttpnetworkrequest_p.h | 4 - src/network/access/qhttpthreaddelegate.cpp | 19 +- src/network/access/qhttpthreaddelegate_p.h | 2 +- src/network/access/qnetworkaccessmanager.cpp | 8 +- src/network/access/qnetworkreplyhttpimpl.cpp | 22 +- src/network/access/qnetworkrequest.cpp | 18 - src/network/access/qnetworkrequest.h | 8 - src/network/access/qspdyprotocolhandler.cpp | 1304 -------------------- src/network/access/qspdyprotocolhandler_p.h | 232 ---- src/network/ssl/qsslconfiguration.cpp | 11 +- src/network/ssl/qsslconfiguration.h | 1 - tests/auto/network/access/access.pro | 1 - tests/auto/network/access/http2/tst_http2.cpp | 25 +- tests/auto/network/access/spdy/BLACKLIST | 7 - tests/auto/network/access/spdy/spdy.pro | 7 - tests/auto/network/access/spdy/tst_spdy.cpp | 693 ----------- .../access/qnetworkreply/tst_qnetworkreply.cpp | 34 +- tests/manual/qnetworkreply/main.cpp | 204 +-- 27 files changed, 96 insertions(+), 2702 deletions(-) delete mode 100644 src/network/access/qspdyprotocolhandler.cpp delete mode 100644 src/network/access/qspdyprotocolhandler_p.h delete mode 100644 tests/auto/network/access/spdy/BLACKLIST delete mode 100644 tests/auto/network/access/spdy/spdy.pro delete mode 100644 tests/auto/network/access/spdy/tst_spdy.cpp diff --git a/src/network/access/access.pri b/src/network/access/access.pri index cfb20dcd71..083fbbf5fd 100644 --- a/src/network/access/access.pri +++ b/src/network/access/access.pri @@ -114,11 +114,4 @@ qtConfig(http) { access/qhttpthreaddelegate_p.h \ access/qnetworkreplyhttpimpl_p.h \ access/qhttp2configuration.h - - qtConfig(ssl) { - SOURCES += \ - access/qspdyprotocolhandler.cpp - HEADERS += \ - access/qspdyprotocolhandler_p.h - } } diff --git a/src/network/access/qhttp2protocolhandler.cpp b/src/network/access/qhttp2protocolhandler.cpp index 9bf5547f15..ee6d493e85 100644 --- a/src/network/access/qhttp2protocolhandler.cpp +++ b/src/network/access/qhttp2protocolhandler.cpp @@ -332,13 +332,13 @@ bool QHttp2ProtocolHandler::sendRequest() // so we cannot create new streams. m_channel->emitFinishedWithError(QNetworkReply::ProtocolUnknownError, "GOAWAY received, cannot start a request"); - m_channel->spdyRequestsToSend.clear(); + m_channel->h2RequestsToSend.clear(); return false; } // Process 'fake' (created by QNetworkAccessManager::connectToHostEncrypted()) // requests first: - auto &requests = m_channel->spdyRequestsToSend; + auto &requests = m_channel->h2RequestsToSend; for (auto it = requests.begin(), endIt = requests.end(); it != endIt;) { const auto &pair = *it; const QString scheme(pair.first.url().scheme()); @@ -862,7 +862,7 @@ void QHttp2ProtocolHandler::handleGOAWAY() m_channel->emitFinishedWithError(QNetworkReply::ProtocolUnknownError, "GOAWAY received, cannot start a request"); // Also, prevent further calls to sendRequest: - m_channel->spdyRequestsToSend.clear(); + m_channel->h2RequestsToSend.clear(); QNetworkReply::NetworkError error = QNetworkReply::NoError; QString message; @@ -1281,7 +1281,7 @@ quint32 QHttp2ProtocolHandler::createNewStream(const HttpMessagePair &message, b const auto replyPrivate = reply->d_func(); replyPrivate->connection = m_connection; replyPrivate->connectionChannel = m_channel; - reply->setSpdyWasUsed(true); + reply->setHttp2WasUsed(true); streamIDs.insert(reply, newStreamID); connect(reply, SIGNAL(destroyed(QObject*)), this, SLOT(_q_replyDestroyed(QObject*))); @@ -1390,7 +1390,7 @@ void QHttp2ProtocolHandler::deleteActiveStream(quint32 streamID) } removeFromSuspended(streamID); - if (m_channel->spdyRequestsToSend.size()) + if (m_channel->h2RequestsToSend.size()) QMetaObject::invokeMethod(this, "sendRequest", Qt::QueuedConnection); } @@ -1509,7 +1509,7 @@ void QHttp2ProtocolHandler::initReplyFromPushPromise(const HttpMessagePair &mess Q_ASSERT(promisedData.contains(cacheKey)); auto promise = promisedData.take(cacheKey); Q_ASSERT(message.second); - message.second->setSpdyWasUsed(true); + message.second->setHttp2WasUsed(true); qCDebug(QT_HTTP2) << "found cached/promised response on stream" << promise.reservedID; diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp index 21c6359807..795efc91d7 100644 --- a/src/network/access/qhttpnetworkconnection.cpp +++ b/src/network/access/qhttpnetworkconnection.cpp @@ -82,9 +82,6 @@ QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(const QString &host hostName(hostName), port(port), encrypt(encrypt), delayIpv4(true) , activeChannelCount(type == QHttpNetworkConnection::ConnectionTypeHTTP2 || type == QHttpNetworkConnection::ConnectionTypeHTTP2Direct -#ifndef QT_NO_SSL - || type == QHttpNetworkConnection::ConnectionTypeSPDY -#endif ? 1 : defaultHttpChannelCount) , channelCount(defaultHttpChannelCount) #ifndef QT_NO_NETWORKPROXY @@ -93,9 +90,9 @@ QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(const QString &host , preConnectRequests(0) , connectionType(type) { - // We allocate all 6 channels even if it's SPDY or HTTP/2 enabled - // connection: in case the protocol negotiation via NPN/ALPN fails, - // we will have normally working HTTP/1.1. + // We allocate all 6 channels even if it's HTTP/2 enabled connection: + // in case the protocol negotiation via NPN/ALPN fails, we will have + // normally working HTTP/1.1. Q_ASSERT(channelCount >= activeChannelCount); channels = new QHttpNetworkConnectionChannel[channelCount]; } @@ -641,10 +638,10 @@ QHttpNetworkReply* QHttpNetworkConnectionPrivate::queueRequest(const QHttpNetwor break; } } - else { // SPDY, HTTP/2 ('h2' mode) + else { // HTTP/2 ('h2' mode) if (!pair.second->d_func()->requestIsPrepared) prepareRequest(pair); - channels[0].spdyRequestsToSend.insert(request.priority(), pair); + channels[0].h2RequestsToSend.insert(request.priority(), pair); } #ifndef Q_OS_WINRT @@ -680,7 +677,7 @@ void QHttpNetworkConnectionPrivate::fillHttp2Queue() for (auto &pair : highPriorityQueue) { if (!pair.second->d_func()->requestIsPrepared) prepareRequest(pair); - channels[0].spdyRequestsToSend.insert(QHttpNetworkRequest::HighPriority, pair); + channels[0].h2RequestsToSend.insert(QHttpNetworkRequest::HighPriority, pair); } highPriorityQueue.clear(); @@ -688,7 +685,7 @@ void QHttpNetworkConnectionPrivate::fillHttp2Queue() for (auto &pair : lowPriorityQueue) { if (!pair.second->d_func()->requestIsPrepared) prepareRequest(pair); - channels[0].spdyRequestsToSend.insert(pair.first.priority(), pair); + channels[0].h2RequestsToSend.insert(pair.first.priority(), pair); } lowPriorityQueue.clear(); @@ -984,12 +981,12 @@ void QHttpNetworkConnectionPrivate::removeReply(QHttpNetworkReply *reply) } } #ifndef QT_NO_SSL - // is the reply inside the SPDY pipeline of this channel already? - QMultiMap::iterator it = channels[i].spdyRequestsToSend.begin(); - QMultiMap::iterator end = channels[i].spdyRequestsToSend.end(); + // is the reply inside the H2 pipeline of this channel already? + QMultiMap::iterator it = channels[i].h2RequestsToSend.begin(); + QMultiMap::iterator end = channels[i].h2RequestsToSend.end(); for (; it != end; ++it) { if (it.value().second == reply) { - channels[i].spdyRequestsToSend.remove(it.key()); + channels[i].h2RequestsToSend.remove(it.key()); QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection); return; @@ -1068,9 +1065,8 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest() break; } case QHttpNetworkConnection::ConnectionTypeHTTP2Direct: - case QHttpNetworkConnection::ConnectionTypeHTTP2: - case QHttpNetworkConnection::ConnectionTypeSPDY: { - if (channels[0].spdyRequestsToSend.isEmpty() && channels[0].switchedToHttp2) + case QHttpNetworkConnection::ConnectionTypeHTTP2: { + if (channels[0].h2RequestsToSend.isEmpty() && channels[0].switchedToHttp2) return; if (networkLayerState == IPv4) @@ -1079,7 +1075,7 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest() channels[0].networkLayerPreference = QAbstractSocket::IPv6Protocol; channels[0].ensureConnection(); if (channels[0].socket && channels[0].socket->state() == QAbstractSocket::ConnectedState - && !channels[0].pendingEncrypt && channels[0].spdyRequestsToSend.size()) + && !channels[0].pendingEncrypt && channels[0].h2RequestsToSend.size()) channels[0].sendRequest(); break; } @@ -1234,19 +1230,18 @@ void QHttpNetworkConnectionPrivate::_q_hostLookupFinished(const QHostInfo &info) if (dequeueRequest(channels[0].socket)) { emitReplyError(channels[0].socket, channels[0].reply, QNetworkReply::HostNotFoundError); networkLayerState = QHttpNetworkConnectionPrivate::Unknown; - } else if (connectionType == QHttpNetworkConnection::ConnectionTypeSPDY - || connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2 + } else if (connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2 || connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) { - for (const HttpMessagePair &spdyPair : qAsConst(channels[0].spdyRequestsToSend)) { + for (const HttpMessagePair &h2Pair : qAsConst(channels[0].h2RequestsToSend)) { // emit error for all replies - QHttpNetworkReply *currentReply = spdyPair.second; + QHttpNetworkReply *currentReply = h2Pair.second; Q_ASSERT(currentReply); emitReplyError(channels[0].socket, currentReply, QNetworkReply::HostNotFoundError); } } else { // Should not happen: we start a host lookup before sending a request, - // so it's natural to have requests either in SPDY/HTTP/2 queue, - // or in low/high priority queues. + // so it's natural to have requests either in HTTP/2 queue, or in low/high + // priority queues. qWarning("QHttpNetworkConnectionPrivate::_q_hostLookupFinished" " could not de-queue request, failed to report HostNotFoundError"); networkLayerState = QHttpNetworkConnectionPrivate::Unknown; @@ -1576,17 +1571,12 @@ void QHttpNetworkConnectionPrivate::emitProxyAuthenticationRequired(const QHttpN pauseConnection(); QHttpNetworkReply *reply; if (connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2 - || connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2Direct -#if QT_CONFIG(ssl) - || connectionType == QHttpNetworkConnection::ConnectionTypeSPDY -#endif - ) { - + || connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) { // we choose the reply to emit the proxyAuth signal from somewhat arbitrarily, // but that does not matter because the signal will ultimately be emitted // by the QNetworkAccessManager. - Q_ASSERT(chan->spdyRequestsToSend.count() > 0); - reply = chan->spdyRequestsToSend.cbegin().value().second; + Q_ASSERT(chan->h2RequestsToSend.count() > 0); + reply = chan->h2RequestsToSend.cbegin().value().second; } else { // HTTP reply = chan->reply; } diff --git a/src/network/access/qhttpnetworkconnection_p.h b/src/network/access/qhttpnetworkconnection_p.h index 6808a0c0ac..fee84bb6c0 100644 --- a/src/network/access/qhttpnetworkconnection_p.h +++ b/src/network/access/qhttpnetworkconnection_p.h @@ -96,7 +96,6 @@ public: enum ConnectionType { ConnectionTypeHTTP, - ConnectionTypeSPDY, ConnectionTypeHTTP2, ConnectionTypeHTTP2Direct }; @@ -172,7 +171,6 @@ private: friend class QHttpNetworkConnectionChannel; friend class QHttp2ProtocolHandler; friend class QHttpProtocolHandler; - friend class QSpdyProtocolHandler; Q_PRIVATE_SLOT(d_func(), void _q_startNextRequest()) Q_PRIVATE_SLOT(d_func(), void _q_hostLookupFinished(QHostInfo)) diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp index 39f392a79b..c6470622ab 100644 --- a/src/network/access/qhttpnetworkconnectionchannel.cpp +++ b/src/network/access/qhttpnetworkconnectionchannel.cpp @@ -48,7 +48,6 @@ #include #include -#include #include #ifndef QT_NO_SSL @@ -931,7 +930,7 @@ void QHttpNetworkConnectionChannel::_q_connected() } else if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) { state = QHttpNetworkConnectionChannel::IdleState; protocolHandler.reset(new QHttp2ProtocolHandler(this)); - if (spdyRequestsToSend.count() > 0) { + if (h2RequestsToSend.count() > 0) { // In case our peer has sent us its settings (window size, max concurrent streams etc.) // let's give _q_receiveReply a chance to read them first ('invokeMethod', QueuedConnection). QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); @@ -1108,15 +1107,11 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket || !connection->d_func()->lowPriorityQueue.isEmpty()); if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2 - || connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct -#ifndef QT_NO_SSL - || connection->connectionType() == QHttpNetworkConnection::ConnectionTypeSPDY -#endif - ) { - QList spdyPairs = spdyRequestsToSend.values(); - for (int a = 0; a < spdyPairs.count(); ++a) { + || connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) { + QList h2Pairs = h2RequestsToSend.values(); + for (int a = 0; a < h2Pairs.count(); ++a) { // emit error for all replies - QHttpNetworkReply *currentReply = spdyPairs.at(a).second; + QHttpNetworkReply *currentReply = h2Pairs.at(a).second; Q_ASSERT(currentReply); emit currentReply->finishedWithError(errorCode, errorString); } @@ -1143,12 +1138,8 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket void QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator* auth) { if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2 - || connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct -#ifndef QT_NO_SSL - || connection->connectionType() == QHttpNetworkConnection::ConnectionTypeSPDY -#endif - ) { - if (spdyRequestsToSend.count() > 0) + || connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) { + if (h2RequestsToSend.count() > 0) connection->d_func()->emitProxyAuthenticationRequired(this, proxy, auth); } else { // HTTP // Need to dequeue the request before we can emit the error. @@ -1171,9 +1162,9 @@ void QHttpNetworkConnectionChannel::emitFinishedWithError(QNetworkReply::Network { if (reply) emit reply->finishedWithError(error, QHttpNetworkConnectionChannel::tr(message)); - QList spdyPairs = spdyRequestsToSend.values(); - for (int a = 0; a < spdyPairs.count(); ++a) { - QHttpNetworkReply *currentReply = spdyPairs.at(a).second; + QList h2Pairs = h2RequestsToSend.values(); + for (int a = 0; a < h2Pairs.count(); ++a) { + QHttpNetworkReply *currentReply = h2Pairs.at(a).second; Q_ASSERT(currentReply); emit currentReply->finishedWithError(error, QHttpNetworkConnectionChannel::tr(message)); } @@ -1195,12 +1186,6 @@ void QHttpNetworkConnectionChannel::_q_encrypted() QByteArray nextProtocol = sslSocket->sslConfiguration().nextNegotiatedProtocol(); if (nextProtocol == QSslConfiguration::NextProtocolHttp1_1) { // fall through to create a QHttpProtocolHandler - } else if (nextProtocol == QSslConfiguration::NextProtocolSpdy3_0) { - protocolHandler.reset(new QSpdyProtocolHandler(this)); - connection->setConnectionType(QHttpNetworkConnection::ConnectionTypeSPDY); - // no need to re-queue requests, if SPDY was enabled on the request it - // has gone to the SPDY queue already - break; } else if (nextProtocol == QSslConfiguration::ALPNProtocolHTTP2) { switchedToHttp2 = true; protocolHandler.reset(new QHttp2ProtocolHandler(this)); @@ -1229,8 +1214,6 @@ void QHttpNetworkConnectionChannel::_q_encrypted() // it again on other channels that our connection can create/open. if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2) protocols.removeAll(QSslConfiguration::ALPNProtocolHTTP2); - else if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeSPDY) - protocols.removeAll(QSslConfiguration::NextProtocolSpdy3_0); if (nProtocols > protocols.size()) { sslConfiguration->setAllowedNextProtocols(protocols); @@ -1240,13 +1223,13 @@ void QHttpNetworkConnectionChannel::_q_encrypted() } connection->setConnectionType(QHttpNetworkConnection::ConnectionTypeHTTP); - // We use only one channel for SPDY or HTTP/2, but normally six for + // We use only one channel for HTTP/2, but normally six for // HTTP/1.1 - let's restore this number to the reserved number of // channels: if (connection->d_func()->activeChannelCount < connection->d_func()->channelCount) { connection->d_func()->activeChannelCount = connection->d_func()->channelCount; - // re-queue requests from SPDY queue to HTTP queue, if any - requeueSpdyRequests(); + // re-queue requests from HTTP/2 queue to HTTP queue, if any + requeueHttp2Requests(); } break; } @@ -1266,11 +1249,9 @@ void QHttpNetworkConnectionChannel::_q_encrypted() state = QHttpNetworkConnectionChannel::IdleState; pendingEncrypt = false; - if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeSPDY || - connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2 || + if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2 || connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) { - // we call setSpdyWasUsed(true) on the replies in the SPDY handler when the request is sent - if (spdyRequestsToSend.count() > 0) { + if (h2RequestsToSend.count() > 0) { // In case our peer has sent us its settings (window size, max concurrent streams etc.) // let's give _q_receiveReply a chance to read them first ('invokeMethod', QueuedConnection). QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); @@ -1279,7 +1260,7 @@ void QHttpNetworkConnectionChannel::_q_encrypted() if (!reply) connection->d_func()->dequeueRequest(socket); if (reply) { - reply->setSpdyWasUsed(false); + reply->setHttp2WasUsed(false); Q_ASSERT(reply->d_func()->connectionChannel == this); emit reply->encrypted(); } @@ -1288,13 +1269,12 @@ void QHttpNetworkConnectionChannel::_q_encrypted() } } -void QHttpNetworkConnectionChannel::requeueSpdyRequests() +void QHttpNetworkConnectionChannel::requeueHttp2Requests() { - QList spdyPairs = spdyRequestsToSend.values(); - for (int a = 0; a < spdyPairs.count(); ++a) { - connection->d_func()->requeueRequest(spdyPairs.at(a)); - } - spdyRequestsToSend.clear(); + QList h2Pairs = h2RequestsToSend.values(); + for (int a = 0; a < h2Pairs.count(); ++a) + connection->d_func()->requeueRequest(h2Pairs.at(a)); + h2RequestsToSend.clear(); } void QHttpNetworkConnectionChannel::_q_sslErrors(const QList &errors) @@ -1312,11 +1292,11 @@ void QHttpNetworkConnectionChannel::_q_sslErrors(const QList &errors) emit reply->sslErrors(errors); } #ifndef QT_NO_SSL - else { // SPDY - QList spdyPairs = spdyRequestsToSend.values(); - for (int a = 0; a < spdyPairs.count(); ++a) { + else { // HTTP/2 + QList h2Pairs = h2RequestsToSend.values(); + for (int a = 0; a < h2Pairs.count(); ++a) { // emit SSL errors for all replies - QHttpNetworkReply *currentReply = spdyPairs.at(a).second; + QHttpNetworkReply *currentReply = h2Pairs.at(a).second; Q_ASSERT(currentReply); emit currentReply->sslErrors(errors); } @@ -1336,10 +1316,10 @@ void QHttpNetworkConnectionChannel::_q_preSharedKeyAuthenticationRequired(QSslPr if (reply) emit reply->preSharedKeyAuthenticationRequired(authenticator); } else { - QList spdyPairs = spdyRequestsToSend.values(); - for (int a = 0; a < spdyPairs.count(); ++a) { + QList h2Pairs = h2RequestsToSend.values(); + for (int a = 0; a < h2Pairs.count(); ++a) { // emit SSL errors for all replies - QHttpNetworkReply *currentReply = spdyPairs.at(a).second; + QHttpNetworkReply *currentReply = h2Pairs.at(a).second; Q_ASSERT(currentReply); emit currentReply->preSharedKeyAuthenticationRequired(authenticator); } diff --git a/src/network/access/qhttpnetworkconnectionchannel_p.h b/src/network/access/qhttpnetworkconnectionchannel_p.h index 270b3eb9ba..44ad2d7959 100644 --- a/src/network/access/qhttpnetworkconnectionchannel_p.h +++ b/src/network/access/qhttpnetworkconnectionchannel_p.h @@ -123,10 +123,7 @@ public: bool authenticationCredentialsSent; bool proxyCredentialsSent; QScopedPointer protocolHandler; - // SPDY or HTTP/2 requests; SPDY is TLS-only, but - // HTTP/2 can be cleartext also, that's why it's - // outside of QT_NO_SSL section. Sorted by priority: - QMultiMap spdyRequestsToSend; + QMultiMap h2RequestsToSend; bool switchedToHttp2 = false; #ifndef QT_NO_SSL bool ignoreAllSslErrors; @@ -135,7 +132,7 @@ public: void ignoreSslErrors(); void ignoreSslErrors(const QList &errors); void setSslConfiguration(const QSslConfiguration &config); - void requeueSpdyRequests(); // when we wanted SPDY but got HTTP + void requeueHttp2Requests(); // when we wanted HTTP/2 but got HTTP/1.1 #endif // to emit the signal for all in-flight replies: void emitFinishedWithError(QNetworkReply::NetworkError error, const char *message); diff --git a/src/network/access/qhttpnetworkreply.cpp b/src/network/access/qhttpnetworkreply.cpp index a8b635c45a..8982b7c745 100644 --- a/src/network/access/qhttpnetworkreply.cpp +++ b/src/network/access/qhttpnetworkreply.cpp @@ -287,14 +287,14 @@ bool QHttpNetworkReply::isPipeliningUsed() const return d_func()->pipeliningUsed; } -bool QHttpNetworkReply::isSpdyUsed() const +bool QHttpNetworkReply::isHttp2Used() const { - return d_func()->spdyUsed; + return d_func()->h2Used; } -void QHttpNetworkReply::setSpdyWasUsed(bool spdy) +void QHttpNetworkReply::setHttp2WasUsed(bool h2) { - d_func()->spdyUsed = spdy; + d_func()->h2Used = h2; } qint64 QHttpNetworkReply::removedContentLength() const @@ -324,15 +324,11 @@ QHttpNetworkReplyPrivate::QHttpNetworkReplyPrivate(const QUrl &newUrl) forceConnectionCloseEnabled(false), lastChunkRead(false), currentChunkSize(0), currentChunkRead(0), readBufferMaxSize(0), - windowSizeDownload(65536), // 64K initial window size according to SPDY standard - windowSizeUpload(65536), // 64K initial window size according to SPDY standard - currentlyReceivedDataInWindow(0), - currentlyUploadedDataInWindow(0), totallyUploadedData(0), removedContentLength(-1), connection(0), autoDecompress(false), responseData(), requestIsPrepared(false) - ,pipeliningUsed(false), spdyUsed(false), downstreamLimited(false) + ,pipeliningUsed(false), h2Used(false), downstreamLimited(false) ,userProvidedDownloadBuffer(0) #ifndef QT_NO_COMPRESS ,inflateStrm(0) diff --git a/src/network/access/qhttpnetworkreply_p.h b/src/network/access/qhttpnetworkreply_p.h index 12cfe359aa..82128f656e 100644 --- a/src/network/access/qhttpnetworkreply_p.h +++ b/src/network/access/qhttpnetworkreply_p.h @@ -137,8 +137,8 @@ public: bool isFinished() const; bool isPipeliningUsed() const; - bool isSpdyUsed() const; - void setSpdyWasUsed(bool spdy); + bool isHttp2Used() const; + void setHttp2WasUsed(bool h2Used); qint64 removedContentLength() const; bool isRedirecting() const; @@ -251,11 +251,7 @@ public: qint64 currentChunkSize; qint64 currentChunkRead; qint64 readBufferMaxSize; - qint32 windowSizeDownload; // only for SPDY - qint32 windowSizeUpload; // only for SPDY - qint32 currentlyReceivedDataInWindow; // only for SPDY - qint32 currentlyUploadedDataInWindow; // only for SPDY - qint64 totallyUploadedData; // only for SPDY + qint64 totallyUploadedData; // HTTP/2 qint64 removedContentLength; QPointer connection; QPointer connectionChannel; @@ -267,7 +263,7 @@ public: bool requestIsPrepared; bool pipeliningUsed; - bool spdyUsed; + bool h2Used; bool downstreamLimited; char* userProvidedDownloadBuffer; diff --git a/src/network/access/qhttpnetworkrequest.cpp b/src/network/access/qhttpnetworkrequest.cpp index a3f71b8d2f..f81924d5d3 100644 --- a/src/network/access/qhttpnetworkrequest.cpp +++ b/src/network/access/qhttpnetworkrequest.cpp @@ -45,7 +45,7 @@ QT_BEGIN_NAMESPACE QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(QHttpNetworkRequest::Operation op, QHttpNetworkRequest::Priority pri, const QUrl &newUrl) : QHttpNetworkHeaderPrivate(newUrl), operation(op), priority(pri), uploadByteDevice(0), - autoDecompress(false), pipeliningAllowed(false), spdyAllowed(false), http2Allowed(false), + autoDecompress(false), pipeliningAllowed(false), http2Allowed(false), http2Direct(false), withCredentials(true), preConnect(false), redirectCount(0), redirectPolicy(QNetworkRequest::ManualRedirectPolicy) { @@ -59,7 +59,6 @@ QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(const QHttpNetworkRequest uploadByteDevice(other.uploadByteDevice), autoDecompress(other.autoDecompress), pipeliningAllowed(other.pipeliningAllowed), - spdyAllowed(other.spdyAllowed), http2Allowed(other.http2Allowed), http2Direct(other.http2Direct), withCredentials(other.withCredentials), @@ -83,7 +82,6 @@ bool QHttpNetworkRequestPrivate::operator==(const QHttpNetworkRequestPrivate &ot && (uploadByteDevice == other.uploadByteDevice) && (autoDecompress == other.autoDecompress) && (pipeliningAllowed == other.pipeliningAllowed) - && (spdyAllowed == other.spdyAllowed) && (http2Allowed == other.http2Allowed) && (http2Direct == other.http2Direct) // we do not clear the customVerb in setOperation @@ -339,16 +337,6 @@ void QHttpNetworkRequest::setPipeliningAllowed(bool b) d->pipeliningAllowed = b; } -bool QHttpNetworkRequest::isSPDYAllowed() const -{ - return d->spdyAllowed; -} - -void QHttpNetworkRequest::setSPDYAllowed(bool b) -{ - d->spdyAllowed = b; -} - bool QHttpNetworkRequest::isHTTP2Allowed() const { return d->http2Allowed; diff --git a/src/network/access/qhttpnetworkrequest_p.h b/src/network/access/qhttpnetworkrequest_p.h index fb4896195b..f263e348ef 100644 --- a/src/network/access/qhttpnetworkrequest_p.h +++ b/src/network/access/qhttpnetworkrequest_p.h @@ -116,9 +116,6 @@ public: bool isPipeliningAllowed() const; void setPipeliningAllowed(bool b); - bool isSPDYAllowed() const; - void setSPDYAllowed(bool b); - bool isHTTP2Allowed() const; void setHTTP2Allowed(bool b); @@ -176,7 +173,6 @@ public: mutable QNonContiguousByteDevice* uploadByteDevice; bool autoDecompress; bool pipeliningAllowed; - bool spdyAllowed; bool http2Allowed; bool http2Direct; bool withCredentials; diff --git a/src/network/access/qhttpthreaddelegate.cpp b/src/network/access/qhttpthreaddelegate.cpp index 63a3c4f204..2933d75d2c 100644 --- a/src/network/access/qhttpthreaddelegate.cpp +++ b/src/network/access/qhttpthreaddelegate.cpp @@ -236,7 +236,7 @@ QHttpThreadDelegate::QHttpThreadDelegate(QObject *parent) : , synchronous(false) , incomingStatusCode(0) , isPipeliningUsed(false) - , isSpdyUsed(false) + , isHttp2Used(false) , incomingContentLength(-1) , removedContentLength(-1) , incomingErrorCode(QNetworkReply::NoError) @@ -320,17 +320,6 @@ void QHttpThreadDelegate::startRequest() } } -#ifndef QT_NO_SSL - if (!isH2 && httpRequest.isSPDYAllowed() && ssl) { - connectionType = QHttpNetworkConnection::ConnectionTypeSPDY; - urlCopy.setScheme(QStringLiteral("spdy")); // to differentiate SPDY requests from HTTPS requests - QList nextProtocols; - nextProtocols << QSslConfiguration::NextProtocolSpdy3_0 - << QSslConfiguration::NextProtocolHttp1_1; - incomingSslConfiguration->setAllowedNextProtocols(nextProtocols); - } -#endif // QT_NO_SSL - #ifndef QT_NO_NETWORKPROXY if (transparentProxy.type() != QNetworkProxy::NoProxy) cacheKey = makeCacheKey(urlCopy, &transparentProxy, httpRequest.peerVerifyName()); @@ -652,7 +641,7 @@ void QHttpThreadDelegate::headerChangedSlot() isPipeliningUsed = httpReply->isPipeliningUsed(); incomingContentLength = httpReply->contentLength(); removedContentLength = httpReply->removedContentLength(); - isSpdyUsed = httpReply->isSpdyUsed(); + isHttp2Used = httpReply->isHttp2Used(); emit downloadMetaData(incomingHeaders, incomingStatusCode, @@ -661,7 +650,7 @@ void QHttpThreadDelegate::headerChangedSlot() downloadBuffer, incomingContentLength, removedContentLength, - isSpdyUsed); + isHttp2Used); } void QHttpThreadDelegate::synchronousHeaderChangedSlot() @@ -677,7 +666,7 @@ void QHttpThreadDelegate::synchronousHeaderChangedSlot() incomingStatusCode = httpReply->statusCode(); incomingReasonPhrase = httpReply->reasonPhrase(); isPipeliningUsed = httpReply->isPipeliningUsed(); - isSpdyUsed = httpReply->isSpdyUsed(); + isHttp2Used = httpReply->isHttp2Used(); incomingContentLength = httpReply->contentLength(); } diff --git a/src/network/access/qhttpthreaddelegate_p.h b/src/network/access/qhttpthreaddelegate_p.h index 355d1afc30..208b2cb149 100644 --- a/src/network/access/qhttpthreaddelegate_p.h +++ b/src/network/access/qhttpthreaddelegate_p.h @@ -112,7 +112,7 @@ public: int incomingStatusCode; QString incomingReasonPhrase; bool isPipeliningUsed; - bool isSpdyUsed; + bool isHttp2Used; qint64 incomingContentLength; qint64 removedContentLength; QNetworkReply::NetworkError incomingErrorCode; diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp index 76b95b5823..125427493d 100644 --- a/src/network/access/qnetworkaccessmanager.cpp +++ b/src/network/access/qnetworkaccessmanager.cpp @@ -1237,12 +1237,10 @@ void QNetworkAccessManager::connectToHostEncrypted(const QString &hostName, quin if (sslConfiguration != QSslConfiguration::defaultConfiguration()) request.setSslConfiguration(sslConfiguration); - // There is no way to enable SPDY/HTTP2 via a request, so we need to check - // the ssl configuration whether SPDY/HTTP2 is allowed here. + // There is no way to enable HTTP2 via a request, so we need to check + // the ssl configuration whether HTTP2 is allowed here. if (sslConfiguration.allowedNextProtocols().contains(QSslConfiguration::ALPNProtocolHTTP2)) - request.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, true); - else if (sslConfiguration.allowedNextProtocols().contains(QSslConfiguration::NextProtocolSpdy3_0)) - request.setAttribute(QNetworkRequest::SpdyAllowedAttribute, true); + request.setAttribute(QNetworkRequest::Http2AllowedAttribute, true); request.setPeerVerifyName(peerName); get(request); diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp index 8ac81d1780..0bd7825186 100644 --- a/src/network/access/qnetworkreplyhttpimpl.cpp +++ b/src/network/access/qnetworkreplyhttpimpl.cpp @@ -774,10 +774,7 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq if (newHttpRequest.attribute(QNetworkRequest::HttpPipeliningAllowedAttribute).toBool()) httpRequest.setPipeliningAllowed(true); - if (request.attribute(QNetworkRequest::SpdyAllowedAttribute).toBool()) - httpRequest.setSPDYAllowed(true); - - if (request.attribute(QNetworkRequest::HTTP2AllowedAttribute).toBool()) + if (request.attribute(QNetworkRequest::Http2AllowedAttribute).toBool()) httpRequest.setHTTP2Allowed(true); if (request.attribute(QNetworkRequest::Http2DirectAttribute).toBool()) { @@ -968,7 +965,7 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq QSharedPointer(), delegate->incomingContentLength, delegate->removedContentLength, - delegate->isSpdyUsed); + delegate->isHttp2Used); replyDownloadData(delegate->synchronousDownloadData); httpError(delegate->incomingErrorCode, delegate->incomingErrorDetail); } else { @@ -980,7 +977,7 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq QSharedPointer(), delegate->incomingContentLength, delegate->removedContentLength, - delegate->isSpdyUsed); + delegate->isHttp2Used); replyDownloadData(delegate->synchronousDownloadData); } @@ -1254,7 +1251,7 @@ void QNetworkReplyHttpImplPrivate::replyDownloadMetaData(const QList db, qint64 contentLength, qint64 removedContentLength, - bool spdyWasUsed) + bool h2Used) { Q_Q(QNetworkReplyHttpImpl); Q_UNUSED(contentLength); @@ -1280,16 +1277,7 @@ void QNetworkReplyHttpImplPrivate::replyDownloadMetaData(const QListsetAttribute(QNetworkRequest::HttpPipeliningWasUsedAttribute, pu); - const QVariant http2Allowed = request.attribute(QNetworkRequest::HTTP2AllowedAttribute); - const QVariant http2Direct = request.attribute(QNetworkRequest::Http2DirectAttribute); - if ((http2Allowed.isValid() && http2Allowed.toBool()) - || (http2Direct.isValid() && http2Direct.toBool())) { - q->setAttribute(QNetworkRequest::HTTP2WasUsedAttribute, spdyWasUsed); - q->setAttribute(QNetworkRequest::SpdyWasUsedAttribute, false); - } else { - q->setAttribute(QNetworkRequest::SpdyWasUsedAttribute, spdyWasUsed); - q->setAttribute(QNetworkRequest::HTTP2WasUsedAttribute, false); - } + q->setAttribute(QNetworkRequest::Http2WasUsedAttribute, h2Used); // reconstruct the HTTP header QList > headerMap = hm; diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp index cedb1597de..af1e0b7278 100644 --- a/src/network/access/qnetworkrequest.cpp +++ b/src/network/access/qnetworkrequest.cpp @@ -272,18 +272,6 @@ QT_BEGIN_NAMESPACE The QNetworkSession ConnectInBackground property will be set according to this attribute. - \value SpdyAllowedAttribute - Requests only, type: QMetaType::Bool (default: false) - Indicates whether the QNetworkAccessManager code is - allowed to use SPDY with this request. This applies only - to SSL requests, and depends on the server supporting SPDY. - Obsolete, use Http2 instead of Spdy. - - \value SpdyWasUsedAttribute - Replies only, type: QMetaType::Bool - Indicates whether SPDY was used for receiving - this reply. Obsolete, use Http2 instead of Spdy. - \value Http2AllowedAttribute Requests only, type: QMetaType::Bool (default: false) Indicates whether the QNetworkAccessManager code is @@ -295,12 +283,6 @@ QT_BEGIN_NAMESPACE Indicates whether HTTP/2 was used for receiving this reply. (This value was introduced in 5.9.) - \value HTTP2AllowedAttribute - Obsolete alias for Http2AllowedAttribute. - - \value HTTP2WasUsedAttribute - Obsolete alias for Http2WasUsedAttribute. - \value EmitAllUploadProgressSignalsAttribute Requests only, type: QMetaType::Bool (default: false) Indicates whether all upload signals should be emitted. diff --git a/src/network/access/qnetworkrequest.h b/src/network/access/qnetworkrequest.h index 72a6555d91..95b5dc89b5 100644 --- a/src/network/access/qnetworkrequest.h +++ b/src/network/access/qnetworkrequest.h @@ -89,18 +89,10 @@ public: DownloadBufferAttribute, // internal SynchronousRequestAttribute, // internal BackgroundRequestAttribute, -#if QT_DEPRECATED_SINCE(5, 15) - SpdyAllowedAttribute, - SpdyWasUsedAttribute, -#endif // QT_DEPRECATED_SINCE(5, 15) EmitAllUploadProgressSignalsAttribute = BackgroundRequestAttribute + 3, FollowRedirectsAttribute, Http2AllowedAttribute, Http2WasUsedAttribute, -#if QT_DEPRECATED_SINCE(5, 15) - HTTP2AllowedAttribute Q_DECL_ENUMERATOR_DEPRECATED_X("Use Http2AllowedAttribute") = Http2AllowedAttribute, - HTTP2WasUsedAttribute Q_DECL_ENUMERATOR_DEPRECATED_X("Use Http2WasUsedAttribute"), -#endif // QT_DEPRECATED_SINCE(5, 15) OriginalContentLengthAttribute, RedirectPolicyAttribute, Http2DirectAttribute, diff --git a/src/network/access/qspdyprotocolhandler.cpp b/src/network/access/qspdyprotocolhandler.cpp deleted file mode 100644 index f845235bf7..0000000000 --- a/src/network/access/qspdyprotocolhandler.cpp +++ /dev/null @@ -1,1304 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 BlackBerry Limited. All rights reserved. -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtNetwork module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** 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 Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** 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-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include -#include -#include -#include - -#if !defined(QT_NO_SSL) - -QT_BEGIN_NAMESPACE - -static const char spdyDictionary[] = { - 0x00, 0x00, 0x00, 0x07, 0x6f, 0x70, 0x74, 0x69, // ....opti - 0x6f, 0x6e, 0x73, 0x00, 0x00, 0x00, 0x04, 0x68, // ons....h - 0x65, 0x61, 0x64, 0x00, 0x00, 0x00, 0x04, 0x70, // ead....p - 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x03, 0x70, // ost....p - 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x64, 0x65, // ut....de - 0x6c, 0x65, 0x74, 0x65, 0x00, 0x00, 0x00, 0x05, // lete.... - 0x74, 0x72, 0x61, 0x63, 0x65, 0x00, 0x00, 0x00, // trace... - 0x06, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x00, // .accept. - 0x00, 0x00, 0x0e, 0x61, 0x63, 0x63, 0x65, 0x70, // ...accep - 0x74, 0x2d, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, // t-charse - 0x74, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x63, 0x63, // t....acc - 0x65, 0x70, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, // ept-enco - 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x0f, // ding.... - 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x6c, // accept-l - 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x00, // anguage. - 0x00, 0x00, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70, // ...accep - 0x74, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, // t-ranges - 0x00, 0x00, 0x00, 0x03, 0x61, 0x67, 0x65, 0x00, // ....age. - 0x00, 0x00, 0x05, 0x61, 0x6c, 0x6c, 0x6f, 0x77, // ...allow - 0x00, 0x00, 0x00, 0x0d, 0x61, 0x75, 0x74, 0x68, // ....auth - 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, // orizatio - 0x6e, 0x00, 0x00, 0x00, 0x0d, 0x63, 0x61, 0x63, // n....cac - 0x68, 0x65, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x72, // he-contr - 0x6f, 0x6c, 0x00, 0x00, 0x00, 0x0a, 0x63, 0x6f, // ol....co - 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, // nnection - 0x00, 0x00, 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, // ....cont - 0x65, 0x6e, 0x74, 0x2d, 0x62, 0x61, 0x73, 0x65, // ent-base - 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, 0x6e, 0x74, // ....cont - 0x65, 0x6e, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, // ent-enco - 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, // ding.... - 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, // content- - 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, // language - 0x00, 0x00, 0x00, 0x0e, 0x63, 0x6f, 0x6e, 0x74, // ....cont - 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67, // ent-leng - 0x74, 0x68, 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, // th....co - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x6f, // ntent-lo - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, // cation.. - 0x00, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, // ..conten - 0x74, 0x2d, 0x6d, 0x64, 0x35, 0x00, 0x00, 0x00, // t-md5... - 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, // .content - 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, // -range.. - 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, // ..conten - 0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x00, 0x00, // t-type.. - 0x00, 0x04, 0x64, 0x61, 0x74, 0x65, 0x00, 0x00, // ..date.. - 0x00, 0x04, 0x65, 0x74, 0x61, 0x67, 0x00, 0x00, // ..etag.. - 0x00, 0x06, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, // ..expect - 0x00, 0x00, 0x00, 0x07, 0x65, 0x78, 0x70, 0x69, // ....expi - 0x72, 0x65, 0x73, 0x00, 0x00, 0x00, 0x04, 0x66, // res....f - 0x72, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x04, 0x68, // rom....h - 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x08, 0x69, // ost....i - 0x66, 0x2d, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, // f-match. - 0x00, 0x00, 0x11, 0x69, 0x66, 0x2d, 0x6d, 0x6f, // ...if-mo - 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2d, 0x73, // dified-s - 0x69, 0x6e, 0x63, 0x65, 0x00, 0x00, 0x00, 0x0d, // ince.... - 0x69, 0x66, 0x2d, 0x6e, 0x6f, 0x6e, 0x65, 0x2d, // if-none- - 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, 0x00, 0x00, // match... - 0x08, 0x69, 0x66, 0x2d, 0x72, 0x61, 0x6e, 0x67, // .if-rang - 0x65, 0x00, 0x00, 0x00, 0x13, 0x69, 0x66, 0x2d, // e....if- - 0x75, 0x6e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, // unmodifi - 0x65, 0x64, 0x2d, 0x73, 0x69, 0x6e, 0x63, 0x65, // ed-since - 0x00, 0x00, 0x00, 0x0d, 0x6c, 0x61, 0x73, 0x74, // ....last - 0x2d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, // -modifie - 0x64, 0x00, 0x00, 0x00, 0x08, 0x6c, 0x6f, 0x63, // d....loc - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, // ation... - 0x0c, 0x6d, 0x61, 0x78, 0x2d, 0x66, 0x6f, 0x72, // .max-for - 0x77, 0x61, 0x72, 0x64, 0x73, 0x00, 0x00, 0x00, // wards... - 0x06, 0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x00, // .pragma. - 0x00, 0x00, 0x12, 0x70, 0x72, 0x6f, 0x78, 0x79, // ...proxy - 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, // -authent - 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00, // icate... - 0x13, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2d, 0x61, // .proxy-a - 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, // uthoriza - 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x05, // tion.... - 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, 0x00, // range... - 0x07, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x72, // .referer - 0x00, 0x00, 0x00, 0x0b, 0x72, 0x65, 0x74, 0x72, // ....retr - 0x79, 0x2d, 0x61, 0x66, 0x74, 0x65, 0x72, 0x00, // y-after. - 0x00, 0x00, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, // ...serve - 0x72, 0x00, 0x00, 0x00, 0x02, 0x74, 0x65, 0x00, // r....te. - 0x00, 0x00, 0x07, 0x74, 0x72, 0x61, 0x69, 0x6c, // ...trail - 0x65, 0x72, 0x00, 0x00, 0x00, 0x11, 0x74, 0x72, // er....tr - 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x2d, 0x65, // ansfer-e - 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x00, // ncoding. - 0x00, 0x00, 0x07, 0x75, 0x70, 0x67, 0x72, 0x61, // ...upgra - 0x64, 0x65, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x73, // de....us - 0x65, 0x72, 0x2d, 0x61, 0x67, 0x65, 0x6e, 0x74, // er-agent - 0x00, 0x00, 0x00, 0x04, 0x76, 0x61, 0x72, 0x79, // ....vary - 0x00, 0x00, 0x00, 0x03, 0x76, 0x69, 0x61, 0x00, // ....via. - 0x00, 0x00, 0x07, 0x77, 0x61, 0x72, 0x6e, 0x69, // ...warni - 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, 0x77, 0x77, // ng....ww - 0x77, 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, // w-authen - 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, // ticate.. - 0x00, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, // ..method - 0x00, 0x00, 0x00, 0x03, 0x67, 0x65, 0x74, 0x00, // ....get. - 0x00, 0x00, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, // ...statu - 0x73, 0x00, 0x00, 0x00, 0x06, 0x32, 0x30, 0x30, // s....200 - 0x20, 0x4f, 0x4b, 0x00, 0x00, 0x00, 0x07, 0x76, // .OK....v - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x00, // ersion.. - 0x00, 0x08, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, // ..HTTP.1 - 0x2e, 0x31, 0x00, 0x00, 0x00, 0x03, 0x75, 0x72, // .1....ur - 0x6c, 0x00, 0x00, 0x00, 0x06, 0x70, 0x75, 0x62, // l....pub - 0x6c, 0x69, 0x63, 0x00, 0x00, 0x00, 0x0a, 0x73, // lic....s - 0x65, 0x74, 0x2d, 0x63, 0x6f, 0x6f, 0x6b, 0x69, // et-cooki - 0x65, 0x00, 0x00, 0x00, 0x0a, 0x6b, 0x65, 0x65, // e....kee - 0x70, 0x2d, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x00, // p-alive. - 0x00, 0x00, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, // ...origi - 0x6e, 0x31, 0x30, 0x30, 0x31, 0x30, 0x31, 0x32, // n1001012 - 0x30, 0x31, 0x32, 0x30, 0x32, 0x32, 0x30, 0x35, // 01202205 - 0x32, 0x30, 0x36, 0x33, 0x30, 0x30, 0x33, 0x30, // 20630030 - 0x32, 0x33, 0x30, 0x33, 0x33, 0x30, 0x34, 0x33, // 23033043 - 0x30, 0x35, 0x33, 0x30, 0x36, 0x33, 0x30, 0x37, // 05306307 - 0x34, 0x30, 0x32, 0x34, 0x30, 0x35, 0x34, 0x30, // 40240540 - 0x36, 0x34, 0x30, 0x37, 0x34, 0x30, 0x38, 0x34, // 64074084 - 0x30, 0x39, 0x34, 0x31, 0x30, 0x34, 0x31, 0x31, // 09410411 - 0x34, 0x31, 0x32, 0x34, 0x31, 0x33, 0x34, 0x31, // 41241341 - 0x34, 0x34, 0x31, 0x35, 0x34, 0x31, 0x36, 0x34, // 44154164 - 0x31, 0x37, 0x35, 0x30, 0x32, 0x35, 0x30, 0x34, // 17502504 - 0x35, 0x30, 0x35, 0x32, 0x30, 0x33, 0x20, 0x4e, // 505203.N - 0x6f, 0x6e, 0x2d, 0x41, 0x75, 0x74, 0x68, 0x6f, // on-Autho - 0x72, 0x69, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65, // ritative - 0x20, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, // .Informa - 0x74, 0x69, 0x6f, 0x6e, 0x32, 0x30, 0x34, 0x20, // tion204. - 0x4e, 0x6f, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x65, // No.Conte - 0x6e, 0x74, 0x33, 0x30, 0x31, 0x20, 0x4d, 0x6f, // nt301.Mo - 0x76, 0x65, 0x64, 0x20, 0x50, 0x65, 0x72, 0x6d, // ved.Perm - 0x61, 0x6e, 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x34, // anently4 - 0x30, 0x30, 0x20, 0x42, 0x61, 0x64, 0x20, 0x52, // 00.Bad.R - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x34, 0x30, // equest40 - 0x31, 0x20, 0x55, 0x6e, 0x61, 0x75, 0x74, 0x68, // 1.Unauth - 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x34, 0x30, // orized40 - 0x33, 0x20, 0x46, 0x6f, 0x72, 0x62, 0x69, 0x64, // 3.Forbid - 0x64, 0x65, 0x6e, 0x34, 0x30, 0x34, 0x20, 0x4e, // den404.N - 0x6f, 0x74, 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, // ot.Found - 0x35, 0x30, 0x30, 0x20, 0x49, 0x6e, 0x74, 0x65, // 500.Inte - 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x53, 0x65, 0x72, // rnal.Ser - 0x76, 0x65, 0x72, 0x20, 0x45, 0x72, 0x72, 0x6f, // ver.Erro - 0x72, 0x35, 0x30, 0x31, 0x20, 0x4e, 0x6f, 0x74, // r501.Not - 0x20, 0x49, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, // .Impleme - 0x6e, 0x74, 0x65, 0x64, 0x35, 0x30, 0x33, 0x20, // nted503. - 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, // Service. - 0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, // Unavaila - 0x62, 0x6c, 0x65, 0x4a, 0x61, 0x6e, 0x20, 0x46, // bleJan.F - 0x65, 0x62, 0x20, 0x4d, 0x61, 0x72, 0x20, 0x41, // eb.Mar.A - 0x70, 0x72, 0x20, 0x4d, 0x61, 0x79, 0x20, 0x4a, // pr.May.J - 0x75, 0x6e, 0x20, 0x4a, 0x75, 0x6c, 0x20, 0x41, // un.Jul.A - 0x75, 0x67, 0x20, 0x53, 0x65, 0x70, 0x74, 0x20, // ug.Sept. - 0x4f, 0x63, 0x74, 0x20, 0x4e, 0x6f, 0x76, 0x20, // Oct.Nov. - 0x44, 0x65, 0x63, 0x20, 0x30, 0x30, 0x3a, 0x30, // Dec.00.0 - 0x30, 0x3a, 0x30, 0x30, 0x20, 0x4d, 0x6f, 0x6e, // 0.00.Mon - 0x2c, 0x20, 0x54, 0x75, 0x65, 0x2c, 0x20, 0x57, // ..Tue..W - 0x65, 0x64, 0x2c, 0x20, 0x54, 0x68, 0x75, 0x2c, // ed..Thu. - 0x20, 0x46, 0x72, 0x69, 0x2c, 0x20, 0x53, 0x61, // .Fri..Sa - 0x74, 0x2c, 0x20, 0x53, 0x75, 0x6e, 0x2c, 0x20, // t..Sun.. - 0x47, 0x4d, 0x54, 0x63, 0x68, 0x75, 0x6e, 0x6b, // GMTchunk - 0x65, 0x64, 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, // ed.text. - 0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x69, 0x6d, 0x61, // html.ima - 0x67, 0x65, 0x2f, 0x70, 0x6e, 0x67, 0x2c, 0x69, // ge.png.i - 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x6a, 0x70, 0x67, // mage.jpg - 0x2c, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67, // .image.g - 0x69, 0x66, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, // if.appli - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, // cation.x - 0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, // ml.appli - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, // cation.x - 0x68, 0x74, 0x6d, 0x6c, 0x2b, 0x78, 0x6d, 0x6c, // html.xml - 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c, // .text.pl - 0x61, 0x69, 0x6e, 0x2c, 0x74, 0x65, 0x78, 0x74, // ain.text - 0x2f, 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, // .javascr - 0x69, 0x70, 0x74, 0x2c, 0x70, 0x75, 0x62, 0x6c, // ipt.publ - 0x69, 0x63, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, // icprivat - 0x65, 0x6d, 0x61, 0x78, 0x2d, 0x61, 0x67, 0x65, // emax-age - 0x3d, 0x67, 0x7a, 0x69, 0x70, 0x2c, 0x64, 0x65, // .gzip.de - 0x66, 0x6c, 0x61, 0x74, 0x65, 0x2c, 0x73, 0x64, // flate.sd - 0x63, 0x68, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, // chcharse - 0x74, 0x3d, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x63, // t.utf-8c - 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x69, // harset.i - 0x73, 0x6f, 0x2d, 0x38, 0x38, 0x35, 0x39, 0x2d, // so-8859- - 0x31, 0x2c, 0x75, 0x74, 0x66, 0x2d, 0x2c, 0x2a, // 1.utf-.. - 0x2c, 0x65, 0x6e, 0x71, 0x3d, 0x30, 0x2e // .enq.0. -}; - -// uncomment to debug -//static void printHex(const QByteArray &ba) -//{ -// QByteArray hex; -// QByteArray clearText; -// for (int a = 0; a < ba.count(); ++a) { -// QByteArray currentHexChar = QByteArray(1, ba.at(a)).toHex().rightJustified(2, ' '); -// QByteArray currentChar; -// if (ba.at(a) >= 32 && ba.at(a) < 126) { // if ASCII, print the letter -// currentChar = QByteArray(1, ba.at(a)); -// } else { -// currentChar = " "; -// } -// clearText.append(currentChar.rightJustified(2, ' ')); -// hex.append(currentHexChar); -// hex.append(' '); -// clearText.append(' '); -// } -// int chunkSize = 102; // 12 == 4 bytes per line -// for (int a = 0; a < hex.count(); a += chunkSize) { -// qDebug() << hex.mid(a, chunkSize); -// qDebug() << clearText.mid(a, chunkSize); -// } -//} - -QSpdyProtocolHandler::QSpdyProtocolHandler(QHttpNetworkConnectionChannel *channel) - : QObject(0), QAbstractProtocolHandler(channel), - m_nextStreamID(-1), - m_maxConcurrentStreams(100), // 100 is recommended in the SPDY RFC - m_initialWindowSize(0), - m_waitingForCompleteStream(false) -{ - m_inflateStream.zalloc = Z_NULL; - m_inflateStream.zfree = Z_NULL; - m_inflateStream.opaque = Z_NULL; - int zlibRet = inflateInit(&m_inflateStream); - Q_ASSERT(zlibRet == Z_OK); - - m_deflateStream.zalloc = Z_NULL; - m_deflateStream.zfree = Z_NULL; - m_deflateStream.opaque = Z_NULL; - - // Do actually not compress (i.e. compression level = 0) - // when sending the headers because of the CRIME attack - zlibRet = deflateInit(&m_deflateStream, /* compression level = */ 0); - Q_ASSERT(zlibRet == Z_OK); - Q_UNUSED(zlibRet); // silence -Wunused-variable -} - -QSpdyProtocolHandler::~QSpdyProtocolHandler() -{ - deflateEnd(&m_deflateStream); - deflateEnd(&m_inflateStream); -} - -bool QSpdyProtocolHandler::sendRequest() -{ - Q_ASSERT(!m_reply); - - int maxPossibleRequests = m_maxConcurrentStreams - m_inFlightStreams.count(); - Q_ASSERT(maxPossibleRequests >= 0); - if (maxPossibleRequests == 0) - return true; // return early if max concurrent requests are exceeded - - m_channel->state = QHttpNetworkConnectionChannel::WritingState; - - int requestsToSend = qMin(m_channel->spdyRequestsToSend.size(), maxPossibleRequests); - - QMultiMap::iterator it = m_channel->spdyRequestsToSend.begin(); - // requests will be ordered by priority (see QMultiMap doc) - for (int a = 0; a < requestsToSend; ++a) { - HttpMessagePair currentPair = *it; - QHttpNetworkRequest currentRequest = currentPair.first; - QHttpNetworkReply *currentReply = currentPair.second; - - currentReply->setSpdyWasUsed(true); - qint32 streamID = generateNextStreamID(); - m_streamIDs.insert(currentReply, streamID); - - currentReply->setRequest(currentRequest); - currentReply->d_func()->connection = m_connection; - currentReply->d_func()->connectionChannel = m_channel; - m_inFlightStreams.insert(streamID, currentPair); - connect(currentReply, SIGNAL(destroyed(QObject*)), this, SLOT(_q_replyDestroyed(QObject*))); - - sendSYN_STREAM(currentPair, streamID, /* associatedToStreamID = */ 0); - m_channel->spdyRequestsToSend.erase(it++); - } - m_channel->state = QHttpNetworkConnectionChannel::IdleState; - return true; -} - -void QSpdyProtocolHandler::_q_replyDestroyed(QObject* reply) -{ - qint32 streamID = m_streamIDs.take(reply); - if (m_inFlightStreams.remove(streamID)) - sendRST_STREAM(streamID, RST_STREAM_CANCEL); -} - -void QSpdyProtocolHandler::_q_receiveReply() -{ - Q_ASSERT(m_socket); - - // only run when the QHttpNetworkConnection is not currently being destructed, e.g. - // this function is called from _q_disconnected which is called because - // of ~QHttpNetworkConnectionPrivate - if (!qobject_cast(m_connection)) { - return; - } - - if (bytesAvailable() < 8) - return; // cannot read frame headers, wait for more data - - char frameHeadersRaw[8]; - if (!readNextChunk(8, frameHeadersRaw)) - return; // this should not happen, we just checked - - const QByteArray frameHeaders(frameHeadersRaw, 8); // ### try without memcpy - if (frameHeadersRaw[0] & 0x80) { - handleControlFrame(frameHeaders); - } else { - handleDataFrame(frameHeaders); - } - - // after handling the current frame, check whether there is more data waiting - if (m_socket->bytesAvailable() > 0) - QMetaObject::invokeMethod(m_channel, "_q_receiveReply", Qt::QueuedConnection); -} - -void QSpdyProtocolHandler::_q_readyRead() -{ - _q_receiveReply(); -} - -static qint16 twoBytesToInt(const char *bytes) -{ - return qFromBigEndian(bytes); -} - -static qint32 threeBytesToInt(const char *bytes) -{ - return qFromBigEndian(bytes) >> 8; -} - -static qint32 fourBytesToInt(const char *bytes) -{ - return qFromBigEndian(bytes); -} - -static void appendIntToThreeBytes(char *output, qint32 number) -{ - qToBigEndian(number, output + 1); - qToBigEndian(number >> 16, output); -} - -static void appendIntToFourBytes(char *output, qint32 number) -{ - qToBigEndian(number, output); -} - -static QByteArray intToFourBytes(qint32 number) // ### try to use appendIntToFourBytes where possible -{ - char data[4]; - qToBigEndian(number, data); - QByteArray ret(data, 4); - return ret; -} - -static QByteArray intToThreeBytes(qint32 number) -{ - char data[4]; - qToBigEndian(number << 8, data); - QByteArray ret(data, 3); - return ret; -} - -static qint32 getStreamID(const char *bytes) -{ - // eliminate most significant bit; it might be 0 or 1 depending on whether - // we are dealing with a control or data frame - return fourBytesToInt(bytes) & 0x3fffffff; -} - -static QByteArray headerField(const QByteArray &name, const QByteArray &value) -{ - QByteArray ret; - ret.reserve(name.count() + value.count() + 8); // 4 byte for length each - ret.append(intToFourBytes(name.count())); - ret.append(name); - ret.append(intToFourBytes(value.count())); - ret.append(value); - return ret; -} - -bool QSpdyProtocolHandler::uncompressHeader(const QByteArray &input, QByteArray *output) -{ - const size_t chunkSize = 1024; - char outputRaw[chunkSize]; - // input bytes will not be changed by zlib, so it is safe to const_cast here - m_inflateStream.next_in = const_cast(reinterpret_cast(input.constData())); - m_inflateStream.avail_in = input.count(); - m_inflateStream.total_in = input.count(); - int zlibRet; - - do { - m_inflateStream.next_out = reinterpret_cast(outputRaw); - m_inflateStream.avail_out = chunkSize; - zlibRet = inflate(&m_inflateStream, Z_SYNC_FLUSH); - if (zlibRet == Z_NEED_DICT) { - zlibRet = inflateSetDictionary(&m_inflateStream, - reinterpret_cast(spdyDictionary), - /* dictionaryLength = */ 1423); - Q_ASSERT(zlibRet == Z_OK); - continue; - } - switch (zlibRet) { - case Z_BUF_ERROR: { - if (m_inflateStream.avail_in == 0) { - int outputSize = chunkSize - m_inflateStream.avail_out; - output->append(outputRaw, outputSize); - m_inflateStream.avail_out = chunkSize; - } - break; - } - case Z_OK: { - int outputSize = chunkSize - m_inflateStream.avail_out; - output->append(outputRaw, outputSize); - break; - } - default: { - qWarning("got unexpected zlib return value: %d", zlibRet); - return false; - } - } - } while (m_inflateStream.avail_in > 0 && zlibRet != Z_STREAM_END); - - Q_ASSERT(m_inflateStream.avail_in == 0); - return true; -} - -QByteArray QSpdyProtocolHandler::composeHeader(const QHttpNetworkRequest &request) -{ - QByteArray uncompressedHeader; - uncompressedHeader.reserve(300); // rough estimate - - // calculate additional headers first, because we need to know the size - // ### do not partially copy the list, but restrict the set header fields - // in QHttpNetworkConnection - QVector > additionalHeaders; - for (int a = 0; a < request.header().count(); ++a) { - QByteArray key = request.header().at(a).first; - if (key == "Connection" || key == "Host" || key == "Keep-Alive" - || key == "Proxy-Connection" || key == "Transfer-Encoding") - continue; // those headers are not valid (section 3.2.1) - additionalHeaders.append(request.header().at(a)); - } - - qint32 numberOfHeaderPairs = 5 + additionalHeaders.count(); // 5 mandatory below + the additional ones - uncompressedHeader.append(intToFourBytes(numberOfHeaderPairs)); - - // mandatory header fields: - - uncompressedHeader.append(headerField(":method", request.methodName())); -#ifndef QT_NO_NETWORKPROXY - bool useProxy = m_connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy; - uncompressedHeader.append(headerField(":path", request.uri(useProxy))); -#else - uncompressedHeader.append(headerField(":path", request.uri(false))); -#endif - uncompressedHeader.append(headerField(":version", "HTTP/1.1")); - - uncompressedHeader.append(headerField(":host", request.url().authority(QUrl::FullyEncoded | QUrl::RemoveUserInfo).toLatin1())); - - uncompressedHeader.append(headerField(":scheme", request.url().scheme().toLatin1())); - - // end of mandatory header fields - - // now add the additional headers - for (int a = 0; a < additionalHeaders.count(); ++a) { - uncompressedHeader.append(headerField(additionalHeaders.at(a).first.toLower(), - additionalHeaders.at(a).second)); - } - - m_deflateStream.total_in = uncompressedHeader.count(); - m_deflateStream.avail_in = uncompressedHeader.count(); - m_deflateStream.next_in = reinterpret_cast(uncompressedHeader.data()); - int outputBytes = uncompressedHeader.count() + 30; // 30 bytes of compression header overhead - m_deflateStream.avail_out = outputBytes; - unsigned char *out = new unsigned char[outputBytes]; - m_deflateStream.next_out = out; - int availOutBefore = m_deflateStream.avail_out; - int zlibRet = deflate(&m_deflateStream, Z_SYNC_FLUSH); // do everything in one go since we use no compression - int compressedHeaderSize = availOutBefore - m_deflateStream.avail_out; - Q_ASSERT(zlibRet == Z_OK); // otherwise, we need to allocate more outputBytes - Q_UNUSED(zlibRet); // silence -Wunused-variable - Q_ASSERT(m_deflateStream.avail_in == 0); - QByteArray compressedHeader(reinterpret_cast(out), compressedHeaderSize); - delete[] out; - - return compressedHeader; -} - -quint64 QSpdyProtocolHandler::bytesAvailable() const -{ - Q_ASSERT(m_socket); - return m_spdyBuffer.byteAmount() + m_socket->bytesAvailable(); -} - -bool QSpdyProtocolHandler::readNextChunk(qint64 length, char *sink) -{ - qint64 expectedReadBytes = length; - qint64 requiredBytesFromBuffer = 0; - - if (m_waitingForCompleteStream) { - requiredBytesFromBuffer = qMin(length, m_spdyBuffer.byteAmount()); - // ### if next chunk from buffer bigger than what we want to read, - // we have to call read() (which memcpy's). Otherwise, we can just - // read the next chunk without memcpy'ing. - qint64 bytesReadFromBuffer = m_spdyBuffer.read(sink, requiredBytesFromBuffer); - Q_ASSERT(bytesReadFromBuffer == requiredBytesFromBuffer); - if (length <= bytesReadFromBuffer) { - return true; // buffer > required size -> no need to read from socket - } - expectedReadBytes -= requiredBytesFromBuffer; - } - qint64 readBytes = m_socket->read(sink + requiredBytesFromBuffer, expectedReadBytes); - - if (readBytes < expectedReadBytes) { - m_waitingForCompleteStream = true; - // ### this is inefficient, we should not put back so much data into the buffer - QByteArray temp(sink, requiredBytesFromBuffer + readBytes); - m_spdyBuffer.append(temp); - return false; - } else { - return true; // buffer must be cleared by calling function - } -} - -void QSpdyProtocolHandler::sendControlFrame(FrameType type, - ControlFrameFlags flags, - const char *data, - quint32 length) -{ - // frame type and stream ID - char header[8]; - header[0] = 0x80u; // leftmost bit == 1 -> is a control frame - header[1] = 0x03; // 3 bit == version 3 - header[2] = 0; - switch (type) { - case FrameType_CREDENTIAL: { - qWarning("sending SPDY CREDENTIAL frame is not yet implemented"); // QTBUG-36188 - return; - } - default: - header[3] = type; - } - - // flags - header[4] = 0; - if (flags & ControlFrame_FLAG_FIN || length == 0) { - Q_ASSERT(type == FrameType_SYN_STREAM || type == FrameType_SYN_REPLY - || type == FrameType_HEADERS || length == 0); - header[4] |= ControlFrame_FLAG_FIN; - } - if (flags & ControlFrame_FLAG_UNIDIRECTIONAL) { - Q_ASSERT(type == FrameType_SYN_STREAM); - header[4] |= ControlFrame_FLAG_UNIDIRECTIONAL; - } - - // length - appendIntToThreeBytes(header + 5, length); - - qint64 written = m_socket->write(header, 8); - Q_ASSERT(written == 8); - written = m_socket->write(data, length); - Q_ASSERT(written == length); - Q_UNUSED(written); // silence -Wunused-variable -} - -void QSpdyProtocolHandler::sendSYN_STREAM(const HttpMessagePair &messagePair, - qint32 streamID, qint32 associatedToStreamID) -{ - QHttpNetworkRequest request = messagePair.first; - QHttpNetworkReply *reply = messagePair.second; - - ControlFrameFlags flags = 0; - - if (!request.uploadByteDevice()) { - // no upload -> this is the last frame, send the FIN flag - flags |= ControlFrame_FLAG_FIN; - reply->d_func()->state = QHttpNetworkReplyPrivate::SPDYHalfClosed; - } else { - reply->d_func()->state = QHttpNetworkReplyPrivate::SPDYUploading; - - // hack: set the stream ID on the device directly, so when we get - // the signal for uploading we know which stream we are sending on - m_streamIDs.insert(request.uploadByteDevice(), streamID); - - QObject::connect(request.uploadByteDevice(), SIGNAL(readyRead()), this, - SLOT(_q_uploadDataReadyRead()), Qt::QueuedConnection); - QObject::connect(request.uploadByteDevice(), SIGNAL(destroyed(QObject*)), this, - SLOT(_q_uploadDataDestroyed(QObject *))); - } - - QByteArray namesAndValues = composeHeader(request); - quint32 length = namesAndValues.count() + 10; // 10 == 4 for Stream-ID + 4 for Associated-To-Stream-ID - // + 2 for Priority, Unused and Slot - - QByteArray wireData; - wireData.reserve(length); - wireData.append(intToFourBytes(streamID)); - wireData.append(intToFourBytes(associatedToStreamID)); - - // priority (3 bits) / unused (5 bits) / slot (8 bits) - char prioAndSlot[2]; - switch (request.priority()) { - case QHttpNetworkRequest::HighPriority: - prioAndSlot[0] = 0x00; // == prio 0 (highest) - break; - case QHttpNetworkRequest::NormalPriority: - prioAndSlot[0] = 0x80u; // == prio 4 - break; - case QHttpNetworkRequest::LowPriority: - prioAndSlot[0] = 0xe0u; // == prio 7 (lowest) - break; - } - prioAndSlot[1] = 0x00; // slot in client certificates (not supported currently) - wireData.append(prioAndSlot, 2); - - wireData.append(namesAndValues); - - sendControlFrame(FrameType_SYN_STREAM, flags, wireData.constData(), length); - - if (reply->d_func()->state == QHttpNetworkReplyPrivate::SPDYUploading) - uploadData(streamID); -} - -void QSpdyProtocolHandler::_q_uploadDataDestroyed(QObject *uploadData) -{ - m_streamIDs.remove(uploadData); -} - -void QSpdyProtocolHandler::sendRST_STREAM(qint32 streamID, RST_STREAM_STATUS_CODE statusCode) -{ - char wireData[8]; - appendIntToFourBytes(wireData, streamID); - appendIntToFourBytes(wireData + 4, statusCode); - sendControlFrame(FrameType_RST_STREAM, /* flags = */ 0, wireData, /* length = */ 8); -} - -void QSpdyProtocolHandler::sendPING(quint32 pingID) -{ - char rawData[4]; - appendIntToFourBytes(rawData, pingID); - sendControlFrame(FrameType_PING, /* flags = */ 0, rawData, /* length = */ 4); -} - -bool QSpdyProtocolHandler::uploadData(qint32 streamID) -{ - // we only rely on SPDY flow control here and don't care about TCP buffers - if (!m_inFlightStreams.contains(streamID)) { - sendRST_STREAM(streamID, RST_STREAM_INVALID_STREAM); - return false; - } - - HttpMessagePair messagePair = m_inFlightStreams.value(streamID); - QHttpNetworkRequest request = messagePair.first; - QHttpNetworkReply *reply = messagePair.second; - Q_ASSERT(reply); - QHttpNetworkReplyPrivate *replyPrivate = reply->d_func(); - Q_ASSERT(replyPrivate); - - if (reply->d_func()->state == QHttpNetworkReplyPrivate::SPDYHalfClosed || reply->d_func()->state == QHttpNetworkReplyPrivate::SPDYClosed) { - qWarning("Trying to upload to closed stream"); - return false; - } - - qint32 dataLeftInWindow = replyPrivate->windowSizeUpload - - replyPrivate->currentlyUploadedDataInWindow; - - while (dataLeftInWindow > 0 && !request.uploadByteDevice()->atEnd()) { - - // get pointer to upload data - qint64 currentReadSize = 0; - const char *readPointer = request.uploadByteDevice()->readPointer(dataLeftInWindow, - currentReadSize); - - if (currentReadSize == -1) { - // premature eof happened - m_connection->d_func()->emitReplyError(m_socket, reply, - QNetworkReply::UnknownNetworkError); - return false; - } else if (readPointer == 0 || currentReadSize == 0) { - // nothing to read currently, break the loop - break; - } else { - DataFrameFlags flags = 0; - // we will send the FIN flag later if appropriate - qint64 currentWriteSize = sendDataFrame(streamID, flags, currentReadSize, readPointer); - if (currentWriteSize == -1 || currentWriteSize != currentReadSize) { - // socket broke down - m_connection->d_func()->emitReplyError(m_socket, reply, - QNetworkReply::UnknownNetworkError); - return false; - } else { - replyPrivate->currentlyUploadedDataInWindow += currentWriteSize; - replyPrivate->totallyUploadedData += currentWriteSize; - dataLeftInWindow = replyPrivate->windowSizeUpload - - replyPrivate->currentlyUploadedDataInWindow; - request.uploadByteDevice()->advanceReadPointer(currentWriteSize); - - emit reply->dataSendProgress(replyPrivate->totallyUploadedData, - request.contentLength()); - } - } - } - if (replyPrivate->totallyUploadedData == request.contentLength()) { - DataFrameFlags finFlag = DataFrame_FLAG_FIN; - qint64 writeSize = sendDataFrame(streamID, finFlag, 0, 0); - Q_ASSERT(writeSize == 0); - Q_UNUSED(writeSize); // silence -Wunused-variable - replyPrivate->state = QHttpNetworkReplyPrivate::SPDYHalfClosed; - if (reply->request().uploadByteDevice()) - reply->request().uploadByteDevice()->disconnect(this); - // ### this will not work if the content length is not known, but - // then again many servers will fail in this case anyhow according - // to the SPDY RFC - } - return true; -} - -void QSpdyProtocolHandler::_q_uploadDataReadyRead() -{ - QNonContiguousByteDevice *device = qobject_cast(sender()); - Q_ASSERT(device); - qint32 streamID = m_streamIDs.value(device); - Q_ASSERT(streamID > 0); - uploadData(streamID); -} - -void QSpdyProtocolHandler::sendWINDOW_UPDATE(qint32 streamID, quint32 deltaWindowSize) -{ - char windowUpdateData[8]; - appendIntToFourBytes(windowUpdateData, streamID); - appendIntToFourBytes(windowUpdateData + 4, deltaWindowSize); - - sendControlFrame(FrameType_WINDOW_UPDATE, /* flags = */ 0, windowUpdateData, /* length = */ 8); -} - -qint64 QSpdyProtocolHandler::sendDataFrame(qint32 streamID, DataFrameFlags flags, - quint32 length, const char *data) -{ - QByteArray wireData; - wireData.reserve(8); - - wireData.append(intToFourBytes(streamID)); - wireData.append(flags); - wireData.append(intToThreeBytes(length)); - - Q_ASSERT(m_socket); - m_socket->write(wireData); - - if (data) { - qint64 ret = m_socket->write(data, length); - return ret; - } else { - return 0; // nothing to write, e.g. FIN flag - } -} - -void QSpdyProtocolHandler::handleControlFrame(const QByteArray &frameHeaders) // ### make it char * -{ - Q_ASSERT(frameHeaders.count() >= 8); - qint16 version = twoBytesToInt(frameHeaders.constData()); - version &= 0x3fff; // eliminate most significant bit to determine version - Q_ASSERT(version == 3); - - qint16 type = twoBytesToInt(frameHeaders.constData() + 2); - - char flags = frameHeaders.at(4); - qint32 length = threeBytesToInt(frameHeaders.constData() + 5); - Q_ASSERT(length > 0); - - QByteArray frameData; - frameData.resize(length); - if (!readNextChunk(length, frameData.data())) { - // put back the frame headers to the buffer - m_spdyBuffer.prepend(frameHeaders); - return; // we couldn't read the whole frame and need to wait - } else { - m_spdyBuffer.clear(); - m_waitingForCompleteStream = false; - } - - switch (type) { - case FrameType_SYN_STREAM: { - handleSYN_STREAM(flags, length, frameData); - break; - } - case FrameType_SYN_REPLY: { - handleSYN_REPLY(flags, length, frameData); - break; - } - case FrameType_RST_STREAM: { - handleRST_STREAM(flags, length, frameData); - break; - } - case FrameType_SETTINGS: { - handleSETTINGS(flags, length, frameData); - break; - } - case FrameType_PING: { - handlePING(flags, length, frameData); - break; - } - case FrameType_GOAWAY: { - handleGOAWAY(flags, length, frameData); - break; - } - case FrameType_HEADERS: { - handleHEADERS(flags, length, frameData); - break; - } - case FrameType_WINDOW_UPDATE: { - handleWINDOW_UPDATE(flags, length, frameData); - break; - } - default: - qWarning("cannot handle frame of type %d", int(type)); - } -} - -void QSpdyProtocolHandler::handleSYN_STREAM(char /*flags*/, quint32 /*length*/, - const QByteArray &frameData) -{ - // not implemented; will be implemented when servers start using it - // we just tell the server that we do not accept that - - qint32 streamID = getStreamID(frameData.constData()); - - sendRST_STREAM(streamID, RST_STREAM_REFUSED_STREAM); -} - -void QSpdyProtocolHandler::handleSYN_REPLY(char flags, quint32 /*length*/, const QByteArray &frameData) -{ - parseHttpHeaders(flags, frameData); -} - -void QSpdyProtocolHandler::parseHttpHeaders(char flags, const QByteArray &frameData) -{ - qint32 streamID = getStreamID(frameData.constData()); - const auto it = m_inFlightStreams.constFind(streamID); - if (it == m_inFlightStreams.cend()) { - sendRST_STREAM(streamID, RST_STREAM_INVALID_STREAM); - return; - } - - flags &= 0x3f; - bool flag_fin = flags & 0x01; - - QByteArray headerValuePairs = frameData.mid(4); - - HttpMessagePair pair = it.value(); - QHttpNetworkReply *httpReply = pair.second; - Q_ASSERT(httpReply != 0); - - if (httpReply->d_func()->state == QHttpNetworkReplyPrivate::SPDYClosed) { - sendRST_STREAM(streamID, RST_STREAM_STREAM_ALREADY_CLOSED); - return; - } - - QByteArray uncompressedHeader; - if (!uncompressHeader(headerValuePairs, &uncompressedHeader)) { - qWarning("error reading header from SYN_REPLY message"); - return; - } - - qint32 headerCount = fourBytesToInt(uncompressedHeader.constData()); - if (headerCount * 8 > uncompressedHeader.size()) { - qWarning("error parsing header from SYN_REPLY message"); - sendRST_STREAM(streamID, RST_STREAM_PROTOCOL_ERROR); - return; - } - qint32 readPointer = 4; - for (qint32 a = 0; a < headerCount; ++a) { - qint32 count = fourBytesToInt(uncompressedHeader.constData() + readPointer); - readPointer += 4; - QByteArray name = uncompressedHeader.mid(readPointer, count); - readPointer += count; - if (readPointer > uncompressedHeader.size()) { - qWarning("error parsing header from SYN_REPLY message"); - sendRST_STREAM(streamID, RST_STREAM_PROTOCOL_ERROR); - return; - } - count = fourBytesToInt(uncompressedHeader.constData() + readPointer); - readPointer += 4; - QByteArray value = uncompressedHeader.mid(readPointer, count); - readPointer += count; - if (readPointer > uncompressedHeader.size()) { - qWarning("error parsing header from SYN_REPLY message"); - sendRST_STREAM(streamID, RST_STREAM_PROTOCOL_ERROR); - return; - } - if (name == ":status") { - httpReply->setStatusCode(value.left(3).toInt()); - httpReply->d_func()->reasonPhrase = QString::fromLatin1(value.mid(4)); - } else if (name == ":version") { - int majorVersion = value.at(5) - 48; - int minorVersion = value.at(7) - 48; - httpReply->d_func()->majorVersion = majorVersion; - httpReply->d_func()->minorVersion = minorVersion; - } else if (name == "content-length") { - httpReply->setContentLength(value.toLongLong()); - } else { - value.replace('\0', name == "set-cookie" ? "\n" : ", "); - httpReply->setHeaderField(name, value); - } - } - emit httpReply->headerChanged(); - - if (flag_fin) { - if (httpReply->d_func()->state != QHttpNetworkReplyPrivate::SPDYHalfClosed) - sendDataFrame(streamID, DataFrame_FLAG_FIN, 0, 0); - replyFinished(httpReply, streamID); - } -} - -void QSpdyProtocolHandler::handleRST_STREAM(char /*flags*/, quint32 length, - const QByteArray &frameData) -{ - // flags are ignored - - Q_ASSERT(length == 8); - Q_UNUSED(length); // silence -Wunused-parameter - qint32 streamID = getStreamID(frameData.constData()); - QHttpNetworkReply *httpReply = m_inFlightStreams.value(streamID).second; - - qint32 statusCodeInt = fourBytesToInt(frameData.constData() + 4); - RST_STREAM_STATUS_CODE statusCode = static_cast(statusCodeInt); - QNetworkReply::NetworkError errorCode; - QByteArray errorMessage; - - switch (statusCode) { - case RST_STREAM_PROTOCOL_ERROR: - errorCode = QNetworkReply::ProtocolFailure; - errorMessage = "SPDY protocol error"; - break; - case RST_STREAM_INVALID_STREAM: - errorCode = QNetworkReply::ProtocolFailure; - errorMessage = "SPDY stream is not active"; - break; - case RST_STREAM_REFUSED_STREAM: - errorCode = QNetworkReply::ProtocolFailure; - errorMessage = "SPDY stream was refused"; - break; - case RST_STREAM_UNSUPPORTED_VERSION: - errorCode = QNetworkReply::ProtocolUnknownError; - errorMessage = "SPDY version is unknown to the server"; - break; - case RST_STREAM_CANCEL: - errorCode = QNetworkReply::ProtocolFailure; - errorMessage = "SPDY stream is no longer needed"; - break; - case RST_STREAM_INTERNAL_ERROR: - errorCode = QNetworkReply::InternalServerError; - errorMessage = "Internal server error"; - break; - case RST_STREAM_FLOW_CONTROL_ERROR: - errorCode = QNetworkReply::ProtocolFailure; - errorMessage = "peer violated the flow control protocol"; - break; - case RST_STREAM_STREAM_IN_USE: - errorCode = QNetworkReply::ProtocolFailure; - errorMessage = "server received a SYN_REPLY for an already open stream"; - break; - case RST_STREAM_STREAM_ALREADY_CLOSED: - errorCode = QNetworkReply::ProtocolFailure; - errorMessage = "server received data or a SYN_REPLY for an already half-closed stream"; - break; - case RST_STREAM_INVALID_CREDENTIALS: - errorCode = QNetworkReply::ContentAccessDenied; - errorMessage = "server received invalid credentials"; - break; - case RST_STREAM_FRAME_TOO_LARGE: - errorCode = QNetworkReply::ProtocolFailure; - errorMessage = "server cannot process the frame because it is too large"; - break; - default: - qWarning("could not understand servers RST_STREAM status code"); - errorCode = QNetworkReply::ProtocolFailure; - errorMessage = "got SPDY RST_STREAM message with unknown error code"; - } - if (httpReply) - replyFinishedWithError(httpReply, streamID, errorCode, errorMessage.constData()); -} - -void QSpdyProtocolHandler::handleSETTINGS(char flags, quint32 /*length*/, const QByteArray &frameData) -{ - Q_ASSERT(frameData.count() > 0); - - SETTINGS_Flags settingsFlags = static_cast(flags); - if (settingsFlags & FLAG_SETTINGS_CLEAR_SETTINGS) { - // ### clear all persistent settings; since we do not persist settings - // as of now, we don't need to clear anything either - } - - qint32 numberOfEntries = fourBytesToInt(frameData.constData()); - Q_ASSERT(numberOfEntries > 0); - for (int a = 0, frameDataIndex = 4; a < numberOfEntries; ++a, frameDataIndex += 8) { - SETTINGS_ID_Flag idFlag = static_cast(frameData[frameDataIndex]); - if (idFlag & FLAG_SETTINGS_PERSIST_VALUE) { - // ### we SHOULD persist the settings here according to the RFC, but we don't have to, - // so implement that later - } // the other value is only sent by us, but not received - - quint32 uniqueID = static_cast( - threeBytesToInt(frameData.constData() + frameDataIndex + 1)); - quint32 value = fourBytesToInt(frameData.constData() + frameDataIndex + 4); - switch (uniqueID) { - case SETTINGS_UPLOAD_BANDWIDTH: { - // ignored for now, just an estimated informative value - break; - } - case SETTINGS_DOWNLOAD_BANDWIDTH: { - // ignored for now, just an estimated informative value - break; - } - case SETTINGS_ROUND_TRIP_TIME: { - // ignored for now, just an estimated informative value - break; - } - case SETTINGS_MAX_CONCURRENT_STREAMS: { - m_maxConcurrentStreams = value; - break; - } - case SETTINGS_CURRENT_CWND: { - // ignored for now, just an informative value - break; - } - case SETTINGS_DOWNLOAD_RETRANS_RATE: { - // ignored for now, just an estimated informative value - break; - } - case SETTINGS_INITIAL_WINDOW_SIZE: { - m_initialWindowSize = value; - break; - } - case SETTINGS_CLIENT_CERTIFICATE_VECTOR_SIZE: { - // client certificates are not supported - break; - } - default: - qWarning("found unknown settings value %u", uint(value)); - } - } -} - -void QSpdyProtocolHandler::handlePING(char /*flags*/, quint32 length, const QByteArray &frameData) -{ - // flags are ignored - - Q_ASSERT(length == 4); - Q_UNUSED(length); // silence -Wunused-parameter - quint32 pingID = fourBytesToInt(frameData.constData()); - - // odd numbered IDs must be ignored - if ((pingID & 1) == 0) // is even? - sendPING(pingID); -} - -void QSpdyProtocolHandler::handleGOAWAY(char /*flags*/, quint32 /*length*/, - const QByteArray &frameData) -{ - // flags are ignored - - qint32 statusCode = static_cast(fourBytesToInt(frameData.constData() + 4)); - QNetworkReply::NetworkError errorCode; - switch (statusCode) { - case GOAWAY_OK: { - errorCode = QNetworkReply::NoError; - break; - } - case GOAWAY_PROTOCOL_ERROR: { - errorCode = QNetworkReply::ProtocolFailure; - break; - } - case GOAWAY_INTERNAL_ERROR: { - errorCode = QNetworkReply::InternalServerError; - break; - } - default: - qWarning("unexpected status code %d", int(statusCode)); - errorCode = QNetworkReply::ProtocolUnknownError; - } - - qint32 lastGoodStreamID = getStreamID(frameData.constData()); - - // emit errors for all replies after the last good stream ID - Q_ASSERT(m_connection); - for (qint32 currentStreamID = lastGoodStreamID + 2; currentStreamID <= m_nextStreamID; - ++currentStreamID) { - QHttpNetworkReply *reply = m_inFlightStreams.value(currentStreamID).second; - Q_ASSERT(reply); - m_connection->d_func()->emitReplyError(m_socket, reply, errorCode); - } - // ### we could make sure a new session is initiated anyhow -} - -void QSpdyProtocolHandler::handleHEADERS(char flags, quint32 /*length*/, - const QByteArray &frameData) -{ - parseHttpHeaders(flags, frameData); -} - -void QSpdyProtocolHandler::handleWINDOW_UPDATE(char /*flags*/, quint32 /*length*/, - const QByteArray &frameData) -{ - qint32 streamID = getStreamID(frameData.constData()); - qint32 deltaWindowSize = fourBytesToInt(frameData.constData() + 4); - - const auto it = m_inFlightStreams.constFind(streamID); - if (it == m_inFlightStreams.cend()) { - sendRST_STREAM(streamID, RST_STREAM_INVALID_STREAM); - return; - } - - QHttpNetworkReply *reply = it.value().second; - Q_ASSERT(reply); - QHttpNetworkReplyPrivate *replyPrivate = reply->d_func(); - Q_ASSERT(replyPrivate); - - // Ignore WINDOW_UPDATE if we are already done. - if (replyPrivate->state == QHttpNetworkReplyPrivate::SPDYHalfClosed || replyPrivate->state == QHttpNetworkReplyPrivate::SPDYClosed) - return; - - replyPrivate->currentlyUploadedDataInWindow = replyPrivate->windowSizeUpload - deltaWindowSize; - uploadData(streamID); // we hopefully can continue to upload -} - - -void QSpdyProtocolHandler::handleDataFrame(const QByteArray &frameHeaders) -{ - Q_ASSERT(frameHeaders.count() >= 8); - - qint32 streamID = getStreamID(frameHeaders.constData()); - const auto it = m_inFlightStreams.constFind(streamID); - if (it == m_inFlightStreams.cend()) { - sendRST_STREAM(streamID, RST_STREAM_INVALID_STREAM); - return; - } - - unsigned char flags = static_cast(frameHeaders.at(4)); - flags &= 0x3f; - bool flag_fin = flags & 0x01; - bool flag_compress = flags & 0x02; - qint32 length = threeBytesToInt(frameHeaders.constData() + 5); - - QByteArray data; - data.resize(length); - if (!readNextChunk(length, data.data())) { - // put back the frame headers to the buffer - m_spdyBuffer.prepend(frameHeaders); - return; // we couldn't read the whole frame and need to wait - } else { - m_spdyBuffer.clear(); - m_waitingForCompleteStream = false; - } - - HttpMessagePair pair = it.value(); - QHttpNetworkRequest httpRequest = pair.first; - QHttpNetworkReply *httpReply = pair.second; - Q_ASSERT(httpReply != 0); - - QHttpNetworkReplyPrivate *replyPrivate = httpReply->d_func(); - - if (replyPrivate->state == QHttpNetworkReplyPrivate::SPDYClosed) { - sendRST_STREAM(streamID, RST_STREAM_STREAM_ALREADY_CLOSED); - return; - } - - // check whether we need to send WINDOW_UPDATE (i.e. tell the sender it can send more) - replyPrivate->currentlyReceivedDataInWindow += length; - qint32 dataLeftInWindow = replyPrivate->windowSizeDownload - replyPrivate->currentlyReceivedDataInWindow; - - if (replyPrivate->currentlyReceivedDataInWindow > 0 - && dataLeftInWindow < replyPrivate->windowSizeDownload / 2) { - - // socket read buffer size is 64K actually, hard coded in the channel - // We can read way more than 64K per socket, because the window size - // here is per stream. - if (replyPrivate->windowSizeDownload >= m_socket->readBufferSize()) { - replyPrivate->windowSizeDownload = m_socket->readBufferSize(); - } else { - replyPrivate->windowSizeDownload *= 1.5; - } - QMetaObject::invokeMethod(this, "sendWINDOW_UPDATE", Qt::QueuedConnection, - Q_ARG(qint32, streamID), - Q_ARG(quint32, replyPrivate->windowSizeDownload)); - // setting the current data count to 0 is a race condition, - // because we call sendWINDOW_UPDATE through the event loop. - // But then again, the whole situation is a race condition because - // we don't know when the packet will arrive at the server; so - // this is most likely good enough here. - replyPrivate->currentlyReceivedDataInWindow = 0; - } - - httpReply->d_func()->compressedData.append(data); - - - replyPrivate->totalProgress += length; - - if (httpRequest.d->autoDecompress && httpReply->d_func()->isCompressed()) { - QByteDataBuffer inDataBuffer; // ### should we introduce one in the http reply? - inDataBuffer.append(data); - qint64 compressedCount = httpReply->d_func()->uncompressBodyData(&inDataBuffer, - &replyPrivate->responseData); - Q_ASSERT(compressedCount >= 0); - Q_UNUSED(compressedCount); // silence -Wunused-variable - } else { - replyPrivate->responseData.append(data); - } - - if (replyPrivate->shouldEmitSignals()) { - emit httpReply->readyRead(); - emit httpReply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength); - } - - if (flag_compress) { - qWarning("SPDY level compression is not supported"); - } - - if (flag_fin) { - if (httpReply->d_func()->state != QHttpNetworkReplyPrivate::SPDYHalfClosed) - sendDataFrame(streamID, DataFrame_FLAG_FIN, 0, 0); - replyFinished(httpReply, streamID); - } -} - -void QSpdyProtocolHandler::replyFinished(QHttpNetworkReply *httpReply, qint32 streamID) -{ - httpReply->d_func()->state = QHttpNetworkReplyPrivate::SPDYClosed; - httpReply->disconnect(this); - if (httpReply->request().uploadByteDevice()) - httpReply->request().uploadByteDevice()->disconnect(this); - int streamsRemoved = m_inFlightStreams.remove(streamID); - Q_ASSERT(streamsRemoved == 1); - Q_UNUSED(streamsRemoved); // silence -Wunused-variable - emit httpReply->finished(); -} - -void QSpdyProtocolHandler::replyFinishedWithError(QHttpNetworkReply *httpReply, qint32 streamID, - QNetworkReply::NetworkError errorCode, const char *errorMessage) -{ - Q_ASSERT(httpReply); - httpReply->d_func()->state = QHttpNetworkReplyPrivate::SPDYClosed; - httpReply->disconnect(this); - if (httpReply->request().uploadByteDevice()) - httpReply->request().uploadByteDevice()->disconnect(this); - int streamsRemoved = m_inFlightStreams.remove(streamID); - Q_ASSERT(streamsRemoved == 1); - Q_UNUSED(streamsRemoved); // silence -Wunused-variable - emit httpReply->finishedWithError(errorCode, QSpdyProtocolHandler::tr(errorMessage)); -} - -qint32 QSpdyProtocolHandler::generateNextStreamID() -{ - // stream IDs initiated by the client must be odd - m_nextStreamID += 2; - return m_nextStreamID; -} - -QT_END_NAMESPACE - -#endif // !defined(QT_NO_SSL) diff --git a/src/network/access/qspdyprotocolhandler_p.h b/src/network/access/qspdyprotocolhandler_p.h deleted file mode 100644 index 14e2ff388a..0000000000 --- a/src/network/access/qspdyprotocolhandler_p.h +++ /dev/null @@ -1,232 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 BlackBerry Limited. All rights reserved. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtNetwork module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** 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 Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** 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-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSPDYPROTOCOLHANDLER_H -#define QSPDYPROTOCOLHANDLER_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of the Network Access API. This header file may change from -// version to version without notice, or even be removed. -// -// We mean it. -// - -#include -#include -#include -#include - -#include - -QT_REQUIRE_CONFIG(http); - -#if !defined(QT_NO_SSL) - -QT_BEGIN_NAMESPACE - -class QHttpNetworkRequest; - -#ifndef HttpMessagePair -typedef QPair HttpMessagePair; -#endif - -class QSpdyProtocolHandler : public QObject, public QAbstractProtocolHandler { - Q_OBJECT -public: - QSpdyProtocolHandler(QHttpNetworkConnectionChannel *channel); - ~QSpdyProtocolHandler(); - - enum DataFrameFlag { - DataFrame_FLAG_FIN = 0x01, - DataFrame_FLAG_COMPRESS = 0x02 - }; - - Q_DECLARE_FLAGS(DataFrameFlags, DataFrameFlag) - - enum ControlFrameFlag { - ControlFrame_FLAG_FIN = 0x01, - ControlFrame_FLAG_UNIDIRECTIONAL = 0x02 - }; - - Q_DECLARE_FLAGS(ControlFrameFlags, ControlFrameFlag) - - enum SETTINGS_Flag { - FLAG_SETTINGS_CLEAR_SETTINGS = 0x01 - }; - - Q_DECLARE_FLAGS(SETTINGS_Flags, SETTINGS_Flag) - - enum SETTINGS_ID_Flag { - FLAG_SETTINGS_PERSIST_VALUE = 0x01, - FLAG_SETTINGS_PERSISTED = 0x02 - }; - - Q_DECLARE_FLAGS(SETTINGS_ID_Flags, SETTINGS_ID_Flag) - - virtual void _q_receiveReply() override; - virtual void _q_readyRead() override; - virtual bool sendRequest() override; - -private slots: - void _q_uploadDataReadyRead(); - void _q_replyDestroyed(QObject*); - void _q_uploadDataDestroyed(QObject *); - -private: - - enum FrameType { - FrameType_SYN_STREAM = 1, - FrameType_SYN_REPLY = 2, - FrameType_RST_STREAM = 3, - FrameType_SETTINGS = 4, - FrameType_PING = 6, - FrameType_GOAWAY = 7, - FrameType_HEADERS = 8, - FrameType_WINDOW_UPDATE = 9, - FrameType_CREDENTIAL // has a special type - }; - - enum StatusCode { - StatusCode_PROTOCOL_ERROR = 1, - StatusCode_INVALID_STREAM = 2, - StatusCode_REFUSED_STREAM = 3, - StatusCode_UNSUPPORTED_VERSION = 4, - StatusCode_CANCEL = 5, - StatusCode_INTERNAL_ERROR = 6, - StatusCode_FLOW_CONTROL_ERROR = 7, - StatusCode_STREAM_IN_USE = 8, - StatusCode_STREAM_ALREADY_CLOSED = 9, - StatusCode_INVALID_CREDENTIALS = 10, - StatusCode_FRAME_TOO_LARGE = 11 - }; - - enum SETTINGS_ID { - SETTINGS_UPLOAD_BANDWIDTH = 1, - SETTINGS_DOWNLOAD_BANDWIDTH = 2, - SETTINGS_ROUND_TRIP_TIME = 3, - SETTINGS_MAX_CONCURRENT_STREAMS = 4, - SETTINGS_CURRENT_CWND = 5, - SETTINGS_DOWNLOAD_RETRANS_RATE = 6, - SETTINGS_INITIAL_WINDOW_SIZE = 7, - SETTINGS_CLIENT_CERTIFICATE_VECTOR_SIZE = 8 - }; - - enum GOAWAY_STATUS { - GOAWAY_OK = 0, - GOAWAY_PROTOCOL_ERROR = 1, - GOAWAY_INTERNAL_ERROR = 11 - }; - - enum RST_STREAM_STATUS_CODE { - RST_STREAM_PROTOCOL_ERROR = 1, - RST_STREAM_INVALID_STREAM = 2, - RST_STREAM_REFUSED_STREAM = 3, - RST_STREAM_UNSUPPORTED_VERSION = 4, - RST_STREAM_CANCEL = 5, - RST_STREAM_INTERNAL_ERROR = 6, - RST_STREAM_FLOW_CONTROL_ERROR = 7, - RST_STREAM_STREAM_IN_USE = 8, - RST_STREAM_STREAM_ALREADY_CLOSED = 9, - RST_STREAM_INVALID_CREDENTIALS = 10, - RST_STREAM_FRAME_TOO_LARGE = 11 - }; - - quint64 bytesAvailable() const; - bool readNextChunk(qint64 length, char *sink); - - void sendControlFrame(FrameType type, ControlFrameFlags flags, const char *data, quint32 length); - - void sendSYN_STREAM(const HttpMessagePair &pair, qint32 streamID, - qint32 associatedToStreamID); - void sendRST_STREAM(qint32 streamID, RST_STREAM_STATUS_CODE statusCode); - void sendPING(quint32 pingID); - - bool uploadData(qint32 streamID); - Q_INVOKABLE void sendWINDOW_UPDATE(qint32 streamID, quint32 deltaWindowSize); - - qint64 sendDataFrame(qint32 streamID, DataFrameFlags flags, quint32 length, - const char *data); - - QByteArray composeHeader(const QHttpNetworkRequest &request); - bool uncompressHeader(const QByteArray &input, QByteArray *output); - - void handleControlFrame(const QByteArray &frameHeaders); - void handleDataFrame(const QByteArray &frameHeaders); - - void handleSYN_STREAM(char, quint32, const QByteArray &frameData); - void handleSYN_REPLY(char flags, quint32, const QByteArray &frameData); - void handleRST_STREAM(char flags, quint32 length, const QByteArray &frameData); - void handleSETTINGS(char flags, quint32 length, const QByteArray &frameData); - void handlePING(char, quint32 length, const QByteArray &frameData); - void handleGOAWAY(char flags, quint32, const QByteArray &frameData); - void handleHEADERS(char flags, quint32, const QByteArray &frameData); - void handleWINDOW_UPDATE(char, quint32, const QByteArray &frameData); - - qint32 generateNextStreamID(); - void parseHttpHeaders(char flags, const QByteArray &frameData); - - void replyFinished(QHttpNetworkReply *httpReply, qint32 streamID); - void replyFinishedWithError(QHttpNetworkReply *httpReply, qint32 streamID, - QNetworkReply::NetworkError errorCode, const char *errorMessage); - - qint32 m_nextStreamID; - QHash m_inFlightStreams; - qint32 m_maxConcurrentStreams; - quint32 m_initialWindowSize; - QByteDataBuffer m_spdyBuffer; - bool m_waitingForCompleteStream; - z_stream m_deflateStream; - z_stream m_inflateStream; - QHash m_streamIDs; -}; - -Q_DECLARE_OPERATORS_FOR_FLAGS(QSpdyProtocolHandler::DataFrameFlags) -Q_DECLARE_OPERATORS_FOR_FLAGS(QSpdyProtocolHandler::ControlFrameFlags) -Q_DECLARE_OPERATORS_FOR_FLAGS(QSpdyProtocolHandler::SETTINGS_Flags) -Q_DECLARE_OPERATORS_FOR_FLAGS(QSpdyProtocolHandler::SETTINGS_ID_Flags) - -QT_END_NAMESPACE - -#endif // !defined(QT_NO_SSL) - -#endif // QSPDYPROTOCOLHANDLER_H diff --git a/src/network/ssl/qsslconfiguration.cpp b/src/network/ssl/qsslconfiguration.cpp index 7e92d3a526..89a47908fc 100644 --- a/src/network/ssl/qsslconfiguration.cpp +++ b/src/network/ssl/qsslconfiguration.cpp @@ -54,7 +54,6 @@ const QSsl::SslOptions QSslConfigurationPrivate::defaultSslOptions = QSsl::SslOp |QSsl::SslOptionDisableSessionPersistence; const char QSslConfiguration::ALPNProtocolHTTP2[] = "h2"; -const char QSslConfiguration::NextProtocolSpdy3_0[] = "spdy/3"; const char QSslConfiguration::NextProtocolHttp1_1[] = "http/1.1"; /*! @@ -133,12 +132,6 @@ const char QSslConfiguration::NextProtocolHttp1_1[] = "http/1.1"; server could not agree on a common next application protocol. */ -/*! - \variable QSslConfiguration::NextProtocolSpdy3_0 - \brief The value used for negotiating SPDY 3.0 during the Next - Protocol Negotiation. -*/ - /*! \variable QSslConfiguration::NextProtocolHttp1_1 \brief The value used for negotiating HTTP 1.1 during the Next @@ -967,7 +960,7 @@ QByteArray QSslConfiguration::nextNegotiatedProtocol() const Whether or not the negotiation succeeded can be queried through nextProtocolNegotiationStatus(). - \sa nextNegotiatedProtocol(), nextProtocolNegotiationStatus(), allowedNextProtocols(), QSslConfiguration::NextProtocolSpdy3_0, QSslConfiguration::NextProtocolHttp1_1 + \sa nextNegotiatedProtocol(), nextProtocolNegotiationStatus(), allowedNextProtocols(), QSslConfiguration::NextProtocolHttp1_1 */ #if QT_VERSION >= QT_VERSION_CHECK(6,0,0) void QSslConfiguration::setAllowedNextProtocols(const QList &protocols) @@ -985,7 +978,7 @@ void QSslConfiguration::setAllowedNextProtocols(QList protocols) server through the Next Protocol Negotiation (NPN) or Application-Layer Protocol Negotiation (ALPN) TLS extension, as set by setAllowedNextProtocols(). - \sa nextNegotiatedProtocol(), nextProtocolNegotiationStatus(), setAllowedNextProtocols(), QSslConfiguration::NextProtocolSpdy3_0, QSslConfiguration::NextProtocolHttp1_1 + \sa nextNegotiatedProtocol(), nextProtocolNegotiationStatus(), setAllowedNextProtocols(), QSslConfiguration::NextProtocolHttp1_1 */ QList QSslConfiguration::allowedNextProtocols() const { diff --git a/src/network/ssl/qsslconfiguration.h b/src/network/ssl/qsslconfiguration.h index c25c2686de..6e73292922 100644 --- a/src/network/ssl/qsslconfiguration.h +++ b/src/network/ssl/qsslconfiguration.h @@ -188,7 +188,6 @@ public: NextProtocolNegotiationStatus nextProtocolNegotiationStatus() const; static const char ALPNProtocolHTTP2[]; - static const char NextProtocolSpdy3_0[]; static const char NextProtocolHttp1_1[]; private: diff --git a/tests/auto/network/access/access.pro b/tests/auto/network/access/access.pro index b140b5e9f2..d2b4d97b21 100644 --- a/tests/auto/network/access/access.pro +++ b/tests/auto/network/access/access.pro @@ -7,7 +7,6 @@ SUBDIRS=\ qnetworkrequest \ qhttpnetworkconnection \ qnetworkreply \ - spdy \ qnetworkcachemetadata \ qftp \ qhttpnetworkreply \ diff --git a/tests/auto/network/access/http2/tst_http2.cpp b/tests/auto/network/access/http2/tst_http2.cpp index c264956d7b..904cfd52b0 100644 --- a/tests/auto/network/access/http2/tst_http2.cpp +++ b/tests/auto/network/access/http2/tst_http2.cpp @@ -219,12 +219,12 @@ void tst_Http2::singleRequest_data() // 'Clear text' that should always work, either via the protocol upgrade // or as direct. - QTest::addRow("h2c-upgrade") << QNetworkRequest::HTTP2AllowedAttribute << H2Type::h2c; + QTest::addRow("h2c-upgrade") << QNetworkRequest::Http2AllowedAttribute << H2Type::h2c; QTest::addRow("h2c-direct") << QNetworkRequest::Http2DirectAttribute << H2Type::h2cDirect; if (!clearTextHTTP2) { // Qt with TLS where TLS-backend supports ALPN. - QTest::addRow("h2-ALPN") << QNetworkRequest::HTTP2AllowedAttribute << H2Type::h2Alpn; + QTest::addRow("h2-ALPN") << QNetworkRequest::Http2AllowedAttribute << H2Type::h2Alpn; } #if QT_CONFIG(ssl) @@ -429,7 +429,7 @@ void tst_Http2::pushPromise() url.setPath("/index.html"); QNetworkRequest request(url); - request.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, QVariant(true)); + request.setAttribute(QNetworkRequest::Http2AllowedAttribute, QVariant(true)); request.setHttp2Configuration(params); auto reply = manager->get(request); @@ -455,7 +455,7 @@ void tst_Http2::pushPromise() url.setPath("/script.js"); QNetworkRequest promisedRequest(url); - promisedRequest.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, QVariant(true)); + promisedRequest.setAttribute(QNetworkRequest::Http2AllowedAttribute, QVariant(true)); reply = manager->get(promisedRequest); connect(reply, &QNetworkReply::finished, this, &tst_Http2::replyFinished); reply->ignoreSslErrors(); @@ -509,7 +509,7 @@ void tst_Http2::goaway() for (int i = 0; i < nRequests; ++i) { url.setPath(QString("/%1").arg(i)); QNetworkRequest request(url); - request.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, QVariant(true)); + request.setAttribute(QNetworkRequest::Http2AllowedAttribute, QVariant(true)); replies[i] = manager->get(request); QCOMPARE(replies[i]->error(), QNetworkReply::NoError); void (QNetworkReply::*errorSignal)(QNetworkReply::NetworkError) = @@ -582,7 +582,7 @@ void tst_Http2::connectToHost_data() #if QT_CONFIG(ssl) QTest::addRow("encrypted-h2-direct") << QNetworkRequest::Http2DirectAttribute << H2Type::h2Direct; if (!clearTextHTTP2) - QTest::addRow("encrypted-h2-ALPN") << QNetworkRequest::HTTP2AllowedAttribute << H2Type::h2Alpn; + QTest::addRow("encrypted-h2-ALPN") << QNetworkRequest::Http2AllowedAttribute << H2Type::h2Alpn; #endif // QT_CONFIG(ssl) // This works for all configurations, tests 'preconnect-http' scheme: // h2 with protocol upgrade is not working for now (the logic is a bit @@ -725,7 +725,7 @@ void tst_Http2::maxFrameSize() #endif // QT_CONFIG(securetransport) auto connectionType = H2Type::h2Alpn; - auto attribute = QNetworkRequest::HTTP2AllowedAttribute; + auto attribute = QNetworkRequest::Http2AllowedAttribute; if (clearTextHTTP2) { connectionType = H2Type::h2Direct; attribute = QNetworkRequest::Http2DirectAttribute; @@ -825,7 +825,7 @@ void tst_Http2::sendRequest(int streamNumber, url.setPath(QString("/stream%1.html").arg(streamNumber)); QNetworkRequest request(url); - request.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, QVariant(true)); + request.setAttribute(QNetworkRequest::Http2AllowedAttribute, QVariant(true)); request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, QVariant(true)); request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("text/plain")); request.setPriority(priority); @@ -932,20 +932,13 @@ void tst_Http2::replyFinished() QCOMPARE(reply->error(), QNetworkReply::NoError); - const QVariant http2Used(reply->attribute(QNetworkRequest::HTTP2WasUsedAttribute)); + const QVariant http2Used(reply->attribute(QNetworkRequest::Http2WasUsedAttribute)); if (!http2Used.isValid() || !http2Used.toBool()) stopEventLoop(); QVERIFY(http2Used.isValid()); QVERIFY(http2Used.toBool()); - const QVariant spdyUsed(reply->attribute(QNetworkRequest::SpdyWasUsedAttribute)); - if (!spdyUsed.isValid() || spdyUsed.toBool()) - stopEventLoop(); - - QVERIFY(spdyUsed.isValid()); - QVERIFY(!spdyUsed.toBool()); - const QVariant code(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute)); if (!code.isValid() || !code.canConvert() || code.value() != 200) stopEventLoop(); diff --git a/tests/auto/network/access/spdy/BLACKLIST b/tests/auto/network/access/spdy/BLACKLIST deleted file mode 100644 index 5cf79327be..0000000000 --- a/tests/auto/network/access/spdy/BLACKLIST +++ /dev/null @@ -1,7 +0,0 @@ -[download] -opensuse-leap -[upload] -opensuse-leap -ubuntu-18.04 -b2qt - diff --git a/tests/auto/network/access/spdy/spdy.pro b/tests/auto/network/access/spdy/spdy.pro deleted file mode 100644 index cdbe60a19b..0000000000 --- a/tests/auto/network/access/spdy/spdy.pro +++ /dev/null @@ -1,7 +0,0 @@ -CONFIG += testcase -TARGET = tst_spdy -SOURCES += tst_spdy.cpp - -QT = core core-private network network-private testlib - -win32:CONFIG += insignificant_test # QTBUG-47128 diff --git a/tests/auto/network/access/spdy/tst_spdy.cpp b/tests/auto/network/access/spdy/tst_spdy.cpp deleted file mode 100644 index f4a5976558..0000000000 --- a/tests/auto/network/access/spdy/tst_spdy.cpp +++ /dev/null @@ -1,693 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 BlackBerry Limited. All rights reserved. -** 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 -#include -#include -#include -#include -#include -#include -#if defined(QT_BUILD_INTERNAL) && !defined(QT_NO_OPENSSL) -#include -#endif // QT_BUILD_INTERNAL && !QT_NO_OPENSSL - -#include "../../../network-settings.h" - -Q_DECLARE_METATYPE(QAuthenticator*) - -class tst_Spdy: public QObject -{ - Q_OBJECT - -public: - tst_Spdy(); - ~tst_Spdy(); - -private Q_SLOTS: - void initTestCase(); - void settingsAndNegotiation_data(); - void settingsAndNegotiation(); -#ifndef QT_NO_NETWORKPROXY - void download_data(); - void download(); -#endif // !QT_NO_NETWORKPROXY - void headerFields(); -#ifndef QT_NO_NETWORKPROXY - void upload_data(); - void upload(); - void errors_data(); - void errors(); -#endif // !QT_NO_NETWORKPROXY - void multipleRequests_data(); - void multipleRequests(); - -private: - QNetworkAccessManager m_manager; - int m_multipleRequestsCount; - int m_multipleRepliesFinishedCount; - const QString m_rfc3252FilePath; - -protected Q_SLOTS: - void proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *authenticator); - void multipleRequestsFinishedSlot(); -}; - -tst_Spdy::tst_Spdy() - : m_rfc3252FilePath(QFINDTESTDATA("../qnetworkreply/rfc3252.txt")) -{ -#if defined(QT_BUILD_INTERNAL) && !defined(QT_NO_SSL) && OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG) - qRegisterMetaType(); // for QSignalSpy - qRegisterMetaType(); - - connect(&m_manager, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)), - this, SLOT(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *))); -#else - QSKIP("Qt built withouth OpenSSL, or the OpenSSL version is too old"); -#endif // defined(QT_BUILD_INTERNAL) && !defined(QT_NO_SSL) ... -} - -tst_Spdy::~tst_Spdy() -{ -} - -void tst_Spdy::initTestCase() -{ - QVERIFY(!m_rfc3252FilePath.isEmpty()); - QVERIFY(QtNetworkSettings::verifyTestNetworkSettings()); -} - -void tst_Spdy::settingsAndNegotiation_data() -{ - QTest::addColumn("url"); - QTest::addColumn("setAttribute"); - QTest::addColumn("enabled"); - QTest::addColumn("expectedProtocol"); - QTest::addColumn("expectedContent"); - - QTest::newRow("default-settings") << QUrl("https://" + QtNetworkSettings::serverName() - + "/qtest/cgi-bin/echo.cgi?1") - << false << false << QByteArray() - << QByteArray("1"); - - QTest::newRow("http-url") << QUrl("http://" + QtNetworkSettings::serverName() - + "/qtest/cgi-bin/echo.cgi?1") - << true << true << QByteArray() - << QByteArray("1"); - - QTest::newRow("spdy-disabled") << QUrl("https://" + QtNetworkSettings::serverName() - + "/qtest/cgi-bin/echo.cgi?1") - << true << false << QByteArray() - << QByteArray("1"); - -#ifndef QT_NO_OPENSSL - QTest::newRow("spdy-enabled") << QUrl("https://" + QtNetworkSettings::serverName() - + "/qtest/cgi-bin/echo.cgi?1") - << true << true << QByteArray(QSslConfiguration::NextProtocolSpdy3_0) - << QByteArray("1"); -#endif // QT_NO_OPENSSL -} - -void tst_Spdy::settingsAndNegotiation() -{ - QFETCH(QUrl, url); - QFETCH(bool, setAttribute); - QFETCH(bool, enabled); - - QNetworkRequest request(url); - - if (setAttribute) { - request.setAttribute(QNetworkRequest::SpdyAllowedAttribute, QVariant(enabled)); - } - - QNetworkReply *reply = m_manager.get(request); - reply->ignoreSslErrors(); - QSignalSpy metaDataChangedSpy(reply, SIGNAL(metaDataChanged())); - QSignalSpy readyReadSpy(reply, SIGNAL(readyRead())); - QSignalSpy finishedSpy(reply, SIGNAL(finished())); - - QObject::connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); - QSignalSpy finishedManagerSpy(&m_manager, SIGNAL(finished(QNetworkReply*))); - - QTestEventLoop::instance().enterLoop(15); - QVERIFY(!QTestEventLoop::instance().timeout()); - - QFETCH(QByteArray, expectedProtocol); - -#ifndef QT_NO_OPENSSL - bool expectedSpdyUsed = (expectedProtocol == QSslConfiguration::NextProtocolSpdy3_0); - QCOMPARE(reply->attribute(QNetworkRequest::SpdyWasUsedAttribute).toBool(), expectedSpdyUsed); -#endif // QT_NO_OPENSSL - - QCOMPARE(metaDataChangedSpy.count(), 1); - QCOMPARE(finishedSpy.count(), 1); - - int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - QCOMPARE(statusCode, 200); - - QByteArray content = reply->readAll(); - - QFETCH(QByteArray, expectedContent); - QCOMPARE(expectedContent, content); - -#ifndef QT_NO_OPENSSL - QSslConfiguration::NextProtocolNegotiationStatus expectedStatus = - (expectedProtocol.isEmpty()) - ? QSslConfiguration::NextProtocolNegotiationNone - : QSslConfiguration::NextProtocolNegotiationNegotiated; - QCOMPARE(reply->sslConfiguration().nextProtocolNegotiationStatus(), - expectedStatus); - - QCOMPARE(reply->sslConfiguration().nextNegotiatedProtocol(), expectedProtocol); -#endif // QT_NO_OPENSSL -} - -void tst_Spdy::proxyAuthenticationRequired(const QNetworkProxy &/*proxy*/, - QAuthenticator *authenticator) -{ - authenticator->setUser("qsockstest"); - authenticator->setPassword("password"); -} - -#ifndef QT_NO_NETWORKPROXY -void tst_Spdy::download_data() -{ - QTest::addColumn("url"); - QTest::addColumn("fileName"); - QTest::addColumn("proxy"); - - QTest::newRow("mediumfile") << QUrl("https://" + QtNetworkSettings::serverName() - + "/qtest/rfc3252.txt") - << m_rfc3252FilePath - << QNetworkProxy(); - - QHostInfo hostInfo = QHostInfo::fromName(QtNetworkSettings::serverName()); - QString proxyserver = hostInfo.addresses().first().toString(); - - QTest::newRow("mediumfile-http-proxy") << QUrl("https://" + QtNetworkSettings::serverName() - + "/qtest/rfc3252.txt") - << m_rfc3252FilePath - << QNetworkProxy(QNetworkProxy::HttpProxy, proxyserver, 3128); - - QTest::newRow("mediumfile-http-proxy-auth") << QUrl("https://" + QtNetworkSettings::serverName() - + "/qtest/rfc3252.txt") - << m_rfc3252FilePath - << QNetworkProxy(QNetworkProxy::HttpProxy, - proxyserver, 3129); - - QTest::newRow("mediumfile-socks-proxy") << QUrl("https://" + QtNetworkSettings::serverName() - + "/qtest/rfc3252.txt") - << m_rfc3252FilePath - << QNetworkProxy(QNetworkProxy::Socks5Proxy, proxyserver, 1080); - - QTest::newRow("mediumfile-socks-proxy-auth") << QUrl("https://" + QtNetworkSettings::serverName() - + "/qtest/rfc3252.txt") - << m_rfc3252FilePath - << QNetworkProxy(QNetworkProxy::Socks5Proxy, - proxyserver, 1081); - - QTest::newRow("bigfile") << QUrl("https://" + QtNetworkSettings::serverName() - + "/qtest/bigfile") - << QFINDTESTDATA("../qnetworkreply/bigfile") - << QNetworkProxy(); -} - -void tst_Spdy::download() -{ - QFETCH(QUrl, url); - QFETCH(QString, fileName); - QFETCH(QNetworkProxy, proxy); - - QNetworkRequest request(url); - request.setAttribute(QNetworkRequest::SpdyAllowedAttribute, true); - - if (proxy.type() != QNetworkProxy::DefaultProxy) { - m_manager.setProxy(proxy); - } - QNetworkReply *reply = m_manager.get(request); - reply->ignoreSslErrors(); - QSignalSpy metaDataChangedSpy(reply, SIGNAL(metaDataChanged())); - QSignalSpy downloadProgressSpy(reply, SIGNAL(downloadProgress(qint64, qint64))); - QSignalSpy readyReadSpy(reply, SIGNAL(readyRead())); - QSignalSpy finishedSpy(reply, SIGNAL(finished())); - - QObject::connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); - QSignalSpy finishedManagerSpy(&m_manager, SIGNAL(finished(QNetworkReply*))); - QSignalSpy proxyAuthRequiredSpy(&m_manager, SIGNAL( - proxyAuthenticationRequired(const QNetworkProxy &, - QAuthenticator *))); - - QTestEventLoop::instance().enterLoop(15); - QVERIFY(!QTestEventLoop::instance().timeout()); - - QCOMPARE(finishedManagerSpy.count(), 1); - QCOMPARE(metaDataChangedSpy.count(), 1); - QCOMPARE(finishedSpy.count(), 1); - QVERIFY(downloadProgressSpy.count() > 0); - QVERIFY(readyReadSpy.count() > 0); - - QVERIFY(proxyAuthRequiredSpy.count() <= 1); - - QCOMPARE(reply->error(), QNetworkReply::NoError); - QCOMPARE(reply->attribute(QNetworkRequest::SpdyWasUsedAttribute).toBool(), true); - QCOMPARE(reply->attribute(QNetworkRequest::ConnectionEncryptedAttribute).toBool(), true); - QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); - - QFile file(fileName); - QVERIFY(file.open(QIODevice::ReadOnly)); - - qint64 contentLength = reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(); - qint64 expectedContentLength = file.bytesAvailable(); - QCOMPARE(contentLength, expectedContentLength); - - QByteArray expectedContent = file.readAll(); - QByteArray content = reply->readAll(); - QCOMPARE(content, expectedContent); - - reply->deleteLater(); - m_manager.setProxy(QNetworkProxy()); // reset -} -#endif // !QT_NO_NETWORKPROXY - -void tst_Spdy::headerFields() -{ - QUrl url(QUrl("https://" + QtNetworkSettings::serverName())); - QNetworkRequest request(url); - request.setAttribute(QNetworkRequest::SpdyAllowedAttribute, true); - - QNetworkReply *reply = m_manager.get(request); - reply->ignoreSslErrors(); - - QObject::connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); - - QTestEventLoop::instance().enterLoop(15); - QVERIFY(!QTestEventLoop::instance().timeout()); - - QCOMPARE(reply->rawHeader("Content-Type"), QByteArray("text/html")); - QVERIFY(reply->rawHeader("Content-Length").toInt() > 0); - QVERIFY(reply->rawHeader("server").contains("Apache")); - - QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader).toByteArray(), QByteArray("text/html")); - QVERIFY(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong() > 0); - QVERIFY(reply->header(QNetworkRequest::LastModifiedHeader).toDateTime().isValid()); - QVERIFY(reply->header(QNetworkRequest::ServerHeader).toByteArray().contains("Apache")); -} - -static inline QByteArray md5sum(const QByteArray &data) -{ - return QCryptographicHash::hash(data, QCryptographicHash::Md5).toHex().append('\n'); -} - -#ifndef QT_NO_NETWORKPROXY -void tst_Spdy::upload_data() -{ - QTest::addColumn("url"); - QTest::addColumn("data"); - QTest::addColumn("uploadMethod"); - QTest::addColumn("uploadObject"); - QTest::addColumn("md5sum"); - QTest::addColumn("proxy"); - - - // 1. test uploading of byte arrays - - QUrl md5Url("https://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/md5sum.cgi"); - - QByteArray data; - data = ""; - QObject *dummyObject = 0; - QTest::newRow("empty") << md5Url << data << QByteArray("POST") << dummyObject - << md5sum(data) << QNetworkProxy(); - - data = "This is a normal message."; - QTest::newRow("generic") << md5Url << data << QByteArray("POST") << dummyObject - << md5sum(data) << QNetworkProxy(); - - data = "This is a message to show that Qt rocks!\r\n\n"; - QTest::newRow("small") << md5Url << data << QByteArray("POST") << dummyObject - << md5sum(data) << QNetworkProxy(); - - data = QByteArray("abcd\0\1\2\abcd",12); - QTest::newRow("with-nul") << md5Url << data << QByteArray("POST") << dummyObject - << md5sum(data) << QNetworkProxy(); - - data = QByteArray(4097, '\4'); - QTest::newRow("4k+1") << md5Url << data << QByteArray("POST") << dummyObject - << md5sum(data)<< QNetworkProxy(); - - QHostInfo hostInfo = QHostInfo::fromName(QtNetworkSettings::serverName()); - QString proxyserver = hostInfo.addresses().first().toString(); - - QTest::newRow("4k+1-with-http-proxy") << md5Url << data << QByteArray("POST") << dummyObject - << md5sum(data) - << QNetworkProxy(QNetworkProxy::HttpProxy, proxyserver, 3128); - - QTest::newRow("4k+1-with-http-proxy-auth") << md5Url << data << QByteArray("POST") << dummyObject - << md5sum(data) - << QNetworkProxy(QNetworkProxy::HttpProxy, - proxyserver, 3129); - - QTest::newRow("4k+1-with-socks-proxy") << md5Url << data << QByteArray("POST") << dummyObject - << md5sum(data) - << QNetworkProxy(QNetworkProxy::Socks5Proxy, proxyserver, 1080); - - QTest::newRow("4k+1-with-socks-proxy-auth") << md5Url << data << QByteArray("POST") << dummyObject - << md5sum(data) - << QNetworkProxy(QNetworkProxy::Socks5Proxy, - proxyserver, 1081); - - data = QByteArray(128*1024+1, '\177'); - QTest::newRow("128k+1") << md5Url << data << QByteArray("POST") << dummyObject - << md5sum(data) << QNetworkProxy(); - - data = QByteArray(128*1024+1, '\177'); - QTest::newRow("128k+1-put") << md5Url << data << QByteArray("PUT") << dummyObject - << md5sum(data) << QNetworkProxy(); - - data = QByteArray(2*1024*1024+1, '\177'); - QTest::newRow("2MB+1") << md5Url << data << QByteArray("POST") << dummyObject - << md5sum(data) << QNetworkProxy(); - - - // 2. test uploading of files - - QFile *file = new QFile(m_rfc3252FilePath); - file->open(QIODevice::ReadOnly); - QTest::newRow("file-26K") << md5Url << QByteArray() << QByteArray("POST") - << static_cast(file) - << QByteArray("b3e32ac459b99d3f59318f3ac31e4bee\n") << QNetworkProxy(); - - QFile *file2 = new QFile(QFINDTESTDATA("../qnetworkreply/image1.jpg")); - file2->open(QIODevice::ReadOnly); - QTest::newRow("file-1MB") << md5Url << QByteArray() << QByteArray("POST") - << static_cast(file2) - << QByteArray("87ef3bb319b004ba9e5e9c9fa713776e\n") << QNetworkProxy(); - - - // 3. test uploading of multipart - - QUrl multiPartUrl("https://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/multipart.cgi"); - - QHttpPart imagePart31; - imagePart31.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg")); - imagePart31.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"testImage1\"")); - imagePart31.setRawHeader("Content-Location", "http://my.test.location.tld"); - imagePart31.setRawHeader("Content-ID", "my@id.tld"); - QFile *file31 = new QFile(QFINDTESTDATA("../qnetworkreply/image1.jpg")); - file31->open(QIODevice::ReadOnly); - imagePart31.setBodyDevice(file31); - QHttpMultiPart *imageMultiPart3 = new QHttpMultiPart(QHttpMultiPart::FormDataType); - imageMultiPart3->append(imagePart31); - file31->setParent(imageMultiPart3); - QHttpPart imagePart32; - imagePart32.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg")); - imagePart32.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"testImage2\"")); - QFile *file32 = new QFile(QFINDTESTDATA("../qnetworkreply/image2.jpg")); - file32->open(QIODevice::ReadOnly); - imagePart32.setBodyDevice(file31); // check that resetting works - imagePart32.setBodyDevice(file32); - imageMultiPart3->append(imagePart32); - file32->setParent(imageMultiPart3); - QHttpPart imagePart33; - imagePart33.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg")); - imagePart33.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"testImage3\"")); - QFile *file33 = new QFile(QFINDTESTDATA("../qnetworkreply/image3.jpg")); - file33->open(QIODevice::ReadOnly); - imagePart33.setBodyDevice(file33); - imageMultiPart3->append(imagePart33); - file33->setParent(imageMultiPart3); - QByteArray expectedData = "content type: multipart/form-data; boundary=\"" - + imageMultiPart3->boundary(); - expectedData.append("\"\nkey: testImage1, value: 87ef3bb319b004ba9e5e9c9fa713776e\n" - "key: testImage2, value: 483761b893f7fb1bd2414344cd1f3dfb\n" - "key: testImage3, value: ab0eb6fd4fcf8b4436254870b4513033\n"); - - QTest::newRow("multipart-3images") << multiPartUrl << QByteArray() << QByteArray("POST") - << static_cast(imageMultiPart3) << expectedData - << QNetworkProxy(); -} - -void tst_Spdy::upload() -{ - QFETCH(QUrl, url); - QNetworkRequest request(url); - request.setAttribute(QNetworkRequest::SpdyAllowedAttribute, true); - - QFETCH(QByteArray, data); - QFETCH(QByteArray, uploadMethod); - QFETCH(QObject *, uploadObject); - QFETCH(QNetworkProxy, proxy); - - if (proxy.type() != QNetworkProxy::DefaultProxy) { - m_manager.setProxy(proxy); - } - - QNetworkReply *reply; - QHttpMultiPart *multiPart = 0; - - if (uploadObject) { - // upload via device - if (QIODevice *device = qobject_cast(uploadObject)) { - reply = m_manager.post(request, device); - } else if ((multiPart = qobject_cast(uploadObject))) { - reply = m_manager.post(request, multiPart); - } else { - QFAIL("got unknown upload device"); - } - } else { - // upload via byte array - if (uploadMethod == "PUT") { - reply = m_manager.put(request, data); - } else { - reply = m_manager.post(request, data); - } - } - - reply->ignoreSslErrors(); - QSignalSpy metaDataChangedSpy(reply, SIGNAL(metaDataChanged())); - QSignalSpy uploadProgressSpy(reply, SIGNAL(uploadProgress(qint64, qint64))); - QSignalSpy readyReadSpy(reply, SIGNAL(readyRead())); - QSignalSpy finishedSpy(reply, SIGNAL(finished())); - - QObject::connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); - QSignalSpy finishedManagerSpy(&m_manager, SIGNAL(finished(QNetworkReply*))); - - QTestEventLoop::instance().enterLoop(20); - QVERIFY(!QTestEventLoop::instance().timeout()); - - QCOMPARE(finishedManagerSpy.count(), 1); - QCOMPARE(metaDataChangedSpy.count(), 1); - QCOMPARE(finishedSpy.count(), 1); - QVERIFY(uploadProgressSpy.count() > 0); - QVERIFY(readyReadSpy.count() > 0); - - QCOMPARE(reply->error(), QNetworkReply::NoError); - QCOMPARE(reply->attribute(QNetworkRequest::SpdyWasUsedAttribute).toBool(), true); - QCOMPARE(reply->attribute(QNetworkRequest::ConnectionEncryptedAttribute).toBool(), true); - QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); - - qint64 contentLength = reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(); - if (!multiPart) // script to test multiparts does not return a content length - QCOMPARE(contentLength, 33); // 33 bytes for md5 sums (including new line) - - QFETCH(QByteArray, md5sum); - QByteArray content = reply->readAll(); - QCOMPARE(content, md5sum); - - reply->deleteLater(); - if (uploadObject) - uploadObject->deleteLater(); - - m_manager.setProxy(QNetworkProxy()); // reset -} - -void tst_Spdy::errors_data() -{ - QTest::addColumn("url"); - QTest::addColumn("proxy"); - QTest::addColumn("ignoreSslErrors"); - QTest::addColumn("expectedReplyError"); - - QTest::newRow("http-404") << QUrl("https://" + QtNetworkSettings::serverName() + "/non-existent-url") - << QNetworkProxy() << true << int(QNetworkReply::ContentNotFoundError); - - QTest::newRow("ssl-errors") << QUrl("https://" + QtNetworkSettings::serverName()) - << QNetworkProxy() << false << int(QNetworkReply::SslHandshakeFailedError); - - QTest::newRow("host-not-found") << QUrl("https://this-host-does-not.exist") - << QNetworkProxy() - << true << int(QNetworkReply::HostNotFoundError); - - QTest::newRow("proxy-not-found") << QUrl("https://" + QtNetworkSettings::serverName()) - << QNetworkProxy(QNetworkProxy::HttpProxy, - "https://this-host-does-not.exist", 3128) - << true << int(QNetworkReply::HostNotFoundError); - - QHostInfo hostInfo = QHostInfo::fromName(QtNetworkSettings::serverName()); - QString proxyserver = hostInfo.addresses().first().toString(); - - QTest::newRow("proxy-unavailable") << QUrl("https://" + QtNetworkSettings::serverName()) - << QNetworkProxy(QNetworkProxy::HttpProxy, proxyserver, 10) - << true << int(QNetworkReply::UnknownNetworkError); - - QTest::newRow("no-proxy-credentials") << QUrl("https://" + QtNetworkSettings::serverName()) - << QNetworkProxy(QNetworkProxy::HttpProxy, proxyserver, 3129) - << true << int(QNetworkReply::ProxyAuthenticationRequiredError); -} - -void tst_Spdy::errors() -{ - QFETCH(QUrl, url); - QFETCH(QNetworkProxy, proxy); - QFETCH(bool, ignoreSslErrors); - QFETCH(int, expectedReplyError); - - QNetworkRequest request(url); - request.setAttribute(QNetworkRequest::SpdyAllowedAttribute, true); - - disconnect(&m_manager, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)), - 0, 0); - if (proxy.type() != QNetworkProxy::DefaultProxy) { - m_manager.setProxy(proxy); - } - QNetworkReply *reply = m_manager.get(request); - if (ignoreSslErrors) - reply->ignoreSslErrors(); - QSignalSpy finishedSpy(reply, SIGNAL(finished())); - QSignalSpy errorSpy(reply, SIGNAL(error(QNetworkReply::NetworkError))); - - QObject::connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); - - QTestEventLoop::instance().enterLoop(15); - QVERIFY(!QTestEventLoop::instance().timeout()); - - QCOMPARE(finishedSpy.count(), 1); - QCOMPARE(errorSpy.count(), 1); - - QCOMPARE(reply->error(), static_cast(expectedReplyError)); - - m_manager.setProxy(QNetworkProxy()); // reset - m_manager.clearAccessCache(); // e.g. to get an SSL error we need a new connection - connect(&m_manager, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)), - this, SLOT(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)), - Qt::UniqueConnection); // reset -} -#endif // !QT_NO_NETWORKPROXY - -void tst_Spdy::multipleRequests_data() -{ - QTest::addColumn >("urls"); - - QString baseUrl = "https://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/echo.cgi?"; - QList urls; - for (int a = 1; a <= 50; ++a) - urls.append(QUrl(baseUrl + QLatin1String(QByteArray::number(a)))); - - QTest::newRow("one-request") << urls.mid(0, 1); - QTest::newRow("two-requests") << urls.mid(0, 2); - QTest::newRow("ten-requests") << urls.mid(0, 10); - QTest::newRow("twenty-requests") << urls.mid(0, 20); - QTest::newRow("fifty-requests") << urls; -} - -void tst_Spdy::multipleRequestsFinishedSlot() -{ - m_multipleRepliesFinishedCount++; - if (m_multipleRepliesFinishedCount == m_multipleRequestsCount) - QTestEventLoop::instance().exitLoop(); -} - -void tst_Spdy::multipleRequests() -{ - QFETCH(QList, urls); - m_multipleRequestsCount = urls.count(); - m_multipleRepliesFinishedCount = 0; - - QList replies; - QList metaDataChangedSpies; - QList readyReadSpies; - QList finishedSpies; - - foreach (const QUrl &url, urls) { - QNetworkRequest request(url); - request.setAttribute(QNetworkRequest::SpdyAllowedAttribute, true); - QNetworkReply *reply = m_manager.get(request); - replies.append(reply); - reply->ignoreSslErrors(); - QObject::connect(reply, SIGNAL(finished()), this, SLOT(multipleRequestsFinishedSlot())); - QSignalSpy *metaDataChangedSpy = new QSignalSpy(reply, SIGNAL(metaDataChanged())); - metaDataChangedSpies << metaDataChangedSpy; - QSignalSpy *readyReadSpy = new QSignalSpy(reply, SIGNAL(readyRead())); - readyReadSpies << readyReadSpy; - QSignalSpy *finishedSpy = new QSignalSpy(reply, SIGNAL(finished())); - finishedSpies << finishedSpy; - } - - QSignalSpy finishedManagerSpy(&m_manager, SIGNAL(finished(QNetworkReply*))); - - QTestEventLoop::instance().enterLoop(15); - QVERIFY(!QTestEventLoop::instance().timeout()); - - QCOMPARE(finishedManagerSpy.count(), m_multipleRequestsCount); - - for (int a = 0; a < replies.count(); ++a) { - -#ifndef QT_NO_OPENSSL - QCOMPARE(replies.at(a)->sslConfiguration().nextProtocolNegotiationStatus(), - QSslConfiguration::NextProtocolNegotiationNegotiated); - QCOMPARE(replies.at(a)->sslConfiguration().nextNegotiatedProtocol(), - QByteArray(QSslConfiguration::NextProtocolSpdy3_0)); -#endif // QT_NO_OPENSSL - - QCOMPARE(replies.at(a)->error(), QNetworkReply::NoError); - QCOMPARE(replies.at(a)->attribute(QNetworkRequest::SpdyWasUsedAttribute).toBool(), true); - QCOMPARE(replies.at(a)->attribute(QNetworkRequest::ConnectionEncryptedAttribute).toBool(), true); - QCOMPARE(replies.at(a)->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); - - // using the echo script, a request to "echo.cgi?1" will return a body of "1" - QByteArray expectedContent = replies.at(a)->url().query().toUtf8(); - QByteArray content = replies.at(a)->readAll(); - QCOMPARE(expectedContent, content); - - QCOMPARE(metaDataChangedSpies.at(a)->count(), 1); - metaDataChangedSpies.at(a)->deleteLater(); - - QCOMPARE(finishedSpies.at(a)->count(), 1); - finishedSpies.at(a)->deleteLater(); - - QVERIFY(readyReadSpies.at(a)->count() > 0); - readyReadSpies.at(a)->deleteLater(); - - replies.at(a)->deleteLater(); - } -} - -QTEST_MAIN(tst_Spdy) - -#include "tst_spdy.moc" diff --git a/tests/benchmarks/network/access/qnetworkreply/tst_qnetworkreply.cpp b/tests/benchmarks/network/access/qnetworkreply/tst_qnetworkreply.cpp index c182ef7ebf..6dd7eaee6b 100644 --- a/tests/benchmarks/network/access/qnetworkreply/tst_qnetworkreply.cpp +++ b/tests/benchmarks/network/access/qnetworkreply/tst_qnetworkreply.cpp @@ -544,15 +544,10 @@ void tst_qnetworkreply::echoPerformance() void tst_qnetworkreply::preConnectEncrypted() { QFETCH(int, sleepTime); - QFETCH(QSslConfiguration, sslConfiguration); - bool spdyEnabled = !sslConfiguration.isNull(); - QString hostName = QLatin1String("www.google.com"); QNetworkAccessManager manager; QNetworkRequest request(QUrl("https://" + hostName)); - if (spdyEnabled) - request.setAttribute(QNetworkRequest::SpdyAllowedAttribute, true); // make sure we have a full request including // DNS lookup, TCP and SSL handshakes @@ -578,12 +573,7 @@ void tst_qnetworkreply::preConnectEncrypted() manager.clearAccessCache(); // now try to make the connection beforehand - if (spdyEnabled) { - request.setAttribute(QNetworkRequest::SpdyAllowedAttribute, true); - manager.connectToHostEncrypted(hostName, 443, sslConfiguration); - } else { - manager.connectToHostEncrypted(hostName); - } + manager.connectToHostEncrypted(hostName); QTestEventLoop::instance().enterLoopMSecs(sleepTime); // now make another request and hopefully use the existing connection @@ -591,8 +581,6 @@ void tst_qnetworkreply::preConnectEncrypted() QNetworkReply *preConnectReply = normalResult.first; QVERIFY(!QTestEventLoop::instance().timeout()); QVERIFY(preConnectReply->error() == QNetworkReply::NoError); - bool spdyWasUsed = preConnectReply->attribute(QNetworkRequest::SpdyWasUsedAttribute).toBool(); - QCOMPARE(spdyEnabled, spdyWasUsed); qint64 preConnectElapsed = preConnectResult.second; qDebug() << request.url().toString() << "full request:" << normalElapsed << "ms, pre-connect request:" << preConnectElapsed << "ms, difference:" @@ -605,27 +593,11 @@ void tst_qnetworkreply::preConnectEncrypted_data() { #ifndef QT_NO_OPENSSL QTest::addColumn("sleepTime"); - QTest::addColumn("sslConfiguration"); - // start a new normal request after preconnecting is done - QTest::newRow("HTTPS-2secs") << 2000 << QSslConfiguration(); + QTest::newRow("HTTPS-2secs") << 2000; // start a new normal request while preconnecting is in-flight - QTest::newRow("HTTPS-100ms") << 100 << QSslConfiguration(); - - QSslConfiguration spdySslConf = QSslConfiguration::defaultConfiguration(); - QList nextProtocols = QList() - << QSslConfiguration::NextProtocolSpdy3_0 - << QSslConfiguration::NextProtocolHttp1_1; - spdySslConf.setAllowedNextProtocols(nextProtocols); - -#if defined(QT_BUILD_INTERNAL) && !defined(QT_NO_SSL) && OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG) - // start a new SPDY request while preconnecting is done - QTest::newRow("SPDY-2secs") << 2000 << spdySslConf; - - // start a new SPDY request while preconnecting is in-flight - QTest::newRow("SPDY-100ms") << 100 << spdySslConf; -#endif // defined (QT_BUILD_INTERNAL) && !defined(QT_NO_SSL) ... + QTest::newRow("HTTPS-100ms") << 100; #endif // QT_NO_OPENSSL } diff --git a/tests/manual/qnetworkreply/main.cpp b/tests/manual/qnetworkreply/main.cpp index afac7a7095..01bd30a854 100644 --- a/tests/manual/qnetworkreply/main.cpp +++ b/tests/manual/qnetworkreply/main.cpp @@ -58,9 +58,6 @@ private slots: void setSslConfiguration_data(); void setSslConfiguration(); void uploadToFacebook(); - void spdy_data(); - void spdy(); - void spdyMultipleRequestsPerHost(); void proxyAuthentication_data(); void proxyAuthentication(); void authentication(); @@ -290,126 +287,6 @@ void tst_qnetworkreply::uploadToFacebook() } } -void tst_qnetworkreply::spdy_data() -{ - QTest::addColumn("host"); - QTest::addColumn("setAttribute"); - QTest::addColumn("enabled"); - QTest::addColumn("expectedProtocol"); - - QList hosts = QList() - << QStringLiteral("www.google.com") // sends SPDY and 30x redirect - << QStringLiteral("www.google.de") // sends SPDY and 200 OK - << QStringLiteral("mail.google.com") // sends SPDY and 200 OK - << QStringLiteral("www.youtube.com") // sends SPDY and 200 OK - << QStringLiteral("www.dropbox.com") // no SPDY, but NPN which selects HTTP - << QStringLiteral("www.facebook.com") // sends SPDY and 200 OK - << QStringLiteral("graph.facebook.com") // sends SPDY and 200 OK - << QStringLiteral("www.twitter.com") // sends SPDY and 30x redirect - << QStringLiteral("twitter.com") // sends SPDY and 200 OK - << QStringLiteral("api.twitter.com"); // sends SPDY and 200 OK - - foreach (const QString &host, hosts) { - QByteArray tag = host.toLocal8Bit(); - tag.append("-not-used"); - QTest::newRow(tag) - << QStringLiteral("https://") + host - << false - << false - << QByteArray(); - - tag = host.toLocal8Bit(); - tag.append("-disabled"); - QTest::newRow(tag) - << QStringLiteral("https://") + host - << true - << false - << QByteArray(); - - if (host != QStringLiteral("api.twitter.com")) { // they don't offer an API over HTTP - tag = host.toLocal8Bit(); - tag.append("-no-https-url"); - QTest::newRow(tag) - << QStringLiteral("http://") + host - << true - << true - << QByteArray(); - } - -#ifndef QT_NO_OPENSSL - tag = host.toLocal8Bit(); - tag.append("-enabled"); - QTest::newRow(tag) - << QStringLiteral("https://") + host - << true - << true - << (host == QStringLiteral("www.dropbox.com") - ? QByteArray(QSslConfiguration::NextProtocolHttp1_1) - : QByteArray(QSslConfiguration::NextProtocolSpdy3_0)); -#endif // QT_NO_OPENSSL - } -} - -void tst_qnetworkreply::spdy() -{ -#if defined(QT_BUILD_INTERNAL) && !defined(QT_NO_SSL) && OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG) - - m_manager.clearAccessCache(); - - QFETCH(QString, host); - QUrl url(host); - QNetworkRequest request(url); - - QFETCH(bool, setAttribute); - QFETCH(bool, enabled); - if (setAttribute) { - request.setAttribute(QNetworkRequest::SpdyAllowedAttribute, QVariant(enabled)); - } - - QNetworkReply *reply = m_manager.get(request); - QObject::connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); - - QSignalSpy metaDataChangedSpy(reply, SIGNAL(metaDataChanged())); - QSignalSpy readyReadSpy(reply, SIGNAL(readyRead())); - QSignalSpy finishedSpy(reply, SIGNAL(finished())); - QSignalSpy finishedManagerSpy(&m_manager, SIGNAL(finished(QNetworkReply*))); - - QTestEventLoop::instance().enterLoop(15); - QVERIFY(!QTestEventLoop::instance().timeout()); - - QFETCH(QByteArray, expectedProtocol); - - bool expectedSpdyUsed = (expectedProtocol == QSslConfiguration::NextProtocolSpdy3_0); - QCOMPARE(reply->attribute(QNetworkRequest::SpdyWasUsedAttribute).toBool(), expectedSpdyUsed); - - QCOMPARE(metaDataChangedSpy.count(), 1); - QCOMPARE(finishedSpy.count(), 1); - QCOMPARE(finishedManagerSpy.count(), 1); - - QUrl redirectUrl = reply->header(QNetworkRequest::LocationHeader).toUrl(); - QByteArray content = reply->readAll(); - - int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - QVERIFY(statusCode >= 200 && statusCode < 500); - if (statusCode == 200 || statusCode >= 400) { - QVERIFY(readyReadSpy.count() > 0); - QVERIFY(!content.isEmpty()); - } else if (statusCode >= 300 && statusCode < 400) { - QVERIFY(!redirectUrl.isEmpty()); - } - - QSslConfiguration::NextProtocolNegotiationStatus expectedStatus = - expectedProtocol.isNull() ? QSslConfiguration::NextProtocolNegotiationNone - : QSslConfiguration::NextProtocolNegotiationNegotiated; - QCOMPARE(reply->sslConfiguration().nextProtocolNegotiationStatus(), - expectedStatus); - - QCOMPARE(reply->sslConfiguration().nextNegotiatedProtocol(), expectedProtocol); -#else - QSKIP("Qt built withouth OpenSSL, or the OpenSSL version is too old"); -#endif // defined(QT_BUILD_INTERNAL) && !defined(QT_NO_SSL) ... -} - void tst_qnetworkreply::spdyReplyFinished() { static int finishedCount = 0; @@ -419,85 +296,6 @@ void tst_qnetworkreply::spdyReplyFinished() QTestEventLoop::instance().exitLoop(); } -void tst_qnetworkreply::spdyMultipleRequestsPerHost() -{ -#if defined(QT_BUILD_INTERNAL) && !defined(QT_NO_SSL) && OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG) - - QList requests; - requests - << QNetworkRequest(QUrl("https://www.facebook.com")) - << QNetworkRequest(QUrl("https://www.facebook.com/images/fb_icon_325x325.png")) - - << QNetworkRequest(QUrl("https://www.google.de")) - << QNetworkRequest(QUrl("https://www.google.de/preferences?hl=de")) - << QNetworkRequest(QUrl("https://www.google.de/intl/de/policies/?fg=1")) - << QNetworkRequest(QUrl("https://www.google.de/intl/de/about.html?fg=1")) - << QNetworkRequest(QUrl("https://www.google.de/services/?fg=1")) - << QNetworkRequest(QUrl("https://www.google.de/intl/de/ads/?fg=1")) - - << QNetworkRequest(QUrl("https://i1.ytimg.com/li/tnHdj3df7iM/default.jpg")) - << QNetworkRequest(QUrl("https://i1.ytimg.com/li/7Dr1BKwqctY/default.jpg")) - << QNetworkRequest(QUrl("https://i1.ytimg.com/li/hfZhJdhTqX8/default.jpg")) - << QNetworkRequest(QUrl("https://i1.ytimg.com/vi/14Nprh8163I/hqdefault.jpg")) - ; - QList replies; - QList metaDataChangedSpies; - QList readyReadSpies; - QList finishedSpies; - - QSignalSpy finishedManagerSpy(&m_manager, SIGNAL(finished(QNetworkReply*))); - - foreach (QNetworkRequest request, requests) { - request.setAttribute(QNetworkRequest::SpdyAllowedAttribute, true); - QNetworkReply *reply = m_manager.get(request); - QObject::connect(reply, SIGNAL(finished()), this, SLOT(spdyReplyFinished())); - replies << reply; - QSignalSpy *metaDataChangedSpy = new QSignalSpy(reply, SIGNAL(metaDataChanged())); - metaDataChangedSpies << metaDataChangedSpy; - QSignalSpy *readyReadSpy = new QSignalSpy(reply, SIGNAL(readyRead())); - readyReadSpies << readyReadSpy; - QSignalSpy *finishedSpy = new QSignalSpy(reply, SIGNAL(finished())); - finishedSpies << finishedSpy; - } - - QCOMPARE(requests.count(), replies.count()); - - QTestEventLoop::instance().enterLoop(15); - QVERIFY(!QTestEventLoop::instance().timeout()); - - QCOMPARE(finishedManagerSpy.count(), requests.count()); - - for (int a = 0; a < replies.count(); ++a) { - - QCOMPARE(replies.at(a)->sslConfiguration().nextProtocolNegotiationStatus(), - QSslConfiguration::NextProtocolNegotiationNegotiated); - QCOMPARE(replies.at(a)->sslConfiguration().nextNegotiatedProtocol(), - QByteArray(QSslConfiguration::NextProtocolSpdy3_0)); - - QCOMPARE(replies.at(a)->error(), QNetworkReply::NoError); - QCOMPARE(replies.at(a)->attribute(QNetworkRequest::SpdyWasUsedAttribute).toBool(), true); - QCOMPARE(replies.at(a)->attribute(QNetworkRequest::ConnectionEncryptedAttribute).toBool(), true); - QCOMPARE(replies.at(a)->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); - - QByteArray content = replies.at(a)->readAll(); - QVERIFY(content.count() > 0); - - QCOMPARE(metaDataChangedSpies.at(a)->count(), 1); - metaDataChangedSpies.at(a)->deleteLater(); - - QCOMPARE(finishedSpies.at(a)->count(), 1); - finishedSpies.at(a)->deleteLater(); - - QVERIFY(readyReadSpies.at(a)->count() > 0); - readyReadSpies.at(a)->deleteLater(); - - replies.at(a)->deleteLater(); - } -#else - QSKIP("Qt built withouth OpenSSL, or the OpenSSL version is too old"); -#endif // defined(QT_BUILD_INTERNAL) && !defined(QT_NO_SSL) ... -} - void tst_qnetworkreply::proxyAuthentication_data() { QTest::addColumn("url"); @@ -586,7 +384,7 @@ void tst_qnetworkreply::npnWithEmptyList() // QTBUG-40714 QUrl url(QStringLiteral("https://www.ossifrage.net/")); QNetworkRequest request(url); - request.setAttribute(QNetworkRequest::SpdyAllowedAttribute, QVariant(true)); + request.setAttribute(QNetworkRequest::Http2AllowedAttribute, QVariant(true)); QNetworkReply *reply = m_manager.get(request); QObject::connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); -- cgit v1.2.3