diff options
Diffstat (limited to 'src/network/access')
36 files changed, 385 insertions, 244 deletions
diff --git a/src/network/access/http2/bitstreams_p.h b/src/network/access/http2/bitstreams_p.h index 9eba319dc2..ca272062a6 100644 --- a/src/network/access/http2/bitstreams_p.h +++ b/src/network/access/http2/bitstreams_p.h @@ -89,7 +89,7 @@ public: void clear(); private: - Q_DISABLE_COPY(BitOStream); + Q_DISABLE_COPY_MOVE(BitOStream); std::vector<uchar> &buffer; quint64 bitsSet; diff --git a/src/network/access/http2/hpacktable.cpp b/src/network/access/http2/hpacktable.cpp index a90ee72d52..fddb5feca5 100644 --- a/src/network/access/http2/hpacktable.cpp +++ b/src/network/access/http2/hpacktable.cpp @@ -42,6 +42,7 @@ #include <QtCore/qdebug.h> #include <algorithm> +#include <cstddef> #include <cstring> #include <limits> @@ -61,7 +62,7 @@ HeaderSize entry_size(const QByteArray &name, const QByteArray &value) // for counting the number of references to the name and value would have // 32 octets of overhead." - const unsigned sum = unsigned(name.size()) + value.size(); + const unsigned sum = unsigned(name.size() + value.size()); if (std::numeric_limits<unsigned>::max() - 32 < sum) return HeaderSize(); return HeaderSize(true, quint32(sum + 32)); @@ -75,7 +76,7 @@ int compare(const QByteArray &lhs, const QByteArray &rhs) if (const int minLen = std::min(lhs.size(), rhs.size())) { // We use memcmp, since strings in headers are allowed // to contain '\0'. - const int cmp = std::memcmp(lhs.constData(), rhs.constData(), minLen); + const int cmp = std::memcmp(lhs.constData(), rhs.constData(), std::size_t(minLen)); if (cmp) return cmp; } @@ -138,82 +139,6 @@ bool FieldLookupTable::SearchEntry::operator < (const SearchEntry &rhs)const return offset > rhs.offset; } -// This data is from HPACK's specs and it's quite -// conveniently sorted == works with binary search as it is. -// Later this can probably change and instead of simple -// vector we'll just reuse FieldLookupTable. -// TODO: it makes sense to generate this table while ... -// configuring/building Qt (some script downloading/parsing/generating -// would be quite handy). -const std::vector<HeaderField> &staticTable() -{ - static std::vector<HeaderField> table = { - {":authority", ""}, - {":method", "GET"}, - {":method", "POST"}, - {":path", "/"}, - {":path", "/index.html"}, - {":scheme", "http"}, - {":scheme", "https"}, - {":status", "200"}, - {":status", "204"}, - {":status", "206"}, - {":status", "304"}, - {":status", "400"}, - {":status", "404"}, - {":status", "500"}, - {"accept-charset", ""}, - {"accept-encoding", "gzip, deflate"}, - {"accept-language", ""}, - {"accept-ranges", ""}, - {"accept", ""}, - {"access-control-allow-origin", ""}, - {"age", ""}, - {"allow", ""}, - {"authorization", ""}, - {"cache-control", ""}, - {"content-disposition", ""}, - {"content-encoding", ""}, - {"content-language", ""}, - {"content-length", ""}, - {"content-location", ""}, - {"content-range", ""}, - {"content-type", ""}, - {"cookie", ""}, - {"date", ""}, - {"etag", ""}, - {"expect", ""}, - {"expires", ""}, - {"from", ""}, - {"host", ""}, - {"if-match", ""}, - {"if-modified-since", ""}, - {"if-none-match", ""}, - {"if-range", ""}, - {"if-unmodified-since", ""}, - {"last-modified", ""}, - {"link", ""}, - {"location", ""}, - {"max-forwards", ""}, - {"proxy-authenticate", ""}, - {"proxy-authorization", ""}, - {"range", ""}, - {"referer", ""}, - {"refresh", ""}, - {"retry-after", ""}, - {"server", ""}, - {"set-cookie", ""}, - {"strict-transport-security", ""}, - {"transfer-encoding", ""}, - {"user-agent", ""}, - {"vary", ""}, - {"via", ""}, - {"www-authenticate", ""} - }; - - return table; -} - FieldLookupTable::FieldLookupTable(quint32 maxSize, bool use) : maxTableSize(maxSize), tableCapacity(maxSize), @@ -296,12 +221,12 @@ void FieldLookupTable::evictEntry() quint32 FieldLookupTable::numberOfEntries() const { - return quint32(staticTable().size()) + nDynamic; + return quint32(staticPart().size()) + nDynamic; } quint32 FieldLookupTable::numberOfStaticEntries() const { - return quint32(staticTable().size()); + return quint32(staticPart().size()); } quint32 FieldLookupTable::numberOfDynamicEntries() const @@ -326,24 +251,18 @@ void FieldLookupTable::clearDynamicTable() bool FieldLookupTable::indexIsValid(quint32 index) const { - return index && index <= staticTable().size() + nDynamic; + return index && index <= staticPart().size() + nDynamic; } quint32 FieldLookupTable::indexOf(const QByteArray &name, const QByteArray &value)const { // Start from the static part first: - const auto &table = staticTable(); + const auto &table = staticPart(); const HeaderField field(name, value); - const auto staticPos = std::lower_bound(table.begin(), table.end(), field, - [](const HeaderField &lhs, const HeaderField &rhs) { - int cmp = compare(lhs.name, rhs.name); - if (cmp) - return cmp < 0; - return compare(lhs.value, rhs.value) < 0; - }); + const auto staticPos = findInStaticPart(field, CompareMode::nameAndValue); if (staticPos != table.end()) { if (staticPos->name == name && staticPos->value == value) - return staticPos - table.begin() + 1; + return quint32(staticPos - table.begin() + 1); } // Now we have to lookup in our dynamic part ... @@ -366,15 +285,12 @@ quint32 FieldLookupTable::indexOf(const QByteArray &name, const QByteArray &valu quint32 FieldLookupTable::indexOf(const QByteArray &name) const { // Start from the static part first: - const auto &table = staticTable(); + const auto &table = staticPart(); const HeaderField field(name, QByteArray()); - const auto staticPos = std::lower_bound(table.begin(), table.end(), field, - [](const HeaderField &lhs, const HeaderField &rhs) { - return compare(lhs.name, rhs.name) < 0; - }); + const auto staticPos = findInStaticPart(field, CompareMode::nameOnly); if (staticPos != table.end()) { if (staticPos->name == name) - return staticPos - table.begin() + 1; + return quint32(staticPos - table.begin() + 1); } // Now we have to lookup in our dynamic part ... @@ -402,7 +318,7 @@ bool FieldLookupTable::field(quint32 index, QByteArray *name, QByteArray *value) if (!indexIsValid(index)) return false; - const auto &table = staticTable(); + const auto &table = staticPart(); if (index - 1 < table.size()) { *name = table[index - 1].name; *value = table[index - 1].value; @@ -477,7 +393,7 @@ quint32 FieldLookupTable::keyToIndex(const SearchEntry &key) const Q_ASSERT(offset < ChunkSize); Q_ASSERT(chunkIndex || offset >= begin); - return quint32(offset + chunkIndex * ChunkSize - begin + 1 + staticTable().size()); + return quint32(offset + chunkIndex * ChunkSize - begin + 1 + staticPart().size()); } FieldLookupTable::SearchEntry FieldLookupTable::frontKey() const @@ -526,6 +442,103 @@ void FieldLookupTable::setMaxDynamicTableSize(quint32 size) updateDynamicTableSize(size); } +// This data is from the HPACK's specs and it's quite conveniently sorted, +// except ... 'accept' is in the wrong position, see how we handle it below. +const std::vector<HeaderField> &FieldLookupTable::staticPart() +{ + static std::vector<HeaderField> table = { + {":authority", ""}, + {":method", "GET"}, + {":method", "POST"}, + {":path", "/"}, + {":path", "/index.html"}, + {":scheme", "http"}, + {":scheme", "https"}, + {":status", "200"}, + {":status", "204"}, + {":status", "206"}, + {":status", "304"}, + {":status", "400"}, + {":status", "404"}, + {":status", "500"}, + {"accept-charset", ""}, + {"accept-encoding", "gzip, deflate"}, + {"accept-language", ""}, + {"accept-ranges", ""}, + {"accept", ""}, + {"access-control-allow-origin", ""}, + {"age", ""}, + {"allow", ""}, + {"authorization", ""}, + {"cache-control", ""}, + {"content-disposition", ""}, + {"content-encoding", ""}, + {"content-language", ""}, + {"content-length", ""}, + {"content-location", ""}, + {"content-range", ""}, + {"content-type", ""}, + {"cookie", ""}, + {"date", ""}, + {"etag", ""}, + {"expect", ""}, + {"expires", ""}, + {"from", ""}, + {"host", ""}, + {"if-match", ""}, + {"if-modified-since", ""}, + {"if-none-match", ""}, + {"if-range", ""}, + {"if-unmodified-since", ""}, + {"last-modified", ""}, + {"link", ""}, + {"location", ""}, + {"max-forwards", ""}, + {"proxy-authenticate", ""}, + {"proxy-authorization", ""}, + {"range", ""}, + {"referer", ""}, + {"refresh", ""}, + {"retry-after", ""}, + {"server", ""}, + {"set-cookie", ""}, + {"strict-transport-security", ""}, + {"transfer-encoding", ""}, + {"user-agent", ""}, + {"vary", ""}, + {"via", ""}, + {"www-authenticate", ""} + }; + + return table; +} + +std::vector<HeaderField>::const_iterator FieldLookupTable::findInStaticPart(const HeaderField &field, CompareMode mode) +{ + const auto &table = staticPart(); + const auto acceptPos = table.begin() + 18; + if (field.name == "accept") { + if (mode == CompareMode::nameAndValue && field.value != "") + return table.end(); + return acceptPos; + } + + auto predicate = [mode](const HeaderField &lhs, const HeaderField &rhs) { + const int cmp = compare(lhs.name, rhs.name); + if (cmp) + return cmp < 0; + else if (mode == CompareMode::nameAndValue) + return compare(lhs.value, rhs.value) < 0; + return false; + }; + + const auto staticPos = std::lower_bound(table.begin(), acceptPos, field, predicate); + if (staticPos != acceptPos) + return staticPos; + + return std::lower_bound(acceptPos + 1, table.end(), field, predicate); +} + } QT_END_NAMESPACE diff --git a/src/network/access/http2/hpacktable_p.h b/src/network/access/http2/hpacktable_p.h index aaea89b986..587d86f09c 100644 --- a/src/network/access/http2/hpacktable_p.h +++ b/src/network/access/http2/hpacktable_p.h @@ -173,6 +173,8 @@ public: bool updateDynamicTableSize(quint32 size); void setMaxDynamicTableSize(quint32 size); + static const std::vector<HeaderField> &staticPart(); + private: // Table's maximum size is controlled // by SETTINGS_HEADER_TABLE_SIZE (HTTP/2, 6.5.2). @@ -225,9 +227,16 @@ private: quint32 indexOfChunk(const Chunk *chunk) const; quint32 keyToIndex(const SearchEntry &key) const; + enum class CompareMode { + nameOnly, + nameAndValue + }; + + static std::vector<HeaderField>::const_iterator findInStaticPart(const HeaderField &field, CompareMode mode); + mutable QByteArray dummyDst; - Q_DISABLE_COPY(FieldLookupTable); + Q_DISABLE_COPY_MOVE(FieldLookupTable) }; } diff --git a/src/network/access/qabstractnetworkcache.cpp b/src/network/access/qabstractnetworkcache.cpp index 2b670b2cce..4e217294c4 100644 --- a/src/network/access/qabstractnetworkcache.cpp +++ b/src/network/access/qabstractnetworkcache.cpp @@ -191,8 +191,8 @@ bool QNetworkCacheMetaData::isValid() const Some cache implementations can keep these cache items in memory for performance reasons, but for security reasons they should not be written to disk. - Specifically with http, documents marked with Pragma: no-cache, or have a Cache-control set to - no-store or no-cache or any https document that doesn't have "Cache-control: public" set will + Specifically with http, documents with Cache-control set to no-store or any + https document that doesn't have "Cache-control: public" set will set the saveToDisk to false. \sa setSaveToDisk() @@ -331,11 +331,11 @@ QDataStream &operator<<(QDataStream &out, const QNetworkCacheMetaData &metaData) static inline QDataStream &operator<<(QDataStream &out, const QNetworkCacheMetaData::AttributesMap &hash) { out << quint32(hash.size()); - QNetworkCacheMetaData::AttributesMap::ConstIterator it = hash.end(); - QNetworkCacheMetaData::AttributesMap::ConstIterator begin = hash.begin(); - while (it != begin) { - --it; + QNetworkCacheMetaData::AttributesMap::ConstIterator it = hash.begin(); + QNetworkCacheMetaData::AttributesMap::ConstIterator end = hash.end(); + while (it != end) { out << int(it.key()) << it.value(); + ++it; } return out; } @@ -383,7 +383,7 @@ static inline QDataStream &operator>>(QDataStream &in, QNetworkCacheMetaData::At int k; QVariant t; in >> k >> t; - hash.insertMulti(QNetworkRequest::Attribute(k), t); + hash.insert(QNetworkRequest::Attribute(k), t); } if (in.status() != QDataStream::Ok) @@ -475,7 +475,7 @@ QAbstractNetworkCache::~QAbstractNetworkCache() the QIODevice when done with it. If there is no cache for \a url, the url is invalid, or if there - is an internal cache error 0 is returned. + is an internal cache error \nullptr is returned. In the base class this is a pure virtual function. @@ -496,7 +496,7 @@ QAbstractNetworkCache::~QAbstractNetworkCache() Returns the device that should be populated with the data for the cache item \a metaData. When all of the data has been written insert() should be called. If metaData is invalid or the url in - the metadata is invalid 0 is returned. + the metadata is invalid \nullptr is returned. The cache owns the device and will take care of deleting it when it is inserted or removed. diff --git a/src/network/access/qabstractnetworkcache.h b/src/network/access/qabstractnetworkcache.h index 678bae2d6e..b604323c41 100644 --- a/src/network/access/qabstractnetworkcache.h +++ b/src/network/access/qabstractnetworkcache.h @@ -68,11 +68,11 @@ public: ~QNetworkCacheMetaData(); #ifdef Q_COMPILER_RVALUE_REFS - QNetworkCacheMetaData &operator=(QNetworkCacheMetaData &&other) Q_DECL_NOTHROW { swap(other); return *this; } + QNetworkCacheMetaData &operator=(QNetworkCacheMetaData &&other) noexcept { swap(other); return *this; } #endif QNetworkCacheMetaData &operator=(const QNetworkCacheMetaData &other); - void swap(QNetworkCacheMetaData &other) Q_DECL_NOTHROW + void swap(QNetworkCacheMetaData &other) noexcept { qSwap(d, other.d); } bool operator==(const QNetworkCacheMetaData &other) const; diff --git a/src/network/access/qftp.cpp b/src/network/access/qftp.cpp index feece4ebaf..4e399f018f 100644 --- a/src/network/access/qftp.cpp +++ b/src/network/access/qftp.cpp @@ -1826,8 +1826,8 @@ int QFtp::cd(const QString &dir) is data available to read. You can then read the data with the read() or readAll() functions. - If \a dev is not 0, the data is written directly to the device \a - dev. Make sure that the \a dev pointer is valid for the duration + If \a dev is not \nullptr, the data is written directly to the device + \a dev. Make sure that the \a dev pointer is valid for the duration of the operation (it is safe to delete it when the commandFinished() signal is emitted). In this case the readyRead() signal is \e not emitted and you cannot read data with the @@ -2154,7 +2154,7 @@ QFtp::Command QFtp::currentCommand() const \internal Returns the QIODevice pointer that is used by the FTP command to read data from or store data to. If there is no current FTP command being executed or - if the command does not use an IO device, this function returns 0. + if the command does not use an IO device, this function returns \nullptr. This function can be used to delete the QIODevice in the slot connected to the commandFinished() signal. diff --git a/src/network/access/qftp_p.h b/src/network/access/qftp_p.h index bba1f9b09d..91d78d1351 100644 --- a/src/network/access/qftp_p.h +++ b/src/network/access/qftp_p.h @@ -67,7 +67,7 @@ class Q_AUTOTEST_EXPORT QFtp : public QObject Q_OBJECT public: - explicit QFtp(QObject *parent = 0); + explicit QFtp(QObject *parent = nullptr); virtual ~QFtp(); enum State { @@ -118,7 +118,7 @@ public: int setTransferMode(TransferMode mode); int list(const QString &dir = QString()); int cd(const QString &dir); - int get(const QString &file, QIODevice *dev=0, TransferType type = Binary); + int get(const QString &file, QIODevice *dev=nullptr, TransferType type = Binary); int put(const QByteArray &data, const QString &file, TransferType type = Binary); int put(QIODevice *dev, const QString &file, TransferType type = Binary); int remove(const QString &file); @@ -158,7 +158,7 @@ Q_SIGNALS: void done(bool); private: - Q_DISABLE_COPY(QFtp) + Q_DISABLE_COPY_MOVE(QFtp) Q_DECLARE_PRIVATE(QFtp) Q_PRIVATE_SLOT(d_func(), void _q_startNextCommand()) diff --git a/src/network/access/qhstspolicy.h b/src/network/access/qhstspolicy.h index 176a8fa635..f1b2ee99e5 100644 --- a/src/network/access/qhstspolicy.h +++ b/src/network/access/qhstspolicy.h @@ -65,10 +65,10 @@ public: QUrl::ParsingMode mode = QUrl::DecodedMode); QHstsPolicy(const QHstsPolicy &rhs); QHstsPolicy &operator=(const QHstsPolicy &rhs); - QHstsPolicy &operator=(QHstsPolicy &&other) Q_DECL_NOTHROW { swap(other); return *this; } + QHstsPolicy &operator=(QHstsPolicy &&other) noexcept { swap(other); return *this; } ~QHstsPolicy(); - void swap(QHstsPolicy &other) Q_DECL_NOTHROW { qSwap(d, other.d); } + void swap(QHstsPolicy &other) noexcept { qSwap(d, other.d); } void setHost(const QString &host, QUrl::ParsingMode mode = QUrl::DecodedMode); QString host(QUrl::ComponentFormattingOptions options = QUrl::FullyDecoded) const; diff --git a/src/network/access/qhstsstore_p.h b/src/network/access/qhstsstore_p.h index e82596b250..5338d15592 100644 --- a/src/network/access/qhstsstore_p.h +++ b/src/network/access/qhstsstore_p.h @@ -87,7 +87,7 @@ private: QVector<QHstsPolicy> observedPolicies; QSettings store; - Q_DISABLE_COPY(QHstsStore) + Q_DISABLE_COPY_MOVE(QHstsStore) }; QT_END_NAMESPACE diff --git a/src/network/access/qhttp2protocolhandler.cpp b/src/network/access/qhttp2protocolhandler.cpp index df7f87efd4..35aee6e3e1 100644 --- a/src/network/access/qhttp2protocolhandler.cpp +++ b/src/network/access/qhttp2protocolhandler.cpp @@ -198,7 +198,7 @@ QHttp2ProtocolHandler::QHttp2ProtocolHandler(QHttpNetworkConnectionChannel *chan } } - if (!channel->ssl) { + if (!channel->ssl && m_connection->connectionType() != QHttpNetworkConnection::ConnectionTypeHTTP2Direct) { // We upgraded from HTTP/1.1 to HTTP/2. channel->request was already sent // as HTTP/1.1 request. The response with status code 101 triggered // protocol switch and now we are waiting for the real response, sent @@ -1056,6 +1056,7 @@ void QHttp2ProtocolHandler::updateStream(Stream &stream, const HPack::HttpHeader Qt::ConnectionType connectionType) { const auto httpReply = stream.reply(); + const auto &httpRequest = stream.request(); Q_ASSERT(httpReply || stream.state == Stream::remoteReserved); if (!httpReply) { @@ -1115,6 +1116,9 @@ void QHttp2ProtocolHandler::updateStream(Stream &stream, const HPack::HttpHeader if (QHttpNetworkReply::isHttpRedirect(statusCode) && redirectUrl.isValid()) httpReply->setRedirectUrl(redirectUrl); + if (httpReplyPrivate->isCompressed() && httpRequest.d->autoDecompress) + httpReplyPrivate->removeAutoDecompressHeader(); + if (QHttpNetworkReply::isHttpRedirect(statusCode) || statusCode == 401 || statusCode == 407) { // These are the status codes that can trigger uploadByteDevice->reset() diff --git a/src/network/access/qhttpmultipart.h b/src/network/access/qhttpmultipart.h index 78585a704d..f718d51d0c 100644 --- a/src/network/access/qhttpmultipart.h +++ b/src/network/access/qhttpmultipart.h @@ -61,11 +61,11 @@ public: QHttpPart(const QHttpPart &other); ~QHttpPart(); #ifdef Q_COMPILER_RVALUE_REFS - QHttpPart &operator=(QHttpPart &&other) Q_DECL_NOTHROW { swap(other); return *this; } + QHttpPart &operator=(QHttpPart &&other) noexcept { swap(other); return *this; } #endif QHttpPart &operator=(const QHttpPart &other); - void swap(QHttpPart &other) Q_DECL_NOTHROW { qSwap(d, other.d); } + void swap(QHttpPart &other) noexcept { qSwap(d, other.d); } bool operator==(const QHttpPart &other) const; inline bool operator!=(const QHttpPart &other) const diff --git a/src/network/access/qhttpmultipart_p.h b/src/network/access/qhttpmultipart_p.h index 363e0b346c..ead1eadf3b 100644 --- a/src/network/access/qhttpmultipart_p.h +++ b/src/network/access/qhttpmultipart_p.h @@ -64,7 +64,7 @@ QT_BEGIN_NAMESPACE class QHttpPartPrivate: public QSharedData, public QNetworkHeadersPrivate { public: - inline QHttpPartPrivate() : bodyDevice(0), headerCreated(false), readPointer(0) + inline QHttpPartPrivate() : bodyDevice(nullptr), headerCreated(false), readPointer(0) { } ~QHttpPartPrivate() diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp index c58fd24a44..0a37122fc6 100644 --- a/src/network/access/qhttpnetworkconnection.cpp +++ b/src/network/access/qhttpnetworkconnection.cpp @@ -398,11 +398,12 @@ void QHttpNetworkConnectionPrivate::copyCredentials(int fromChannel, QAuthentica { Q_ASSERT(auth); - // NTLM is a multi phase authentication. Copying credentials between authenticators would mess things up. + // NTLM and Negotiate do multi-phase authentication. + // Copying credentialsbetween authenticators would mess things up. if (fromChannel >= 0) { - if (!isProxy && channels[fromChannel].authMethod == QAuthenticatorPrivate::Ntlm) - return; - if (isProxy && channels[fromChannel].proxyAuthMethod == QAuthenticatorPrivate::Ntlm) + const QHttpNetworkConnectionChannel &channel = channels[fromChannel]; + const QAuthenticatorPrivate::Method method = isProxy ? channel.proxyAuthMethod : channel.authMethod; + if (method == QAuthenticatorPrivate::Ntlm || method == QAuthenticatorPrivate::Negotiate) return; } @@ -592,7 +593,7 @@ void QHttpNetworkConnectionPrivate::createAuthorization(QAbstractSocket *socket, if ((channels[i].authMethod != QAuthenticatorPrivate::Ntlm && request.headerField("Authorization").isEmpty()) || channels[i].lastStatus == 401) { QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(channels[i].authenticator); if (priv && priv->method != QAuthenticatorPrivate::None) { - QByteArray response = priv->calculateResponse(request.methodName(), request.uri(false)); + QByteArray response = priv->calculateResponse(request.methodName(), request.uri(false), request.url().host()); request.setHeaderField("Authorization", response); channels[i].authenticationCredentialsSent = true; } @@ -604,7 +605,7 @@ void QHttpNetworkConnectionPrivate::createAuthorization(QAbstractSocket *socket, if (!(channels[i].proxyAuthMethod == QAuthenticatorPrivate::Ntlm && channels[i].lastStatus != 407)) { QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(channels[i].proxyAuthenticator); if (priv && priv->method != QAuthenticatorPrivate::None) { - QByteArray response = priv->calculateResponse(request.methodName(), request.uri(false)); + QByteArray response = priv->calculateResponse(request.methodName(), request.uri(false), networkProxy.hostName()); request.setHeaderField("Proxy-Authorization", response); channels[i].proxyCredentialsSent = true; } @@ -641,7 +642,7 @@ QHttpNetworkReply* QHttpNetworkConnectionPrivate::queueRequest(const QHttpNetwor else { // SPDY, HTTP/2 ('h2' mode) if (!pair.second->d_func()->requestIsPrepared) prepareRequest(pair); - channels[0].spdyRequestsToSend.insertMulti(request.priority(), pair); + channels[0].spdyRequestsToSend.insert(request.priority(), pair); } #ifndef Q_OS_WINRT @@ -677,7 +678,7 @@ void QHttpNetworkConnectionPrivate::fillHttp2Queue() for (auto &pair : highPriorityQueue) { if (!pair.second->d_func()->requestIsPrepared) prepareRequest(pair); - channels[0].spdyRequestsToSend.insertMulti(QHttpNetworkRequest::HighPriority, pair); + channels[0].spdyRequestsToSend.insert(QHttpNetworkRequest::HighPriority, pair); } highPriorityQueue.clear(); @@ -685,7 +686,7 @@ void QHttpNetworkConnectionPrivate::fillHttp2Queue() for (auto &pair : lowPriorityQueue) { if (!pair.second->d_func()->requestIsPrepared) prepareRequest(pair); - channels[0].spdyRequestsToSend.insertMulti(pair.first.priority(), pair); + channels[0].spdyRequestsToSend.insert(pair.first.priority(), pair); } lowPriorityQueue.clear(); @@ -1317,7 +1318,7 @@ QHttpNetworkConnection::QHttpNetworkConnection(const QString &hostName, quint16 : QObject(*(new QHttpNetworkConnectionPrivate(hostName, port, encrypt, connectionType)), parent) { Q_D(QHttpNetworkConnection); - d->networkSession = qMove(networkSession); + d->networkSession = std::move(networkSession); d->init(); } @@ -1329,7 +1330,7 @@ QHttpNetworkConnection::QHttpNetworkConnection(quint16 connectionCount, const QS connectionType)), parent) { Q_D(QHttpNetworkConnection); - d->networkSession = qMove(networkSession); + d->networkSession = std::move(networkSession); d->init(); } #else @@ -1476,7 +1477,7 @@ QSharedPointer<QSslContext> QHttpNetworkConnection::sslContext() void QHttpNetworkConnection::setSslContext(QSharedPointer<QSslContext> context) { Q_D(QHttpNetworkConnection); - d->sslContext = qMove(context); + d->sslContext = std::move(context); } void QHttpNetworkConnection::ignoreSslErrors(int channel) @@ -1518,6 +1519,18 @@ void QHttpNetworkConnection::preConnectFinished() d_func()->preConnectRequests--; } +QString QHttpNetworkConnection::peerVerifyName() const +{ + Q_D(const QHttpNetworkConnection); + return d->peerVerifyName; +} + +void QHttpNetworkConnection::setPeerVerifyName(const QString &peerName) +{ + Q_D(QHttpNetworkConnection); + d->peerVerifyName = peerName; +} + #ifndef QT_NO_NETWORKPROXY // only called from QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired, not // from QHttpNetworkConnectionChannel::handleAuthenticationChallenge diff --git a/src/network/access/qhttpnetworkconnection_p.h b/src/network/access/qhttpnetworkconnection_p.h index 91827a6eb1..2f3c334248 100644 --- a/src/network/access/qhttpnetworkconnection_p.h +++ b/src/network/access/qhttpnetworkconnection_p.h @@ -101,10 +101,10 @@ public: #ifndef QT_NO_BEARERMANAGEMENT explicit QHttpNetworkConnection(const QString &hostName, quint16 port = 80, bool encrypt = false, ConnectionType connectionType = ConnectionTypeHTTP, - QObject *parent = 0, QSharedPointer<QNetworkSession> networkSession + QObject *parent = nullptr, QSharedPointer<QNetworkSession> networkSession = QSharedPointer<QNetworkSession>()); QHttpNetworkConnection(quint16 channelCount, const QString &hostName, quint16 port = 80, - bool encrypt = false, QObject *parent = 0, + bool encrypt = false, QObject *parent = nullptr, QSharedPointer<QNetworkSession> networkSession = QSharedPointer<QNetworkSession>(), ConnectionType connectionType = ConnectionTypeHTTP); #else @@ -154,9 +154,11 @@ public: void preConnectFinished(); + QString peerVerifyName() const; + void setPeerVerifyName(const QString &peerName); private: Q_DECLARE_PRIVATE(QHttpNetworkConnection) - Q_DISABLE_COPY(QHttpNetworkConnection) + Q_DISABLE_COPY_MOVE(QHttpNetworkConnection) friend class QHttpThreadDelegate; friend class QHttpNetworkReply; friend class QHttpNetworkReplyPrivate; @@ -289,6 +291,8 @@ public: Http2::ProtocolParameters http2Parameters; + QString peerVerifyName; + friend class QHttpNetworkConnectionChannel; }; diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp index 5726925cb0..f79a4d1dc6 100644 --- a/src/network/access/qhttpnetworkconnectionchannel.cpp +++ b/src/network/access/qhttpnetworkconnectionchannel.cpp @@ -392,6 +392,7 @@ bool QHttpNetworkConnectionChannel::ensureConnection() if (!connection->sslContext().isNull()) QSslSocketPrivate::checkSettingSslContext(sslSocket, connection->sslContext()); + sslSocket->setPeerVerifyName(connection->d_func()->peerVerifyName); sslSocket->connectToHostEncrypted(connectHost, connectPort, QIODevice::ReadWrite, networkLayerPreference); if (ignoreAllSslErrors) sslSocket->ignoreSslErrors(); @@ -966,7 +967,10 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket } else if (state != QHttpNetworkConnectionChannel::IdleState && state != QHttpNetworkConnectionChannel::ReadingState) { // Try to reconnect/resend before sending an error. // While "Reading" the _q_disconnected() will handle this. - if (reconnectAttempts-- > 0) { + // If we're using ssl then the protocolHandler is not initialized until + // "encrypted" has been emitted, since retrying requires the protocolHandler (asserted) + // we will not try if encryption is not done. + if (!pendingEncrypt && reconnectAttempts-- > 0) { resendCurrentRequest(); return; } else { diff --git a/src/network/access/qhttpnetworkreply.cpp b/src/network/access/qhttpnetworkreply.cpp index c9c3172304..a8b635c45a 100644 --- a/src/network/access/qhttpnetworkreply.cpp +++ b/src/network/access/qhttpnetworkreply.cpp @@ -444,6 +444,9 @@ QAuthenticatorPrivate::Method QHttpNetworkReplyPrivate::authenticationMethod(boo } else if (method < QAuthenticatorPrivate::DigestMd5 && line.startsWith("digest")) { method = QAuthenticatorPrivate::DigestMd5; + } else if (method < QAuthenticatorPrivate::Negotiate + && line.startsWith("negotiate")) { + method = QAuthenticatorPrivate::Negotiate; } } return method; diff --git a/src/network/access/qhttpnetworkreply_p.h b/src/network/access/qhttpnetworkreply_p.h index 863e21ea3e..12cfe359aa 100644 --- a/src/network/access/qhttpnetworkreply_p.h +++ b/src/network/access/qhttpnetworkreply_p.h @@ -89,7 +89,7 @@ class Q_AUTOTEST_EXPORT QHttpNetworkReply : public QObject, public QHttpNetworkH Q_OBJECT public: - explicit QHttpNetworkReply(const QUrl &url = QUrl(), QObject *parent = 0); + explicit QHttpNetworkReply(const QUrl &url = QUrl(), QObject *parent = nullptr); virtual ~QHttpNetworkReply(); QUrl url() const override; diff --git a/src/network/access/qhttpnetworkrequest.cpp b/src/network/access/qhttpnetworkrequest.cpp index 8de9760710..a3f71b8d2f 100644 --- a/src/network/access/qhttpnetworkrequest.cpp +++ b/src/network/access/qhttpnetworkrequest.cpp @@ -66,7 +66,8 @@ QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(const QHttpNetworkRequest ssl(other.ssl), preConnect(other.preConnect), redirectCount(other.redirectCount), - redirectPolicy(other.redirectPolicy) + redirectPolicy(other.redirectPolicy), + peerVerifyName(other.peerVerifyName) { } @@ -90,7 +91,8 @@ bool QHttpNetworkRequestPrivate::operator==(const QHttpNetworkRequestPrivate &ot && (withCredentials == other.withCredentials) && (ssl == other.ssl) && (preConnect == other.preConnect) - && (redirectPolicy == other.redirectPolicy); + && (redirectPolicy == other.redirectPolicy) + && (peerVerifyName == other.peerVerifyName); } QByteArray QHttpNetworkRequest::methodName() const @@ -397,6 +399,15 @@ int QHttpNetworkRequest::minorVersion() const return 1; } +QString QHttpNetworkRequest::peerVerifyName() const +{ + return d->peerVerifyName; +} + +void QHttpNetworkRequest::setPeerVerifyName(const QString &peerName) +{ + d->peerVerifyName = peerName; +} QT_END_NAMESPACE diff --git a/src/network/access/qhttpnetworkrequest_p.h b/src/network/access/qhttpnetworkrequest_p.h index bc797537ae..fb4896195b 100644 --- a/src/network/access/qhttpnetworkrequest_p.h +++ b/src/network/access/qhttpnetworkrequest_p.h @@ -147,6 +147,8 @@ public: QByteArray methodName() const; QByteArray uri(bool throughProxy) const; + QString peerVerifyName() const; + void setPeerVerifyName(const QString &peerName); private: QSharedDataPointer<QHttpNetworkRequestPrivate> d; friend class QHttpNetworkRequestPrivate; @@ -182,6 +184,7 @@ public: bool preConnect; int redirectCount; QNetworkRequest::RedirectPolicy redirectPolicy; + QString peerVerifyName; }; diff --git a/src/network/access/qhttpthreaddelegate.cpp b/src/network/access/qhttpthreaddelegate.cpp index 0e97acdd9d..6fb4710d77 100644 --- a/src/network/access/qhttpthreaddelegate.cpp +++ b/src/network/access/qhttpthreaddelegate.cpp @@ -123,7 +123,7 @@ static QNetworkReply::NetworkError statusCodeFromHttp(int httpStatusCode, const } -static QByteArray makeCacheKey(QUrl &url, QNetworkProxy *proxy) +static QByteArray makeCacheKey(QUrl &url, QNetworkProxy *proxy, const QString &peerVerifyName) { QString result; QUrl copy = url; @@ -170,7 +170,8 @@ static QByteArray makeCacheKey(QUrl &url, QNetworkProxy *proxy) #else Q_UNUSED(proxy) #endif - + if (!peerVerifyName.isEmpty()) + result += QLatin1Char(':') + peerVerifyName; return "http-connection:" + std::move(result).toLatin1(); } @@ -188,7 +189,7 @@ public: QHttpNetworkConnection::ConnectionType connectionType, QSharedPointer<QNetworkSession> networkSession) : QHttpNetworkConnection(hostName, port, encrypt, connectionType, /*parent=*/0, - qMove(networkSession)) + std::move(networkSession)) #endif { setExpires(true); @@ -317,12 +318,12 @@ void QHttpThreadDelegate::startRequest() #ifndef QT_NO_NETWORKPROXY if (transparentProxy.type() != QNetworkProxy::NoProxy) - cacheKey = makeCacheKey(urlCopy, &transparentProxy); + cacheKey = makeCacheKey(urlCopy, &transparentProxy, httpRequest.peerVerifyName()); else if (cacheProxy.type() != QNetworkProxy::NoProxy) - cacheKey = makeCacheKey(urlCopy, &cacheProxy); + cacheKey = makeCacheKey(urlCopy, &cacheProxy, httpRequest.peerVerifyName()); else #endif - cacheKey = makeCacheKey(urlCopy, 0); + cacheKey = makeCacheKey(urlCopy, 0, httpRequest.peerVerifyName()); // the http object is actually a QHttpNetworkConnection @@ -352,7 +353,7 @@ void QHttpThreadDelegate::startRequest() httpConnection->setTransparentProxy(transparentProxy); httpConnection->setCacheProxy(cacheProxy); #endif - + httpConnection->setPeerVerifyName(httpRequest.peerVerifyName()); // cache the QHttpNetworkConnection corresponding to this cache key connections.localData()->addEntry(cacheKey, httpConnection); } else { diff --git a/src/network/access/qhttpthreaddelegate_p.h b/src/network/access/qhttpthreaddelegate_p.h index 019a8b8b74..6184b39b30 100644 --- a/src/network/access/qhttpthreaddelegate_p.h +++ b/src/network/access/qhttpthreaddelegate_p.h @@ -82,7 +82,7 @@ class QHttpThreadDelegate : public QObject { Q_OBJECT public: - explicit QHttpThreadDelegate(QObject *parent = 0); + explicit QHttpThreadDelegate(QObject *parent = nullptr); ~QHttpThreadDelegate(); @@ -207,7 +207,7 @@ public: : QNonContiguousByteDevice(), wantDataPending(false), m_amount(0), - m_data(0), + m_data(nullptr), m_atEnd(aE), m_size(s), m_pos(0) @@ -240,12 +240,12 @@ public: // Do nothing, we already sent a wantData signal and wait for results len = 0; } - return 0; + return nullptr; } bool advanceReadPointer(qint64 a) override { - if (m_data == 0) + if (m_data == nullptr) return false; m_amount -= a; @@ -269,7 +269,7 @@ public: bool reset() override { m_amount = 0; - m_data = 0; + m_data = nullptr; m_dataArray.clear(); if (wantDataPending) { diff --git a/src/network/access/qnetworkaccessauthenticationmanager_p.h b/src/network/access/qnetworkaccessauthenticationmanager_p.h index 548675728f..31111ca2a5 100644 --- a/src/network/access/qnetworkaccessauthenticationmanager_p.h +++ b/src/network/access/qnetworkaccessauthenticationmanager_p.h @@ -90,12 +90,12 @@ public: void cacheCredentials(const QUrl &url, const QAuthenticator *auth); QNetworkAuthenticationCredential fetchCachedCredentials(const QUrl &url, - const QAuthenticator *auth = 0); + const QAuthenticator *auth = nullptr); #ifndef QT_NO_NETWORKPROXY void cacheProxyCredentials(const QNetworkProxy &proxy, const QAuthenticator *auth); QNetworkAuthenticationCredential fetchCachedProxyCredentials(const QNetworkProxy &proxy, - const QAuthenticator *auth = 0); + const QAuthenticator *auth = nullptr); #endif void clearCache(); diff --git a/src/network/access/qnetworkaccesscachebackend.cpp b/src/network/access/qnetworkaccesscachebackend.cpp index 0c9a88596d..22fdc5bb0b 100644 --- a/src/network/access/qnetworkaccesscachebackend.cpp +++ b/src/network/access/qnetworkaccesscachebackend.cpp @@ -87,15 +87,16 @@ bool QNetworkAccessCacheBackend::sendCacheContents() setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, attributes.value(QNetworkRequest::HttpReasonPhraseAttribute)); // set the raw headers - QNetworkCacheMetaData::RawHeaderList rawHeaders = item.rawHeaders(); - QNetworkCacheMetaData::RawHeaderList::ConstIterator it = rawHeaders.constBegin(), - end = rawHeaders.constEnd(); - for ( ; it != end; ++it) { - if (it->first.toLower() == "cache-control" && - it->second.toLower().contains("must-revalidate")) { - return false; + const QNetworkCacheMetaData::RawHeaderList rawHeaders = item.rawHeaders(); + for (const auto &header : rawHeaders) { + if (header.first.toLower() == "cache-control") { + const QByteArray cacheControlValue = header.second.toLower(); + if (cacheControlValue.contains("must-revalidate") + || cacheControlValue.contains("no-cache")) { + return false; + } } - setRawHeader(it->first, it->second); + setRawHeader(header.first, header.second); } // handle a possible redirect diff --git a/src/network/access/qnetworkaccessftpbackend.cpp b/src/network/access/qnetworkaccessftpbackend.cpp index fd6589b396..5ad820eba0 100644 --- a/src/network/access/qnetworkaccessftpbackend.cpp +++ b/src/network/access/qnetworkaccessftpbackend.cpp @@ -351,7 +351,7 @@ void QNetworkAccessFtpBackend::ftpDone() } } else if (state == Statting) { // statted successfully, send the actual request - emit metaDataChanged(); + metaDataChanged(); state = Transferring; QFtp::TransferType type = QFtp::Binary; diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp index 263469ce38..50b9488594 100644 --- a/src/network/access/qnetworkaccessmanager.cpp +++ b/src/network/access/qnetworkaccessmanager.cpp @@ -494,8 +494,8 @@ QNetworkAccessManager::QNetworkAccessManager(QObject *parent) // connect(&d->networkConfigurationManager, SIGNAL(onlineStateChanged(bool)), SLOT(_q_onlineStateChanged(bool))); - connect(&d->networkConfigurationManager, SIGNAL(configurationChanged(const QNetworkConfiguration &)), - SLOT(_q_configurationChanged(const QNetworkConfiguration &))); + connect(&d->networkConfigurationManager, SIGNAL(configurationChanged(QNetworkConfiguration)), + SLOT(_q_configurationChanged(QNetworkConfiguration))); #endif } @@ -1181,9 +1181,37 @@ QSharedPointer<QNetworkSession> QNetworkAccessManagerPrivate::getNetworkSession( \sa connectToHost(), get(), post(), put(), deleteResource() */ + void QNetworkAccessManager::connectToHostEncrypted(const QString &hostName, quint16 port, const QSslConfiguration &sslConfiguration) { + connectToHostEncrypted(hostName, port, sslConfiguration, QString()); +} + +/*! + \since 5.13 + \overload + + Initiates a connection to the host given by \a hostName at port \a port, using + \a sslConfiguration with \a peerName set to be the hostName used for certificate + validation. This function is useful to complete the TCP and SSL handshake + to a host before the HTTPS request is made, resulting in a lower network latency. + + \note Preconnecting a SPDY connection can be done by calling setAllowedNextProtocols() + on \a sslConfiguration with QSslConfiguration::NextProtocolSpdy3_0 contained in + the list of allowed protocols. When using SPDY, one single connection per host is + enough, i.e. calling this method multiple times per host will not result in faster + network transactions. + + \note This function has no possibility to report errors. + + \sa connectToHost(), get(), post(), put(), deleteResource() +*/ + +void QNetworkAccessManager::connectToHostEncrypted(const QString &hostName, quint16 port, + const QSslConfiguration &sslConfiguration, + const QString &peerName) +{ QUrl url; url.setHost(hostName); url.setPort(port); @@ -1198,6 +1226,7 @@ void QNetworkAccessManager::connectToHostEncrypted(const QString &hostName, quin QSslConfiguration::NextProtocolSpdy3_0)) request.setAttribute(QNetworkRequest::SpdyAllowedAttribute, true); + request.setPeerVerifyName(peerName); get(request); } #endif @@ -1361,7 +1390,8 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera QString scheme = req.url().scheme(); #ifdef Q_OS_WASM - if (scheme == QLatin1String("http") || scheme == QLatin1String("https")) { + // Support http, https, and relateive urls + if (scheme == QLatin1String("http") || scheme == QLatin1String("https") || scheme.isEmpty()) { QNetworkReplyWasmImpl *reply = new QNetworkReplyWasmImpl(this); QNetworkReplyWasmImplPrivate *priv = reply->d_func(); priv->manager = this; @@ -1848,6 +1878,7 @@ void QNetworkAccessManagerPrivate::createSession(const QNetworkConfiguration &co if (config.isValid()) newSession = QSharedNetworkSessionManager::getSession(config); + QNetworkSession::State oldState = QNetworkSession::Invalid; if (networkSessionStrongRef) { //do nothing if new and old session are the same if (networkSessionStrongRef == newSession) @@ -1859,6 +1890,7 @@ void QNetworkAccessManagerPrivate::createSession(const QNetworkConfiguration &co q, SLOT(_q_networkSessionStateChanged(QNetworkSession::State))); QObject::disconnect(networkSessionStrongRef.data(), SIGNAL(error(QNetworkSession::SessionError)), q, SLOT(_q_networkSessionFailed(QNetworkSession::SessionError))); + oldState = networkSessionStrongRef->state(); } //switch to new session (null if config was invalid) @@ -1884,7 +1916,11 @@ void QNetworkAccessManagerPrivate::createSession(const QNetworkConfiguration &co QObject::connect(networkSessionStrongRef.data(), SIGNAL(error(QNetworkSession::SessionError)), q, SLOT(_q_networkSessionFailed(QNetworkSession::SessionError))); - _q_networkSessionStateChanged(networkSessionStrongRef->state()); + const QNetworkSession::State newState = networkSessionStrongRef->state(); + if (newState != oldState) { + QMetaObject::invokeMethod(q, "_q_networkSessionStateChanged", Qt::QueuedConnection, + Q_ARG(QNetworkSession::State, newState)); + } } void QNetworkAccessManagerPrivate::_q_networkSessionClosed() diff --git a/src/network/access/qnetworkaccessmanager.h b/src/network/access/qnetworkaccessmanager.h index 67b3a8b71b..7e2f7683d0 100644 --- a/src/network/access/qnetworkaccessmanager.h +++ b/src/network/access/qnetworkaccessmanager.h @@ -158,6 +158,9 @@ public: #ifndef QT_NO_SSL void connectToHostEncrypted(const QString &hostName, quint16 port = 443, const QSslConfiguration &sslConfiguration = QSslConfiguration::defaultConfiguration()); + void connectToHostEncrypted(const QString &hostName, quint16 port, + const QSslConfiguration &sslConfiguration, + const QString &peerName); #endif void connectToHost(const QString &hostName, quint16 port = 80); diff --git a/src/network/access/qnetworkcookie.h b/src/network/access/qnetworkcookie.h index e462b98555..58c504f9ae 100644 --- a/src/network/access/qnetworkcookie.h +++ b/src/network/access/qnetworkcookie.h @@ -67,11 +67,11 @@ public: QNetworkCookie(const QNetworkCookie &other); ~QNetworkCookie(); #ifdef Q_COMPILER_RVALUE_REFS - QNetworkCookie &operator=(QNetworkCookie &&other) Q_DECL_NOTHROW { swap(other); return *this; } + QNetworkCookie &operator=(QNetworkCookie &&other) noexcept { swap(other); return *this; } #endif QNetworkCookie &operator=(const QNetworkCookie &other); - void swap(QNetworkCookie &other) Q_DECL_NOTHROW { qSwap(d, other.d); } + void swap(QNetworkCookie &other) noexcept { qSwap(d, other.d); } bool operator==(const QNetworkCookie &other) const; inline bool operator!=(const QNetworkCookie &other) const diff --git a/src/network/access/qnetworkdiskcache_p.h b/src/network/access/qnetworkdiskcache_p.h index f7988e7dda..c797e63830 100644 --- a/src/network/access/qnetworkdiskcache_p.h +++ b/src/network/access/qnetworkdiskcache_p.h @@ -67,7 +67,7 @@ class QFile; class QCacheItem { public: - QCacheItem() : file(0) + QCacheItem() : file(nullptr) { } ~QCacheItem() @@ -85,7 +85,7 @@ public: metaData = QNetworkCacheMetaData(); data.close(); delete file; - file = 0; + file = nullptr; } void writeHeader(QFile *device) const; void writeCompressedData(QFile *device) const; diff --git a/src/network/access/qnetworkfile.cpp b/src/network/access/qnetworkfile.cpp index 374dd26e2e..b7c91f28d8 100644 --- a/src/network/access/qnetworkfile.cpp +++ b/src/network/access/qnetworkfile.cpp @@ -65,21 +65,21 @@ void QNetworkFile::open() if (fi.isDir()) { QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Cannot open %1: Path is a directory").arg(fileName()); - error(QNetworkReply::ContentOperationNotPermittedError, msg); + emit error(QNetworkReply::ContentOperationNotPermittedError, msg); } else { - headerRead(QNetworkRequest::LastModifiedHeader, QVariant::fromValue(fi.lastModified())); - headerRead(QNetworkRequest::ContentLengthHeader, QVariant::fromValue(fi.size())); + emit headerRead(QNetworkRequest::LastModifiedHeader, QVariant::fromValue(fi.lastModified())); + emit headerRead(QNetworkRequest::ContentLengthHeader, QVariant::fromValue(fi.size())); opened = QFile::open(QIODevice::ReadOnly | QIODevice::Unbuffered); if (!opened) { QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Error opening %1: %2").arg(fileName(), errorString()); if (exists()) - error(QNetworkReply::ContentAccessDenied, msg); + emit error(QNetworkReply::ContentAccessDenied, msg); else - error(QNetworkReply::ContentNotFoundError, msg); + emit error(QNetworkReply::ContentNotFoundError, msg); } } - finished(opened); + emit finished(opened); } void QNetworkFile::close() diff --git a/src/network/access/qnetworkreplyfileimpl_p.h b/src/network/access/qnetworkreplyfileimpl_p.h index 55aece0bed..48d82abd3f 100644 --- a/src/network/access/qnetworkreplyfileimpl_p.h +++ b/src/network/access/qnetworkreplyfileimpl_p.h @@ -80,6 +80,7 @@ public: private Q_SLOTS: void fileOpenFinished(bool isOpen); +private: Q_DECLARE_PRIVATE(QNetworkReplyFileImpl) }; diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp index 8750a841f6..f801ef0c88 100644 --- a/src/network/access/qnetworkreplyhttpimpl.cpp +++ b/src/network/access/qnetworkreplyhttpimpl.cpp @@ -524,6 +524,8 @@ bool QNetworkReplyHttpImplPrivate::loadFromCacheIfAllowed(QHttpNetworkRequest &h QHash<QByteArray, QByteArray> cacheControl = parseHttpOptionHeader(it->second); if (cacheControl.contains("must-revalidate")) return false; + if (cacheControl.contains("no-cache")) + return false; } QDateTime currentDateTime = QDateTime::currentDateTimeUtc(); @@ -785,6 +787,7 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq if (request.attribute(QNetworkRequest::EmitAllUploadProgressSignalsAttribute).toBool()) emitAllUploadProgressSignals = true; + httpRequest.setPeerVerifyName(newHttpRequest.peerVerifyName()); // Create the HTTP thread delegate QHttpThreadDelegate *delegate = new QHttpThreadDelegate; @@ -917,7 +920,7 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq // From http thread to user thread: QObject::connect(forwardUploadDevice, SIGNAL(wantData(qint64)), q, SLOT(wantUploadDataSlot(qint64))); - QObject::connect(forwardUploadDevice,SIGNAL(processedData(qint64, qint64)), + QObject::connect(forwardUploadDevice,SIGNAL(processedData(qint64,qint64)), q, SLOT(sentUploadDataSlot(qint64,qint64))); QObject::connect(forwardUploadDevice, SIGNAL(resetData(bool*)), q, SLOT(resetUploadDataSlot(bool*)), @@ -1561,7 +1564,7 @@ bool QNetworkReplyHttpImplPrivate::sendCacheContents(const QNetworkCacheMetaData QIODevice *contents = nc->data(url); if (!contents) { #if defined(QNETWORKACCESSHTTPBACKEND_DEBUG) - qDebug() << "Can not send cache, the contents are 0" << url; + qDebug() << "Cannot send cache, the contents are 0" << url; #endif return false; } @@ -1676,13 +1679,13 @@ QNetworkCacheMetaData QNetworkReplyHttpImplPrivate::fetchCacheMetaData(const QNe || header == "content-range" || header == "content-type") continue; - - // For MS servers that send "Content-Length: 0" on 304 responses - // ignore this too - if (header == "content-length") - continue; } + // IIS has been known to send "Content-Length: 0" on 304 responses, so + // ignore this too + if (header == "content-length" && statusCode == 304) + continue; + #if defined(QNETWORKACCESSHTTPBACKEND_DEBUG) QByteArray n = q->rawHeader(header); QByteArray o; @@ -1730,18 +1733,8 @@ QNetworkCacheMetaData QNetworkReplyHttpImplPrivate::fetchCacheMetaData(const QNe if (httpRequest.operation() == QHttpNetworkRequest::Get) { canDiskCache = true; - // 14.32 - // HTTP/1.1 caches SHOULD treat "Pragma: no-cache" as if the client - // had sent "Cache-Control: no-cache". - it = cacheHeaders.findRawHeader("pragma"); - if (it != cacheHeaders.rawHeaders.constEnd() - && it->second == "no-cache") - canDiskCache = false; - // HTTP/1.1. Check the Cache-Control header - if (cacheControl.contains("no-cache")) - canDiskCache = false; - else if (cacheControl.contains("no-store")) + if (cacheControl.contains("no-store")) canDiskCache = false; // responses to POST might be cacheable @@ -1879,11 +1872,9 @@ void QNetworkReplyHttpImplPrivate::_q_startOperation() { Q_Q(QNetworkReplyHttpImpl); - // ensure this function is only being called once - if (state == Working) { - qDebug() << "QNetworkReplyHttpImplPrivate::_q_startOperation was called more than once" << url; + if (state == Working) // ensure this function is only being called once return; - } + state = Working; #ifndef QT_NO_BEARERMANAGEMENT diff --git a/src/network/access/qnetworkreplyimpl_p.h b/src/network/access/qnetworkreplyimpl_p.h index f4e8284ab6..4881e84e9c 100644 --- a/src/network/access/qnetworkreplyimpl_p.h +++ b/src/network/access/qnetworkreplyimpl_p.h @@ -74,7 +74,7 @@ class QNetworkReplyImpl: public QNetworkReply { Q_OBJECT public: - QNetworkReplyImpl(QObject *parent = 0); + QNetworkReplyImpl(QObject *parent = nullptr); ~QNetworkReplyImpl(); virtual void abort() override; diff --git a/src/network/access/qnetworkreplywasmimpl.cpp b/src/network/access/qnetworkreplywasmimpl.cpp index 23ca62acd4..bb6ef07741 100644 --- a/src/network/access/qnetworkreplywasmimpl.cpp +++ b/src/network/access/qnetworkreplywasmimpl.cpp @@ -102,6 +102,8 @@ static void q_loadCallback(val event) return; } QString statusText = QString::fromStdString(xhr["statusText"].as<std::string>()); + int readyState = xhr["readyState"].as<int>(); + if (status == 200 || status == 203) { QString responseString; const std::string responseType = xhr["responseType"].as<std::string>(); @@ -112,13 +114,15 @@ static void q_loadCallback(val event) QString::fromStdWString(val::global("JSON").call<std::wstring>("stringify", xhr["response"])); } else if (responseType == "arraybuffer" || responseType == "blob") { // handle this data in the FileReader, triggered by the call to readAsArrayBuffer + val blob = xhr["response"]; + val reader = val::global("FileReader").new_(); - reader.set("onload", val::module_property("QNetworkReplyWasmImplPrivate_readBinary")); + reader.set("onload", val::module_property("qt_QNetworkReplyWasmImplPrivate_readBinary")); reader.set("data-handler", xhr["data-handler"]); - reader.call<void>("readAsArrayBuffer", xhr["response"]); + + reader.call<void>("readAsArrayBuffer", blob); } - int readyState = xhr["readyState"].as<int>(); if (readyState == 4) { // done reply->setReplyAttributes(xhr["data-handler"].as<quintptr>(), status, statusText); @@ -167,15 +171,15 @@ static void q_readBinary(val event) reinterpret_cast<quintptr>(buffer.data()), size); destinationTypedArray.call<void>("set", sourceTypedArray); reply->dataReceived(buffer, buffer.size()); + QCoreApplication::processEvents(); } - -EMSCRIPTEN_BINDINGS(network_module) { - function("QNetworkReplyWasmImplPrivate_requestErrorCallback", q_requestErrorCallback); - function("QNetworkReplyWasmImplPrivate_progressCallback", q_progressCallback); - function("QNetworkReplyWasmImplPrivate_loadCallback", q_loadCallback); - function("QNetworkReplyWasmImplPrivate_responseHeadersCallback", q_responseHeadersCallback); - function("QNetworkReplyWasmImplPrivate_readBinary", q_readBinary); +EMSCRIPTEN_BINDINGS(qtNetworkModule) { + function("qt_QNetworkReplyWasmImplPrivate_requestErrorCallback", q_requestErrorCallback); + function("qt_QNetworkReplyWasmImplPrivate_progressCallback", q_progressCallback); + function("qt_QNetworkReplyWasmImplPrivate_loadCallback", q_loadCallback); + function("qt_QNetworkReplyWasmImplPrivate_responseHeadersCallback", q_responseHeadersCallback); + function("qt_QNetworkReplyWasmImplPrivate_readBinary", q_readBinary); } QNetworkReplyWasmImplPrivate::QNetworkReplyWasmImplPrivate() @@ -222,11 +226,18 @@ QByteArray QNetworkReplyWasmImpl::methodName() const void QNetworkReplyWasmImpl::close() { + setFinished(true); + emit finished(); + QNetworkReply::close(); } void QNetworkReplyWasmImpl::abort() { + Q_D(const QNetworkReplyWasmImpl); + setError( QNetworkReply::OperationCanceledError, "Operation canceled" ); + d->doAbort(); + close(); } @@ -234,9 +245,6 @@ qint64 QNetworkReplyWasmImpl::bytesAvailable() const { Q_D(const QNetworkReplyWasmImpl); - if (!d->isFinished) - return QNetworkReply::bytesAvailable(); - return QNetworkReply::bytesAvailable() + d->downloadBufferCurrentSize - d->downloadBufferReadPosition; } @@ -258,7 +266,7 @@ qint64 QNetworkReplyWasmImpl::readData(char *data, qint64 maxlen) Q_D(QNetworkReplyWasmImpl); qint64 howMuch = qMin(maxlen, (d->downloadBuffer.size() - d->downloadBufferReadPosition)); - memcpy(data, d->downloadBuffer.constData(), howMuch); + memcpy(data, d->downloadBuffer.constData() + d->downloadBufferReadPosition, howMuch); d->downloadBufferReadPosition += howMuch; return howMuch; @@ -307,32 +315,29 @@ void QNetworkReplyWasmImplPrivate::setReplyAttributes(quintptr data, int statusC handler->q_func()->setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, statusReason); } +void QNetworkReplyWasmImplPrivate::doAbort() const +{ + m_xhr.call<void>("abort"); +} + void QNetworkReplyWasmImplPrivate::doSendRequest() { Q_Q(QNetworkReplyWasmImpl); totalDownloadSize = 0; - val xhr = val::global("XMLHttpRequest").new_(); + m_xhr = val::global("XMLHttpRequest").new_(); std::string verb = q->methodName().toStdString(); - QUrl url; QString extraDataString; - if (request.url().hasQuery()) { //strip query from url - extraDataString = request.url().query(QUrl::FullyEncoded); - QString urlStr = request.url().toString(); - url.setUrl(urlStr.left(urlStr.indexOf("?"))); - } else { - url = request.url(); - } - xhr.call<void>("open", verb, url.toString().toStdString()); + m_xhr.call<void>("open", verb, request.url().toString().toStdString()); - xhr.set("onerror", val::module_property("QNetworkReplyWasmImplPrivate_requestErrorCallback")); - xhr.set("onload", val::module_property("QNetworkReplyWasmImplPrivate_loadCallback")); - xhr.set("onprogress", val::module_property("QNetworkReplyWasmImplPrivate_progressCallback")); - xhr.set("onreadystatechange", val::module_property("QNetworkReplyWasmImplPrivate_responseHeadersCallback")); + m_xhr.set("onerror", val::module_property("qt_QNetworkReplyWasmImplPrivate_requestErrorCallback")); + m_xhr.set("onload", val::module_property("qt_QNetworkReplyWasmImplPrivate_loadCallback")); + m_xhr.set("onprogress", val::module_property("qt_QNetworkReplyWasmImplPrivate_progressCallback")); + m_xhr.set("onreadystatechange", val::module_property("qt_QNetworkReplyWasmImplPrivate_responseHeadersCallback")); - xhr.set("data-handler", val(quintptr(reinterpret_cast<void *>(this)))); + m_xhr.set("data-handler", val(quintptr(reinterpret_cast<void *>(this)))); QByteArray contentType = request.rawHeader("Content-Type"); @@ -351,11 +356,10 @@ void QNetworkReplyWasmImplPrivate::doSendRequest() } if (contentType.contains("json")) { if (!extraDataString.isEmpty()) { - xhr.set("responseType", val("json")); + m_xhr.set("responseType", val("json")); dataToSend = val(extraDataString.toStdString()); } - } - if (contentType.contains("form")) { //construct form data + } else if (contentType.contains("form")) { //construct form data if (!extraDataString.isEmpty()) { val formData = val::global("FormData").new_(); QStringList formList = extraDataString.split('&'); @@ -365,12 +369,14 @@ void QNetworkReplyWasmImplPrivate::doSendRequest() } dataToSend = formData; } + } else { + m_xhr.set("responseType", val("blob")); } // set request headers for (auto header : request.rawHeaderList()) { - xhr.call<void>("setRequestHeader", header.toStdString(), request.rawHeader(header).toStdString()); + m_xhr.call<void>("setRequestHeader", header.toStdString(), request.rawHeader(header).toStdString()); } - xhr.call<void>("send", dataToSend); + m_xhr.call<void>("send", dataToSend); } void QNetworkReplyWasmImplPrivate::emitReplyError(QNetworkReply::NetworkError errorCode, const QString &errorString) @@ -414,10 +420,13 @@ void QNetworkReplyWasmImplPrivate::dataReceived(const QByteArray &buffer, int bu downloadBuffer.append(buffer, bufferSize); + emit q->readyRead(); + if (downloadBufferCurrentSize == totalDownloadSize) { - q->setFinished(true); - emit q->finished(); - } + q->setFinished(true); + emit q->readChannelFinished(); + emit q->finished(); + } } //taken from qnetworkrequest.cpp diff --git a/src/network/access/qnetworkreplywasmimpl_p.h b/src/network/access/qnetworkreplywasmimpl_p.h index 69c90de41a..e1e6bf4e24 100644 --- a/src/network/access/qnetworkreplywasmimpl_p.h +++ b/src/network/access/qnetworkreplywasmimpl_p.h @@ -62,6 +62,7 @@ #include <emscripten.h> #include <emscripten/html5.h> +#include <emscripten/val.h> QT_BEGIN_NAMESPACE @@ -134,6 +135,9 @@ public: QIODevice *outgoingData; QSharedPointer<QRingBuffer> outgoingDataBuffer; + emscripten::val m_xhr = emscripten::val::null(); + void doAbort() const; + static QNetworkReply::NetworkError statusCodeFromHttp(int httpStatusCode, const QUrl &url); Q_DECLARE_PUBLIC(QNetworkReplyWasmImpl) }; diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp index 689eecfbb9..f15c43cdd8 100644 --- a/src/network/access/qnetworkrequest.cpp +++ b/src/network/access/qnetworkrequest.cpp @@ -438,6 +438,7 @@ public: if (other.sslConfiguration) sslConfiguration = new QSslConfiguration(*other.sslConfiguration); #endif + peerVerifyName = other.peerVerifyName; } inline bool operator==(const QNetworkRequestPrivate &other) const @@ -446,7 +447,8 @@ public: priority == other.priority && rawHeaders == other.rawHeaders && attributes == other.attributes && - maxRedirectsAllowed == other.maxRedirectsAllowed; + maxRedirectsAllowed == other.maxRedirectsAllowed && + peerVerifyName == other.peerVerifyName; // don't compare cookedHeaders } @@ -456,6 +458,7 @@ public: mutable QSslConfiguration *sslConfiguration; #endif int maxRedirectsAllowed; + QString peerVerifyName; }; /*! @@ -714,7 +717,7 @@ void QNetworkRequest::setOriginatingObject(QObject *object) \since 4.6 Returns a reference to the object that initiated this - network request; returns 0 if not set or the object has + network request; returns \nullptr if not set or the object has been destroyed. \sa setOriginatingObject() @@ -789,6 +792,32 @@ void QNetworkRequest::setMaximumRedirectsAllowed(int maxRedirectsAllowed) d->maxRedirectsAllowed = maxRedirectsAllowed; } +/*! + \since 5.13 + + Returns the host name set for the certificate validation, as set by + setPeerVerifyName. By default this returns a null string. + + \sa setPeerVerifyName +*/ +QString QNetworkRequest::peerVerifyName() const +{ + return d->peerVerifyName; +} + +/*! + \since 5.13 + + Sets \a peerName as host name for the certificate validation, instead of the one used for the + TCP connection. + + \sa peerVerifyName +*/ +void QNetworkRequest::setPeerVerifyName(const QString &peerName) +{ + d->peerVerifyName = peerName; +} + static QByteArray headerName(QNetworkRequest::KnownHeaders header) { switch (header) { diff --git a/src/network/access/qnetworkrequest.h b/src/network/access/qnetworkrequest.h index 8462eae8c8..2515ff6ead 100644 --- a/src/network/access/qnetworkrequest.h +++ b/src/network/access/qnetworkrequest.h @@ -131,11 +131,11 @@ public: QNetworkRequest(const QNetworkRequest &other); ~QNetworkRequest(); #ifdef Q_COMPILER_RVALUE_REFS - QNetworkRequest &operator=(QNetworkRequest &&other) Q_DECL_NOTHROW { swap(other); return *this; } + QNetworkRequest &operator=(QNetworkRequest &&other) noexcept { swap(other); return *this; } #endif QNetworkRequest &operator=(const QNetworkRequest &other); - void swap(QNetworkRequest &other) Q_DECL_NOTHROW { qSwap(d, other.d); } + void swap(QNetworkRequest &other) noexcept { qSwap(d, other.d); } bool operator==(const QNetworkRequest &other) const; inline bool operator!=(const QNetworkRequest &other) const @@ -173,6 +173,8 @@ public: int maximumRedirectsAllowed() const; void setMaximumRedirectsAllowed(int maximumRedirectsAllowed); + QString peerVerifyName() const; + void setPeerVerifyName(const QString &peerName); private: QSharedDataPointer<QNetworkRequestPrivate> d; friend class QNetworkRequestPrivate; |