diff options
author | Lena Biliaieva <lena.biliaieva@qt.io> | 2024-01-04 16:48:13 +0100 |
---|---|---|
committer | Lena Biliaieva <lena.biliaieva@qt.io> | 2024-01-25 00:48:10 +0000 |
commit | 15b0bd69ff2a3ac967ddb98b5fc3c3ce8c3d5b4b (patch) | |
tree | 935f59cf41c0b2e122f44447e9ba7d14fdbd6030 | |
parent | c29a235833410fde4cb4d502f89129bccd7403f0 (diff) |
Network: Use QHttpHeaders in QHttpHeaderParser
QHttpHeaderParser::headers() method is changed to return QHttpHeaders.
QAuthenticatorPrivate::parseHttpResponse() method is changed to work with QHttpHeaders.
QHttpNetworkHeader::header() method is updated to return QHttpHeaders.
Tests are updated.
Task-number: QTBUG-120133
Change-Id: I20a18b509acd7a8b8d93884cff8349519d64293e
Reviewed-by: Ievgenii Meshcheriakov <ievgenii.meshcheriakov@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
Reviewed-by: Øystein Heskestad <oystein.heskestad@qt.io>
27 files changed, 194 insertions, 192 deletions
diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt index 82b59af329..ee61bc6113 100644 --- a/src/network/CMakeLists.txt +++ b/src/network/CMakeLists.txt @@ -20,6 +20,7 @@ qt_internal_add_module(Network access/qnetworkcookie.cpp access/qnetworkcookie.h access/qnetworkcookie_p.h access/qnetworkcookiejar.cpp access/qnetworkcookiejar.h access/qnetworkcookiejar_p.h access/qnetworkfile.cpp access/qnetworkfile_p.h + access/qhttpheaders.cpp access/qhttpheaders.h access/qhttpheaderparser.cpp access/qhttpheaderparser_p.h access/qnetworkreply.cpp access/qnetworkreply.h access/qnetworkreply_p.h access/qnetworkreplydataimpl.cpp access/qnetworkreplydataimpl_p.h @@ -124,7 +125,6 @@ qt_internal_extend_target(Network CONDITION QT_FEATURE_http access/qhttp2configuration.cpp access/qhttp2configuration.h access/qhttp2connection.cpp access/qhttp2connection_p.h access/qhttp2protocolhandler.cpp access/qhttp2protocolhandler_p.h - access/qhttpheaders.cpp access/qhttpheaders.h access/qhttpmultipart.cpp access/qhttpmultipart.h access/qhttpmultipart_p.h access/qhttpnetworkconnection.cpp access/qhttpnetworkconnection_p.h access/qhttpnetworkconnectionchannel.cpp access/qhttpnetworkconnectionchannel_p.h diff --git a/src/network/access/http2/http2protocol.cpp b/src/network/access/http2/http2protocol.cpp index 1e7291e48e..7d582497ab 100644 --- a/src/network/access/http2/http2protocol.cpp +++ b/src/network/access/http2/http2protocol.cpp @@ -184,14 +184,13 @@ QNetworkReply::NetworkError qt_error(quint32 errorCode) bool is_protocol_upgraded(const QHttpNetworkReply &reply) { - if (reply.statusCode() == 101) { - // Do some minimal checks here - we expect 'Upgrade: h2c' to be found. - const auto &header = reply.header(); - for (const QPair<QByteArray, QByteArray> &field : header) { - if (field.first.compare("upgrade", Qt::CaseInsensitive) == 0 && - field.second.compare("h2c", Qt::CaseInsensitive) == 0) - return true; - } + if (reply.statusCode() != 101) + return false; + + // Do some minimal checks here - we expect 'Upgrade: h2c' to be found. + for (const auto &v : reply.header().values(QHttpHeaders::WellKnownHeader::Upgrade)) { + if (v.compare("h2c", Qt::CaseInsensitive) == 0) + return true; } return false; diff --git a/src/network/access/qhsts.cpp b/src/network/access/qhsts.cpp index 13755d0003..21ed08ce4a 100644 --- a/src/network/access/qhsts.cpp +++ b/src/network/access/qhsts.cpp @@ -3,6 +3,8 @@ #include "qhsts_p.h" +#include "qhttpheaders.h" + #include "QtCore/private/qipaddress_p.h" #include "QtCore/qlist.h" @@ -40,7 +42,7 @@ static bool is_valid_domain_name(const QString &host) return true; } -void QHstsCache::updateFromHeaders(const QList<QPair<QByteArray, QByteArray>> &headers, +void QHstsCache::updateFromHeaders(const QHttpHeaders &headers, const QUrl &url) { if (!url.isValid()) @@ -324,27 +326,25 @@ quoted-pair = "\" CHAR */ -bool QHstsHeaderParser::parse(const QList<QPair<QByteArray, QByteArray>> &headers) +bool QHstsHeaderParser::parse(const QHttpHeaders &headers) { - for (const auto &h : headers) { - // We compare directly because header name was already 'trimmed' for us: - if (h.first.compare("Strict-Transport-Security", Qt::CaseInsensitive) == 0) { - header = h.second; - // RFC6797, 8.1: - // - // The UA MUST ignore any STS header fields not conforming to the - // grammar specified in Section 6.1 ("Strict-Transport-Security HTTP - // Response Header Field"). - // - // If a UA receives more than one STS header field in an HTTP - // response message over secure transport, then the UA MUST process - // only the first such header field. - // - // We read this as: ignore all invalid headers and take the first valid: - if (parseSTSHeader() && maxAgeFound) { - expiry = QDateTime::currentDateTimeUtc().addSecs(maxAge); - return true; - } + for (const auto &value : headers.values( + QHttpHeaders::WellKnownHeader::StrictTransportSecurity)) { + header = value; + // RFC6797, 8.1: + // + // The UA MUST ignore any STS header fields not conforming to the + // grammar specified in Section 6.1 ("Strict-Transport-Security HTTP + // Response Header Field"). + // + // If a UA receives more than one STS header field in an HTTP + // response message over secure transport, then the UA MUST process + // only the first such header field. + // + // We read this as: ignore all invalid headers and take the first valid: + if (parseSTSHeader() && maxAgeFound) { + expiry = QDateTime::currentDateTimeUtc().addSecs(maxAge); + return true; } } diff --git a/src/network/access/qhsts_p.h b/src/network/access/qhsts_p.h index 96d4ee8dc5..ff9378197b 100644 --- a/src/network/access/qhsts_p.h +++ b/src/network/access/qhsts_p.h @@ -31,11 +31,13 @@ QT_BEGIN_NAMESPACE +class QHttpHeaders; + class Q_AUTOTEST_EXPORT QHstsCache { public: - void updateFromHeaders(const QList<QPair<QByteArray, QByteArray>> &headers, + void updateFromHeaders(const QHttpHeaders &headers, const QUrl &url); void updateFromPolicies(const QList<QHstsPolicy> &hosts); void updateKnownHost(const QUrl &url, const QDateTime &expires, @@ -90,7 +92,7 @@ class Q_AUTOTEST_EXPORT QHstsHeaderParser { public: - bool parse(const QList<QPair<QByteArray, QByteArray>> &headers); + bool parse(const QHttpHeaders &headers); QDateTime expirationDate() const { return expiry; } bool includeSubDomains() const { return subDomainsFound; } diff --git a/src/network/access/qhttp2protocolhandler.cpp b/src/network/access/qhttp2protocolhandler.cpp index 60f7ea12a6..4bb057b209 100644 --- a/src/network/access/qhttp2protocolhandler.cpp +++ b/src/network/access/qhttp2protocolhandler.cpp @@ -60,7 +60,7 @@ HPack::HttpHeader build_headers(const QHttpNetworkRequest &request, quint32 maxH if (size.second > maxHeaderListSize) return HttpHeader(); // Bad, we cannot send this request ... - const auto requestHeader = request.header(); + const auto requestHeader = request.header().toListOfPairs(); for (const auto &field : requestHeader) { const HeaderSize delta = entry_size(field.first, field.second); if (!delta.first) // Overflow??? diff --git a/src/network/access/qhttpheaderparser.cpp b/src/network/access/qhttpheaderparser.cpp index cb6b479543..0b7882c18a 100644 --- a/src/network/access/qhttpheaderparser.cpp +++ b/src/network/access/qhttpheaderparser.cpp @@ -52,11 +52,11 @@ bool QHttpHeaderParser::parseHeaders(QByteArrayView header) if (header.size() - (header.endsWith("\r\n") ? 2 : 1) > maxTotalSize) return false; - QList<QPair<QByteArray, QByteArray>> result; + QHttpHeaders result; while (!header.empty()) { const qsizetype colon = header.indexOf(':'); if (colon == -1) // if no colon check if empty headers - return result.empty() && (header == "\n" || header == "\r\n"); + return result.isEmpty() && (header == "\n" || header == "\r\n"); if (result.size() >= maxFieldCount) return false; QByteArrayView name = header.first(colon); @@ -82,7 +82,7 @@ bool QHttpHeaderParser::parseHeaders(QByteArrayView header) header = header.sliced(endLine + 1); } while (hSpaceStart(header)); Q_ASSERT(name.size() + 1 + value.size() <= maxFieldSize); - result.append(qMakePair(name.toByteArray(), value)); + result.append(name, value); } fields = result; @@ -128,25 +128,15 @@ bool QHttpHeaderParser::parseStatus(QByteArrayView status) return ok && uint(majorVersion) <= 9 && uint(minorVersion) <= 9; } -const QList<QPair<QByteArray, QByteArray> >& QHttpHeaderParser::headers() const +const QHttpHeaders& QHttpHeaderParser::headers() const { return fields; } -static auto firstEqualsName(QByteArrayView name) -{ - return [name](const QPair<QByteArray, QByteArray> &header) { - return name.compare(header.first, Qt::CaseInsensitive) == 0; - }; -} - QByteArray QHttpHeaderParser::firstHeaderField(QByteArrayView name, const QByteArray &defaultValue) const { - const auto it = std::find_if(fields.begin(), fields.end(), firstEqualsName(name)); - if (it != fields.end()) - return it->second; - return defaultValue; + return fields.value(name, defaultValue).toByteArray(); } QByteArray QHttpHeaderParser::combinedHeaderValue(QByteArrayView name, const QByteArray &defaultValue) const @@ -159,33 +149,28 @@ QByteArray QHttpHeaderParser::combinedHeaderValue(QByteArrayView name, const QBy QList<QByteArray> QHttpHeaderParser::headerFieldValues(QByteArrayView name) const { - QList<QByteArray> result; - for (auto it = fields.constBegin(); it != fields.constEnd(); ++it) - if (name.compare(it->first, Qt::CaseInsensitive) == 0) - result += it->second; - - return result; + return fields.values(name); } void QHttpHeaderParser::removeHeaderField(QByteArrayView name) { - fields.removeIf(firstEqualsName(name)); + fields.removeAll(name); } void QHttpHeaderParser::setHeaderField(const QByteArray &name, const QByteArray &data) { removeHeaderField(name); - fields.append(qMakePair(name, data)); + fields.append(name, data); } void QHttpHeaderParser::prependHeaderField(const QByteArray &name, const QByteArray &data) { - fields.prepend(qMakePair(name, data)); + fields.insert(0, name, data); } void QHttpHeaderParser::appendHeaderField(const QByteArray &name, const QByteArray &data) { - fields.append(qMakePair(name, data)); + fields.append(name, data); } void QHttpHeaderParser::clearHeaders() diff --git a/src/network/access/qhttpheaderparser_p.h b/src/network/access/qhttpheaderparser_p.h index 50ca83693a..5e8f3c8130 100644 --- a/src/network/access/qhttpheaderparser_p.h +++ b/src/network/access/qhttpheaderparser_p.h @@ -16,6 +16,7 @@ // #include <QtNetwork/private/qtnetworkglobal_p.h> +#include <QtNetwork/qhttpheaders.h> #include <QByteArray> #include <QList> @@ -52,7 +53,7 @@ public: bool parseHeaders(QByteArrayView headers); bool parseStatus(QByteArrayView status); - const QList<QPair<QByteArray, QByteArray> >& headers() const; + const QHttpHeaders& headers() const; void setStatusCode(int code); int getStatusCode() const; int getMajorVersion() const; @@ -83,7 +84,7 @@ public: qsizetype maxHeaderFields() const { return maxFieldCount; } private: - QList<QPair<QByteArray, QByteArray> > fields; + QHttpHeaders fields; QString reasonPhrase; int statusCode; int majorVersion; diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp index 1f2fb6d03c..b8a49a926d 100644 --- a/src/network/access/qhttpnetworkconnection.cpp +++ b/src/network/access/qhttpnetworkconnection.cpp @@ -411,7 +411,7 @@ bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket resend = false; //create the response header to be used with QAuthenticatorPrivate. - QList<QPair<QByteArray, QByteArray> > fields = reply->header(); + const auto headers = reply->header(); // Check that any of the proposed authenticate methods are supported const QByteArray header = isProxy ? "proxy-authenticate" : "www-authenticate"; @@ -427,7 +427,7 @@ bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket if (auth->isNull()) auth->detach(); QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(*auth); - priv->parseHttpResponse(fields, isProxy, reply->url().host()); + priv->parseHttpResponse(headers, isProxy, reply->url().host()); // Update method in case it changed if (priv->method == QAuthenticatorPrivate::None) return false; @@ -523,12 +523,9 @@ QHttpNetworkConnectionPrivate::parseRedirectResponse(QHttpNetworkReply *reply) return {{}, QNetworkReply::NoError}; QUrl redirectUrl; - const QList<QPair<QByteArray, QByteArray> > fields = reply->header(); - for (const QNetworkReply::RawHeaderPair &header : fields) { - if (header.first.compare("location", Qt::CaseInsensitive) == 0) { - redirectUrl = QUrl::fromEncoded(header.second); - break; - } + const QHttpHeaders fields = reply->header(); + if (const auto h = fields.values(QHttpHeaders::WellKnownHeader::Location); !h.empty()) { + redirectUrl = QUrl::fromEncoded(h.first()); } // If the location url is invalid/empty, we return ProtocolUnknownError diff --git a/src/network/access/qhttpnetworkheader.cpp b/src/network/access/qhttpnetworkheader.cpp index 8ef664c012..7f9c94dc9c 100644 --- a/src/network/access/qhttpnetworkheader.cpp +++ b/src/network/access/qhttpnetworkheader.cpp @@ -53,7 +53,7 @@ void QHttpNetworkHeaderPrivate::prependHeaderField(const QByteArray &name, const parser.prependHeaderField(name, data); } -QList<QPair<QByteArray, QByteArray> > QHttpNetworkHeaderPrivate::headers() const +QHttpHeaders QHttpNetworkHeaderPrivate::headers() const { return parser.headers(); } diff --git a/src/network/access/qhttpnetworkheader_p.h b/src/network/access/qhttpnetworkheader_p.h index 57c06eb95c..afbc6cb6fe 100644 --- a/src/network/access/qhttpnetworkheader_p.h +++ b/src/network/access/qhttpnetworkheader_p.h @@ -17,6 +17,7 @@ #include <QtNetwork/private/qtnetworkglobal_p.h> #include <QtNetwork/private/qhttpheaderparser_p.h> +#include <QtNetwork/qhttpheaders.h> #include <qshareddata.h> #include <qurl.h> @@ -40,7 +41,7 @@ public: virtual qint64 contentLength() const = 0; virtual void setContentLength(qint64 length) = 0; - virtual QList<QPair<QByteArray, QByteArray> > header() const = 0; + virtual QHttpHeaders header() const = 0; virtual QByteArray headerField(QByteArrayView name, const QByteArray &defaultValue = QByteArray()) const = 0; virtual void setHeaderField(const QByteArray &name, const QByteArray &data) = 0; }; @@ -61,7 +62,7 @@ public: void setHeaderField(const QByteArray &name, const QByteArray &data); void prependHeaderField(const QByteArray &name, const QByteArray &data); void clearHeaders(); - QList<QPair<QByteArray, QByteArray> > headers() const; + QHttpHeaders headers() const; bool operator==(const QHttpNetworkHeaderPrivate &other) const; }; diff --git a/src/network/access/qhttpnetworkreply.cpp b/src/network/access/qhttpnetworkreply.cpp index aad41e9a14..6d82e81322 100644 --- a/src/network/access/qhttpnetworkreply.cpp +++ b/src/network/access/qhttpnetworkreply.cpp @@ -67,7 +67,7 @@ void QHttpNetworkReply::setContentLength(qint64 length) d->setContentLength(length); } -QList<QPair<QByteArray, QByteArray> > QHttpNetworkReply::header() const +QHttpHeaders QHttpNetworkReply::header() const { return d_func()->parser.headers(); } diff --git a/src/network/access/qhttpnetworkreply_p.h b/src/network/access/qhttpnetworkreply_p.h index 54ff53452b..c1b155caa7 100644 --- a/src/network/access/qhttpnetworkreply_p.h +++ b/src/network/access/qhttpnetworkreply_p.h @@ -41,6 +41,7 @@ Q_MOC_INCLUDE(<QtNetwork/QNetworkProxy>) Q_MOC_INCLUDE(<QtNetwork/QAuthenticator>) #include <private/qdecompresshelper_p.h> +#include <QtNetwork/qhttpheaders.h> #include <QtCore/qpointer.h> @@ -72,7 +73,7 @@ public: qint64 contentLength() const override; void setContentLength(qint64 length) override; - QList<QPair<QByteArray, QByteArray> > header() const override; + QHttpHeaders header() const override; QByteArray headerField(QByteArrayView name, const QByteArray &defaultValue = QByteArray()) const override; void setHeaderField(const QByteArray &name, const QByteArray &data) override; void appendHeaderField(const QByteArray &name, const QByteArray &data); diff --git a/src/network/access/qhttpnetworkrequest.cpp b/src/network/access/qhttpnetworkrequest.cpp index c32a75eccb..bc3eb67adc 100644 --- a/src/network/access/qhttpnetworkrequest.cpp +++ b/src/network/access/qhttpnetworkrequest.cpp @@ -112,7 +112,7 @@ QByteArray QHttpNetworkRequest::uri(bool throughProxy) const QByteArray QHttpNetworkRequestPrivate::header(const QHttpNetworkRequest &request, bool throughProxy) { - const QList<QPair<QByteArray, QByteArray> > fields = request.header(); + const QList<QPair<QByteArray, QByteArray> > fields = request.header().toListOfPairs(); QByteArray ba; ba.reserve(40 + fields.size()*25); // very rough lower bound estimation @@ -235,7 +235,7 @@ void QHttpNetworkRequest::setContentLength(qint64 length) d->setContentLength(length); } -QList<QPair<QByteArray, QByteArray> > QHttpNetworkRequest::header() const +QHttpHeaders QHttpNetworkRequest::header() const { return d->parser.headers(); } diff --git a/src/network/access/qhttpnetworkrequest_p.h b/src/network/access/qhttpnetworkrequest_p.h index 6cf8ca2401..131885f6d2 100644 --- a/src/network/access/qhttpnetworkrequest_p.h +++ b/src/network/access/qhttpnetworkrequest_p.h @@ -65,7 +65,7 @@ public: qint64 contentLength() const override; void setContentLength(qint64 length) override; - QList<QPair<QByteArray, QByteArray> > header() const override; + QHttpHeaders header() const override; QByteArray headerField(QByteArrayView name, const QByteArray &defaultValue = QByteArray()) const override; void setHeaderField(const QByteArray &name, const QByteArray &data) override; void prependHeaderField(const QByteArray &name, const QByteArray &data); diff --git a/src/network/access/qhttpthreaddelegate_p.h b/src/network/access/qhttpthreaddelegate_p.h index 0d052e2450..38e9fb4d78 100644 --- a/src/network/access/qhttpthreaddelegate_p.h +++ b/src/network/access/qhttpthreaddelegate_p.h @@ -33,6 +33,7 @@ #include "private/qnoncontiguousbytedevice_p.h" #include "qnetworkaccessauthenticationmanager_p.h" #include <QtNetwork/private/http2protocol_p.h> +#include <QtNetwork/qhttpheaders.h> QT_REQUIRE_CONFIG(http); @@ -74,7 +75,7 @@ public: // outgoing, Retrieved in the synchronous HTTP case QByteArray synchronousDownloadData; - QList<QPair<QByteArray,QByteArray> > incomingHeaders; + QHttpHeaders incomingHeaders; int incomingStatusCode; QString incomingReasonPhrase; bool isPipeliningUsed; @@ -112,7 +113,7 @@ signals: #endif void socketStartedConnecting(); void requestSent(); - void downloadMetaData(const QList<QPair<QByteArray,QByteArray> > &, int, const QString &, bool, + void downloadMetaData(const QHttpHeaders &, int, const QString &, bool, QSharedPointer<char>, qint64, qint64, bool, bool); void downloadProgress(qint64, qint64); void downloadData(const QByteArray &); diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp index 53bc0f5283..84d9e08b91 100644 --- a/src/network/access/qnetworkreplyhttpimpl.cpp +++ b/src/network/access/qnetworkreplyhttpimpl.cpp @@ -1325,7 +1325,7 @@ void QNetworkReplyHttpImplPrivate::checkForRedirect(const int statusCode) } } -void QNetworkReplyHttpImplPrivate::replyDownloadMetaData(const QList<QPair<QByteArray,QByteArray> > &hm, +void QNetworkReplyHttpImplPrivate::replyDownloadMetaData(const QHttpHeaders &hm, int sc, const QString &rp, bool pu, QSharedPointer<char> db, qint64 contentLength, @@ -1364,7 +1364,7 @@ void QNetworkReplyHttpImplPrivate::replyDownloadMetaData(const QList<QPair<QByte const bool autoDecompress = request.rawHeader("accept-encoding").isEmpty(); const bool shouldDecompress = isCompressed && autoDecompress; // reconstruct the HTTP header - for (const auto &[key, originValue] : hm) { + for (const auto &[key, originValue] : hm.toListOfPairs()) { QByteArray value = q->rawHeader(key); // Reset any previous "location" header set in the reply. In case of diff --git a/src/network/access/qnetworkreplyhttpimpl_p.h b/src/network/access/qnetworkreplyhttpimpl_p.h index 11897d1420..e00c43bdb3 100644 --- a/src/network/access/qnetworkreplyhttpimpl_p.h +++ b/src/network/access/qnetworkreplyhttpimpl_p.h @@ -244,7 +244,7 @@ public: // From HTTP thread: void replyDownloadData(QByteArray); void replyFinished(); - void replyDownloadMetaData(const QList<QPair<QByteArray,QByteArray> > &, int, const QString &, + void replyDownloadMetaData(const QHttpHeaders &, int, const QString &, bool, QSharedPointer<char>, qint64, qint64, bool, bool); void replyDownloadProgressSlot(qint64,qint64); void httpAuthenticationRequired(const QHttpNetworkRequest &request, QAuthenticator *auth); diff --git a/src/network/kernel/qauthenticator.cpp b/src/network/kernel/qauthenticator.cpp index e2ad91f171..e42450d7e5 100644 --- a/src/network/kernel/qauthenticator.cpp +++ b/src/network/kernel/qauthenticator.cpp @@ -14,6 +14,7 @@ #include <qstring.h> #include <qdatetime.h> #include <qrandom.h> +#include <QtNetwork/qhttpheaders.h> #ifdef Q_OS_WIN #include <qmutex.h> @@ -444,14 +445,14 @@ static bool verifyDigestMD5(QByteArrayView value) return true; // assume it's ok if algorithm is not specified } -void QAuthenticatorPrivate::parseHttpResponse(const QList<QPair<QByteArray, QByteArray>> &values, +void QAuthenticatorPrivate::parseHttpResponse(const QHttpHeaders &headers, bool isProxy, QStringView host) { #if !QT_CONFIG(gssapi) Q_UNUSED(host); #endif - const auto search = isProxy ? - QByteArrayView("proxy-authenticate") : QByteArrayView("www-authenticate"); + const auto search = isProxy ? QHttpHeaders::WellKnownHeader::ProxyAuthenticate + : QHttpHeaders::WellKnownHeader::WWWAuthenticate; method = None; /* @@ -465,23 +466,21 @@ void QAuthenticatorPrivate::parseHttpResponse(const QList<QPair<QByteArray, QByt */ QByteArrayView headerVal; - for (const auto ¤t : values) { - if (current.first.compare(search, Qt::CaseInsensitive) != 0) - continue; - const QLatin1StringView str(current.second); + for (const auto ¤t : headers.values(search)) { + const QLatin1StringView str(current); if (method < Basic && str.startsWith("basic"_L1, Qt::CaseInsensitive)) { method = Basic; - headerVal = QByteArrayView(current.second).mid(6); + headerVal = QByteArrayView(current).mid(6); } else if (method < Ntlm && str.startsWith("ntlm"_L1, Qt::CaseInsensitive)) { method = Ntlm; - headerVal = QByteArrayView(current.second).mid(5); + headerVal = QByteArrayView(current).mid(5); } else if (method < DigestMd5 && str.startsWith("digest"_L1, Qt::CaseInsensitive)) { // Make sure the algorithm is actually MD5 before committing to it: - if (!verifyDigestMD5(QByteArrayView(current.second).sliced(7))) + if (!verifyDigestMD5(QByteArrayView(current).sliced(7))) continue; method = DigestMd5; - headerVal = QByteArrayView(current.second).mid(7); + headerVal = QByteArrayView(current).mid(7); } else if (method < Negotiate && str.startsWith("negotiate"_L1, Qt::CaseInsensitive)) { #if QT_CONFIG(sspi) || QT_CONFIG(gssapi) // if it's not supported then we shouldn't try to use it #if QT_CONFIG(gssapi) @@ -492,7 +491,7 @@ void QAuthenticatorPrivate::parseHttpResponse(const QList<QPair<QByteArray, QByt continue; #endif method = Negotiate; - headerVal = QByteArrayView(current.second).mid(10); + headerVal = QByteArrayView(current).mid(10); #endif } } diff --git a/src/network/kernel/qauthenticator_p.h b/src/network/kernel/qauthenticator_p.h index 7c4fe736b5..bc16139941 100644 --- a/src/network/kernel/qauthenticator_p.h +++ b/src/network/kernel/qauthenticator_p.h @@ -26,6 +26,7 @@ QT_BEGIN_NAMESPACE class QHttpResponseHeader; +class QHttpHeaders; #if QT_CONFIG(sspi) // SSPI class QSSPIWindowsHandles; #elif QT_CONFIG(gssapi) // GSSAPI @@ -80,8 +81,7 @@ public: static QHash<QByteArray, QByteArray> parseDigestAuthenticationChallenge(QByteArrayView challenge); - void parseHttpResponse(const QList<QPair<QByteArray, QByteArray>> &, bool isProxy, - QStringView host); + void parseHttpResponse(const QHttpHeaders &headers, bool isProxy, QStringView host); void updateCredentials(); static bool isMethodSupported(QByteArrayView method); diff --git a/src/network/socket/qhttpsocketengine.cpp b/src/network/socket/qhttpsocketengine.cpp index c339912240..a700023bd5 100644 --- a/src/network/socket/qhttpsocketengine.cpp +++ b/src/network/socket/qhttpsocketengine.cpp @@ -553,7 +553,8 @@ void QHttpSocketEngine::slotSocketReadNotification() d->authenticator.detach(); priv = QAuthenticatorPrivate::getPrivate(d->authenticator); - priv->parseHttpResponse(d->reply->header(), true, d->proxy.hostName()); + const auto headers = d->reply->header(); + priv->parseHttpResponse(headers, true, d->proxy.hostName()); if (priv->phase == QAuthenticatorPrivate::Invalid) { // problem parsing the reply diff --git a/tests/auto/network/access/hsts/tst_qhsts.cpp b/tests/auto/network/access/hsts/tst_qhsts.cpp index 97a2d2889e..d81bc3e3e3 100644 --- a/tests/auto/network/access/hsts/tst_qhsts.cpp +++ b/tests/auto/network/access/hsts/tst_qhsts.cpp @@ -9,6 +9,7 @@ #include <QtCore/qpair.h> #include <QtCore/qurl.h> +#include <QtNetwork/qhttpheaders.h> #include <QtNetwork/private/qhstsstore_p.h> #include <QtNetwork/private/qhsts_p.h> @@ -189,110 +190,108 @@ void tst_QHsts::testPolicyExpiration() void tst_QHsts::testSTSHeaderParser() { QHstsHeaderParser parser; - using Header = QPair<QByteArray, QByteArray>; - using Headers = QList<Header>; QVERIFY(!parser.includeSubDomains()); QVERIFY(!parser.expirationDate().isValid()); - Headers list; - QVERIFY(!parser.parse(list)); + QHttpHeaders headers; + QVERIFY(!parser.parse(headers)); QVERIFY(!parser.includeSubDomains()); QVERIFY(!parser.expirationDate().isValid()); - list << Header("Strict-Transport-security", "200"); - QVERIFY(!parser.parse(list)); + headers.append("Strict-Transport-security", "200"); + QVERIFY(!parser.parse(headers)); QVERIFY(!parser.includeSubDomains()); QVERIFY(!parser.expirationDate().isValid()); // This header is missing REQUIRED max-age directive, so we'll ignore it: - list << Header("Strict-Transport-Security", "includeSubDomains"); - QVERIFY(!parser.parse(list)); + headers.append("Strict-Transport-Security", "includeSubDomains"); + QVERIFY(!parser.parse(headers)); QVERIFY(!parser.includeSubDomains()); QVERIFY(!parser.expirationDate().isValid()); - list.pop_back(); - list << Header("Strict-Transport-Security", "includeSubDomains;max-age=1000"); - QVERIFY(parser.parse(list)); + headers.removeAt(headers.size() - 1); + headers.append("Strict-Transport-Security", "includeSubDomains;max-age=1000"); + QVERIFY(parser.parse(headers)); QVERIFY(parser.expirationDate() > QDateTime::currentDateTimeUtc()); QVERIFY(parser.includeSubDomains()); - list.pop_back(); - list << Header("strict-transport-security", "includeSubDomains;max-age=1000"); - QVERIFY(parser.parse(list)); + headers.removeAt(headers.size() - 1); + headers.append("strict-transport-security", "includeSubDomains;max-age=1000"); + QVERIFY(parser.parse(headers)); QVERIFY(parser.expirationDate() > QDateTime::currentDateTimeUtc()); QVERIFY(parser.includeSubDomains()); - list.pop_back(); + headers.removeAt(headers.size() - 1); // Invalid (includeSubDomains twice): - list << Header("Strict-Transport-Security", "max-age = 1000 ; includeSubDomains;includeSubDomains"); - QVERIFY(!parser.parse(list)); + headers.append("Strict-Transport-Security", "max-age = 1000 ; includeSubDomains;includeSubDomains"); + QVERIFY(!parser.parse(headers)); QVERIFY(!parser.includeSubDomains()); QVERIFY(!parser.expirationDate().isValid()); - list.pop_back(); + headers.removeAt(headers.size() - 1); // Invalid (weird number of seconds): - list << Header("Strict-Transport-Security", "max-age=-1000 ; includeSubDomains"); - QVERIFY(!parser.parse(list)); + headers.append("Strict-Transport-Security", "max-age=-1000 ; includeSubDomains"); + QVERIFY(!parser.parse(headers)); QVERIFY(!parser.includeSubDomains()); QVERIFY(!parser.expirationDate().isValid()); - list.pop_back(); + headers.removeAt(headers.size() - 1); // Note, directives are case-insensitive + we should ignore unknown directive. - list << Header("Strict-Transport-Security", ";max-age=1000 ;includesubdomains;;" + headers.append("Strict-Transport-Security", ";max-age=1000 ;includesubdomains;;" "nowsomeunknownheader=\"somevaluewithescapes\\;\""); - QVERIFY(parser.parse(list)); + QVERIFY(parser.parse(headers)); QVERIFY(parser.includeSubDomains()); QVERIFY(parser.expirationDate().isValid()); - list.pop_back(); + headers.removeAt(headers.size() - 1); // Check that we know how to unescape max-age: - list << Header("Strict-Transport-Security", "max-age=\"1000\""); - QVERIFY(parser.parse(list)); + headers.append("Strict-Transport-Security", "max-age=\"1000\""); + QVERIFY(parser.parse(headers)); QVERIFY(!parser.includeSubDomains()); QVERIFY(parser.expirationDate().isValid()); - list.pop_back(); + headers.removeAt(headers.size() - 1); // The only STS header, with invalid syntax though, to be ignored: - list << Header("Strict-Transport-Security", "max-age; max-age=15768000"); - QVERIFY(!parser.parse(list)); + headers.append("Strict-Transport-Security", "max-age; max-age=15768000"); + QVERIFY(!parser.parse(headers)); QVERIFY(!parser.includeSubDomains()); QVERIFY(!parser.expirationDate().isValid()); // Now we check that our parse chosses the first valid STS header and ignores // others: - list.clear(); - list << Header("Strict-Transport-Security", "includeSubdomains; max-age=\"hehehe\";"); - list << Header("Strict-Transport-Security", "max-age=10101"); - QVERIFY(parser.parse(list)); + headers.clear(); + headers.append("Strict-Transport-Security", "includeSubdomains; max-age=\"hehehe\";"); + headers.append("Strict-Transport-Security", "max-age=10101"); + QVERIFY(parser.parse(headers)); QVERIFY(!parser.includeSubDomains()); QVERIFY(parser.expirationDate().isValid()); - list.clear(); - list << Header("Strict-Transport-Security", "max-age=0"); - QVERIFY(parser.parse(list)); + headers.clear(); + headers.append("Strict-Transport-Security", "max-age=0"); + QVERIFY(parser.parse(headers)); QVERIFY(!parser.includeSubDomains()); QVERIFY(parser.expirationDate() <= QDateTime::currentDateTimeUtc()); // Parsing is case-insensitive: - list.pop_back(); - list << Header("Strict-Transport-Security", "Max-aGE=1000; InclUdesUbdomains"); - QVERIFY(parser.parse(list)); + headers.removeAt(headers.size() - 1); + headers.append("Strict-Transport-Security", "Max-aGE=1000; InclUdesUbdomains"); + QVERIFY(parser.parse(headers)); QVERIFY(parser.includeSubDomains()); QVERIFY(parser.expirationDate().isValid()); // Grammar of STS header is quite permissive, let's check we can parse // some weird but valid header: - list.pop_back(); - list << Header("Strict-Transport-Security", ";;; max-age = 17; ; ; ; ;;; ;;" + headers.removeAt(headers.size() - 1); + headers.append("Strict-Transport-Security", ";;; max-age = 17; ; ; ; ;;; ;;" ";;; ; includeSubdomains ;;thisIsUnknownDirective;;;;"); - QVERIFY(parser.parse(list)); + QVERIFY(parser.parse(headers)); QVERIFY(parser.includeSubDomains()); QVERIFY(parser.expirationDate().isValid()); - list.pop_back(); - list << Header("Strict-Transport-Security", "max-age=1000; includeSubDomains bogon"); - QVERIFY(!parser.parse(list)); + headers.removeAt(headers.size() - 1); + headers.append("Strict-Transport-Security", "max-age=1000; includeSubDomains bogon"); + QVERIFY(!parser.parse(headers)); QVERIFY(!parser.includeSubDomains()); QVERIFY(!parser.expirationDate().isValid()); } diff --git a/tests/auto/network/access/http2/http2srv.cpp b/tests/auto/network/access/http2/http2srv.cpp index d7c8c2fd8e..f98fed0322 100644 --- a/tests/auto/network/access/http2/http2srv.cpp +++ b/tests/auto/network/access/http2/http2srv.cpp @@ -377,16 +377,12 @@ bool Http2Server::verifyProtocolUpgradeRequest() bool settingsOk = false; QHttpNetworkReplyPrivate *firstRequestReader = protocolUpgradeHandler->d_func(); + const auto headers = firstRequestReader->headers(); // That's how we append them, that's what I expect to find: - for (const auto &header : firstRequestReader->headers()) { - if (header.first == "Connection") - connectionOk = header.second.contains("Upgrade, HTTP2-Settings"); - else if (header.first == "Upgrade") - upgradeOk = header.second.contains("h2c"); - else if (header.first == "HTTP2-Settings") - settingsOk = true; - } + connectionOk = headers.combinedValue(QHttpHeaders::WellKnownHeader::Connection).contains("Upgrade, HTTP2-Settings"); + upgradeOk = headers.combinedValue(QHttpHeaders::WellKnownHeader::Upgrade).contains("h2c"); + settingsOk = headers.contains("HTTP2-Settings"); return connectionOk && upgradeOk && settingsOk; } diff --git a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp index 099cf1b047..8cdd15a5f9 100644 --- a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp +++ b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp @@ -731,11 +731,11 @@ private: void parseContentLength() { - int index = receivedData.indexOf("Content-Length:"); + int index = receivedData.indexOf("content-length:"); if (index == -1) return; - index += sizeof("Content-Length:") - 1; + index += sizeof("content-length:") - 1; const auto end = std::find(receivedData.cbegin() + index, receivedData.cend(), '\r'); auto num = receivedData.mid(index, std::distance(receivedData.cbegin() + index, end)); bool ok; @@ -3451,7 +3451,7 @@ void tst_QNetworkReply::connectToIPv6Address() if (!QtNetworkSettings::hasIPv6()) QSKIP("system doesn't support ipv6!"); - QByteArray httpResponse = QByteArray("HTTP/1.0 200 OK\r\nContent-Length: "); + QByteArray httpResponse = QByteArray("HTTP/1.0 200 OK\r\ncontent-length: "); httpResponse += QByteArray::number(dataToSend.size()); httpResponse += "\r\n\r\n"; httpResponse += dataToSend; @@ -3466,7 +3466,7 @@ void tst_QNetworkReply::connectToIPv6Address() QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply)); QByteArray content = reply->readAll(); //qDebug() << server.receivedData; - QByteArray hostinfo = "\r\nHost: " + hostfield + ':' + QByteArray::number(server.serverPort()) + "\r\n"; + QByteArray hostinfo = "\r\nhost: " + hostfield + ':' + QByteArray::number(server.serverPort()) + "\r\n"; QVERIFY(server.receivedData.contains(hostinfo)); QCOMPARE(content, dataToSend); QCOMPARE(reply->url(), request.url()); @@ -6275,7 +6275,7 @@ void tst_QNetworkReply::httpProxyCommands() manager.setProxy(proxy); QNetworkRequest request(url); - request.setRawHeader("User-Agent", "QNetworkReplyAutoTest/1.0"); + request.setRawHeader("user-agent", "QNetworkReplyAutoTest/1.0"); QNetworkReplyPtr reply(manager.get(request)); // wait for the finished signal @@ -6292,10 +6292,11 @@ void tst_QNetworkReply::httpProxyCommands() QCOMPARE(receivedHeader, expectedCommand); //QTBUG-17223 - make sure the user agent from the request is sent to proxy server even for CONNECT - int uapos = proxyServer.receivedData.indexOf("User-Agent"); + const QByteArray cUserAgent = "user-agent: "; + int uapos = proxyServer.receivedData.toLower().indexOf(cUserAgent) + cUserAgent.size(); int uaend = proxyServer.receivedData.indexOf("\r\n", uapos); QByteArray uaheader = proxyServer.receivedData.mid(uapos, uaend - uapos); - QCOMPARE(uaheader, QByteArray("User-Agent: QNetworkReplyAutoTest/1.0")); + QCOMPARE(uaheader, QByteArray("QNetworkReplyAutoTest/1.0")); } class ProxyChangeHelper : public QObject @@ -8513,7 +8514,7 @@ void tst_QNetworkReply::httpUserAgent() QVERIFY(reply->isFinished()); QCOMPARE(reply->error(), QNetworkReply::NoError); - QVERIFY(server.receivedData.contains("\r\nUser-Agent: abcDEFghi\r\n")); + QVERIFY(server.receivedData.contains("\r\nuser-agent: abcDEFghi\r\n")); } void tst_QNetworkReply::synchronousAuthenticationCache() @@ -8533,7 +8534,7 @@ void tst_QNetworkReply::synchronousAuthenticationCache() "Content-Type: text/plain\r\n" "\r\n" "auth"; - QRegularExpression rx("Authorization: Basic ([^\r\n]*)\r\n"); + QRegularExpression rx("authorization: Basic ([^\r\n]*)\r\n"); QRegularExpressionMatch match = rx.match(receivedData); if (match.hasMatch()) { if (QByteArray::fromBase64(match.captured(1).toLatin1()) == "login:password") { @@ -9190,7 +9191,7 @@ void tst_QNetworkReply::ioHttpCookiesDuringRedirect() manager.setRedirectPolicy(oldRedirectPolicy); QVERIFY(waitForFinish(reply) == Success); - QVERIFY(target.receivedData.contains("\r\nCookie: hello=world\r\n")); + QVERIFY(target.receivedData.contains("\r\ncookie: hello=world\r\n")); QVERIFY(validateRedirectedResponseHeaders(reply)); } @@ -10081,7 +10082,7 @@ void tst_QNetworkReply::contentEncoding() QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort()))); if (!decompress) { // This disables decompression of the received content: - request.setRawHeader("Accept-Encoding", QLatin1String("%1").arg(encoding).toLatin1()); + request.setRawHeader("accept-encoding", QLatin1String("%1").arg(encoding).toLatin1()); // This disables the zerocopy optimization request.setAttribute(QNetworkRequest::MaximumDownloadBufferSizeAttribute, 0); } @@ -10093,7 +10094,7 @@ void tst_QNetworkReply::contentEncoding() { // Check that we included the content encoding method in our Accept-Encoding header const QByteArray &receivedData = server.receivedData; - int start = receivedData.indexOf("Accept-Encoding"); + int start = receivedData.indexOf("accept-encoding"); QVERIFY(start != -1); int end = receivedData.indexOf("\r\n", start); QVERIFY(end != -1); diff --git a/tests/auto/network/access/qrestaccessmanager/httptestserver.cpp b/tests/auto/network/access/qrestaccessmanager/httptestserver.cpp index 089342ff8b..00995920d5 100644 --- a/tests/auto/network/access/qrestaccessmanager/httptestserver.cpp +++ b/tests/auto/network/access/qrestaccessmanager/httptestserver.cpp @@ -67,8 +67,9 @@ void HttpTestServer::handleDataAvailable() Q_ASSERT(m_handler); Q_ASSERT(state == State::AllDone); - if (m_request.headers.contains("Host")) { - const auto parts = m_request.headers["Host"].split(':'); + if (auto values = m_request.headers.values( + QHttpHeaders::WellKnownHeader::Host); !values.empty()) { + const auto parts = values.first().split(':'); m_request.url.setHost(parts.at(0)); if (parts.size() == 2) m_request.url.setPort(parts.at(1).toUInt()); @@ -83,8 +84,9 @@ void HttpTestServer::handleDataAvailable() responseMessage += QByteArray::number(response.status); responseMessage += CRLF; // Insert headers if any - for (const auto &[name,value] : response.headers.asKeyValueRange()) { + for (const auto &[name,value] : response.headers.toListOfPairs()) { responseMessage += name; + responseMessage += ": "; responseMessage += value; responseMessage += CRLF; } @@ -236,7 +238,7 @@ bool HttpTestServer::readHeaders(QTcpSocket *socket) QByteArray key = fragment.sliced(0, index).trimmed(); QByteArray value = fragment.sliced(index + 1).trimmed(); - m_request.headers.insert(std::move(key), std::move(value)); + m_request.headers.append(key, value); fragment.clear(); } } @@ -247,9 +249,10 @@ bool HttpTestServer::readHeaders(QTcpSocket *socket) bool HttpTestServer::readBody(QTcpSocket *socket) { qint64 bytesLeft = 0; - if (m_request.headers.contains("Content-Length")) { + if (auto values = m_request.headers.values( + QHttpHeaders::WellKnownHeader::ContentLength); !values.empty()) { bool conversionResult; - bytesLeft = m_request.headers["Content-Length"].toInt(&conversionResult); + bytesLeft = values.first().toInt(&conversionResult); if (!conversionResult) return false; fragment.resize(bytesLeft); diff --git a/tests/auto/network/access/qrestaccessmanager/httptestserver_p.h b/tests/auto/network/access/qrestaccessmanager/httptestserver_p.h index 1498c4bdb7..ead6590a55 100644 --- a/tests/auto/network/access/qrestaccessmanager/httptestserver_p.h +++ b/tests/auto/network/access/qrestaccessmanager/httptestserver_p.h @@ -5,6 +5,7 @@ #define QRESTACCESSSMANAGER_HTTPTESTSERVER_P_H #include <QtNetwork/qtcpserver.h> +#include <QtNetwork/qhttpheaders.h> #include <QtCore/qmap.h> #include <QtCore/qurl.h> @@ -18,7 +19,7 @@ struct HttpData { QByteArray method; quint16 port = 0; QPair<quint8, quint8> version; - QMap<QByteArray, QByteArray> headers; + QHttpHeaders headers; }; struct ResponseControl diff --git a/tests/auto/network/access/qrestaccessmanager/tst_qrestaccessmanager.cpp b/tests/auto/network/access/qrestaccessmanager/tst_qrestaccessmanager.cpp index b619f3fcd5..d1018952bd 100644 --- a/tests/auto/network/access/qrestaccessmanager/tst_qrestaccessmanager.cpp +++ b/tests/auto/network/access/qrestaccessmanager/tst_qrestaccessmanager.cpp @@ -22,6 +22,8 @@ using namespace Qt::StringLiterals; using namespace std::chrono_literals; +using Header = QHttpHeaders::WellKnownHeader; + class tst_QRestAccessManager : public QObject { Q_OBJECT @@ -533,9 +535,9 @@ void tst_QRestAccessManager::authentication() HttpData serverSideRequest; server.setHandler([&](HttpData request, HttpData &response, ResponseControl&) { - if (!request.headers.contains("Authorization"_ba)) { + if (!request.headers.contains(Header::Authorization)) { response.status = 401; - response.headers.insert("WWW-Authenticate: "_ba, "Basic realm=\"secret_place\""_ba); + response.headers.append(Header::WWWAuthenticate, "Basic realm=\"secret_place\""_ba); } else { response.status = 200; } @@ -557,7 +559,9 @@ void tst_QRestAccessManager::authentication() QTRY_VERIFY(replyFromServer); // Server and QRestAM/QNAM exchange req/res twice, but finished() should be emitted just once QCOMPARE(finishedCount, 1); - QCOMPARE(serverSideRequest.headers["Authorization"_ba], "Basic YV91c2VyOmFfcGFzc3dvcmQ="_ba); + const auto resultHeaders = serverSideRequest.headers.values(Header::Authorization); + QVERIFY(!resultHeaders.empty()); + QCOMPARE(resultHeaders.first(), "Basic YV91c2VyOmFfcGFzc3dvcmQ="_ba); } void tst_QRestAccessManager::userInfo() @@ -576,9 +580,9 @@ void tst_QRestAccessManager::userInfo() HttpData serverSideRequest; server.setHandler([&](HttpData request, HttpData& response, ResponseControl&) { - if (!request.headers.contains("Authorization"_ba)) { + if (!request.headers.contains(Header::Authorization)) { response.status = 401; - response.headers.insert("WWW-Authenticate: "_ba, "Basic realm=\"secret_place\""_ba); + response.headers.append(Header::WWWAuthenticate,"Basic realm=\"secret_place\""_ba); } else { response.status = 200; } @@ -589,7 +593,9 @@ void tst_QRestAccessManager::userInfo() QTRY_VERIFY(reply.get()->isFinished()); QVERIFY(reply.get()->isSuccess()); QCOMPARE(reply.get()->httpStatus(), 200); - QCOMPARE(serverSideRequest.headers["Authorization"_ba], "Basic YV91c2VyOmFfcGFzc3dvcmQ="_ba); + const auto resultHeaders = serverSideRequest.headers.values(Header::Authorization); + QVERIFY(!resultHeaders.empty()); + QCOMPARE(resultHeaders.first(), "Basic YV91c2VyOmFfcGFzc3dvcmQ="_ba); // Verify that debug output does not contain password QString debugOutput; @@ -864,33 +870,38 @@ void tst_QRestAccessManager::text() // QString from text() should match with the original (UTF-16) QString. // Successful UTF-8 - serverSideResponse.headers.insert("Content-Type:"_ba, "text/plain; charset=UTF-8"_ba); + serverSideResponse.headers.append(Header::ContentType, "text/plain; charset=UTF-8"_ba); serverSideResponse.body = encUTF8(sourceString); VERIFY_TEXT_REPLY_OK; // Successful UTF-16 - serverSideResponse.headers.insert("Content-Type:"_ba, "text/plain; charset=UTF-16"_ba); + serverSideResponse.headers.removeAll(Header::ContentType); + serverSideResponse.headers.append(Header::ContentType, "text/plain; charset=UTF-16"_ba); serverSideResponse.body = encUTF16(sourceString); VERIFY_TEXT_REPLY_OK; // Successful UTF-16, parameter case insensitivity - serverSideResponse.headers.insert("Content-Type:"_ba, "text/plain; chARset=uTf-16"_ba); + serverSideResponse.headers.removeAll(Header::ContentType); + serverSideResponse.headers.append(Header::ContentType, "text/plain; chARset=uTf-16"_ba); serverSideResponse.body = encUTF16(sourceString); VERIFY_TEXT_REPLY_OK; // Successful UTF-32 - serverSideResponse.headers.insert("Content-Type:"_ba, "text/plain; charset=UTF-32"_ba); + serverSideResponse.headers.removeAll(Header::ContentType); + serverSideResponse.headers.append(Header::ContentType, "text/plain; charset=UTF-32"_ba); serverSideResponse.body = encUTF32(sourceString); VERIFY_TEXT_REPLY_OK; // Successful UTF-32 with spec-wise allowed extra content in the Content-Type header value - serverSideResponse.headers.insert("Content-Type:"_ba, + serverSideResponse.headers.removeAll(Header::ContentType); + serverSideResponse.headers.append(Header::ContentType, "text/plain; charset = \"UTF-32\";extraparameter=bar"_ba); serverSideResponse.body = encUTF32(sourceString); VERIFY_TEXT_REPLY_OK; // Unsuccessful UTF-32, wrong encoding indicated (indicated charset UTF-32 but data is UTF-8) - serverSideResponse.headers.insert("Content-Type:"_ba, "text/plain; charset=UTF-32"_ba); + serverSideResponse.headers.removeAll(Header::ContentType); + serverSideResponse.headers.append(Header::ContentType, "text/plain; charset=UTF-32"_ba); serverSideResponse.body = encUTF8(sourceString); manager.get(request, this, [&](QRestReply *reply) { replyFromServer = reply; }); QTRY_VERIFY(replyFromServer); @@ -900,12 +911,14 @@ void tst_QRestAccessManager::text() replyFromServer = nullptr; // Unsupported encoding - serverSideResponse.headers.insert("Content-Type:"_ba, "text/plain; charset=foo"_ba); + serverSideResponse.headers.removeAll(Header::ContentType); + serverSideResponse.headers.append(Header::ContentType, "text/plain; charset=foo"_ba); serverSideResponse.body = encUTF8(sourceString); VERIFY_TEXT_REPLY_ERROR("text(): Charset \"foo\" is not supported") // Broken UTF-8 - serverSideResponse.headers.insert("Content-Type:"_ba, "text/plain; charset=UTF-8"_ba); + serverSideResponse.headers.removeAll(Header::ContentType); + serverSideResponse.headers.append(Header::ContentType, "text/plain; charset=UTF-8"_ba); serverSideResponse.body = "\xF0\x28\x8C\x28\xA0\xB0\xC0\xD0"; // invalid characters VERIFY_TEXT_REPLY_ERROR("text() Decoding error occurred"); } @@ -925,7 +938,8 @@ void tst_QRestAccessManager::textStreaming() ResponseControl *responseControl = nullptr; HttpData serverSideResponse; // The response data the server responds with - serverSideResponse.headers.insert("Content-Type:"_ba, "text/plain; charset=UTF-8"_ba); + serverSideResponse.headers.removeAll(Header::ContentType); + serverSideResponse.headers.append(Header::ContentType, "text/plain; charset=UTF-8"_ba); serverSideResponse.body = encUTF8(expectedData); serverSideResponse.status = 200; @@ -983,7 +997,7 @@ void tst_QRestAccessManager::download() ResponseControl *responseControl = nullptr; serverSideResponse.status = 200; // Set content-length header so that underlying QNAM is able to report bytesTotal correctly - serverSideResponse.headers.insert("Content-Length: ", + serverSideResponse.headers.append(Header::ContentType, QString::number(expectedData.size()).toLatin1()); server.setHandler([&](HttpData, HttpData &response, ResponseControl &control) { response = serverSideResponse; diff --git a/tests/auto/network/kernel/qauthenticator/tst_qauthenticator.cpp b/tests/auto/network/kernel/qauthenticator/tst_qauthenticator.cpp index 744dabd3da..5dfd652322 100644 --- a/tests/auto/network/kernel/qauthenticator/tst_qauthenticator.cpp +++ b/tests/auto/network/kernel/qauthenticator/tst_qauthenticator.cpp @@ -6,6 +6,7 @@ #include <QTest> #include <QtCore/QCoreApplication> #include <QtNetwork/QAuthenticator> +#include <QtNetwork/QHttpHeaders> #include <private/qauthenticator_p.h> @@ -60,8 +61,8 @@ void tst_QAuthenticator::basicAuth() QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(auth); QCOMPARE(priv->phase, QAuthenticatorPrivate::Start); - QList<QPair<QByteArray, QByteArray> > headers; - headers << qMakePair(QByteArray("WWW-Authenticate"), "Basic " + data.toUtf8()); + QHttpHeaders headers; + headers.append(QByteArray("WWW-Authenticate"), "Basic " + data.toUtf8()); priv->parseHttpResponse(headers, /*isProxy = */ false, {}); QCOMPARE(auth.realm(), realm); @@ -103,13 +104,13 @@ void tst_QAuthenticator::ntlmAuth() QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(auth); QCOMPARE(priv->phase, QAuthenticatorPrivate::Start); - QList<QPair<QByteArray, QByteArray> > headers; + QHttpHeaders headers; // NTLM phase 1: negotiate // This phase of NTLM contains no information, other than what we're willing to negotiate // Current implementation uses flags: // NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_REQUEST_TARGET - headers << qMakePair(QByteArrayLiteral("WWW-Authenticate"), QByteArrayLiteral("NTLM")); + headers.append(QByteArrayLiteral("WWW-Authenticate"), QByteArrayLiteral("NTLM")); priv->parseHttpResponse(headers, /*isProxy = */ false, {}); if (sso) QVERIFY(priv->calculateResponse("GET", "/", u"").startsWith("NTLM ")); @@ -118,7 +119,7 @@ void tst_QAuthenticator::ntlmAuth() // NTLM phase 2: challenge headers.clear(); - headers << qMakePair(QByteArray("WWW-Authenticate"), "NTLM " + data.toUtf8()); + headers.append(QByteArray("WWW-Authenticate"), "NTLM " + data.toUtf8()); priv->parseHttpResponse(headers, /*isProxy = */ false, {}); QEXPECT_FAIL("with-realm", "NTLM authentication code doesn't extract the realm", Continue); @@ -143,10 +144,10 @@ void tst_QAuthenticator::sha256AndMd5Digest() QVERIFY(priv->isMethodSupported("digest")); // sanity check QCOMPARE(priv->phase, QAuthenticatorPrivate::Start); - QList<QPair<QByteArray, QByteArray>> headers; + QHttpHeaders headers; // Put sha256 first, so that its parsed first... - headers.emplace_back("WWW-Authenticate", sha256); - headers.emplace_back("WWW-Authenticate", md5); + headers.append("WWW-Authenticate", sha256); + headers.append("WWW-Authenticate", md5); priv->parseHttpResponse(headers, false, QString()); QByteArray response = priv->calculateResponse("GET", "/index", {}); |