From fa456ad5501dc74446329222d12ba0fa2ecd0457 Mon Sep 17 00:00:00 2001 From: Lena Biliaieva Date: Fri, 8 Mar 2024 18:10:02 +0100 Subject: [PATCH] Add QHttpHeaders to QNetworkRequest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added headers() and setHeaders() methods to QNetworkRequest. [ChangeLog][QtNetwork][QNetworkRequest] Added headers() and setHeaders() methods to QNetworkRequest, which provide an interface to work with QHttpHeaders. Task-number: QTBUG-107751 Change-Id: I2e1dc7cb2efab5903eb7ff23b75d01aefe13273d Reviewed-by: MÃ¥rten Nordheim --- src/network/CMakeLists.txt | 1 + src/network/access/qhttpheaders.h | 3 +- src/network/access/qhttpheadershelper.cpp | 25 ++++++ src/network/access/qhttpheadershelper_p.h | 30 ++++++++ src/network/access/qnetworkrequest.cpp | 66 ++++++++++++++++ src/network/access/qnetworkrequest.h | 5 ++ src/network/access/qnetworkrequest_p.h | 9 +++ tests/auto/network/access/CMakeLists.txt | 1 + .../access/qhttpheadershelper/CMakeLists.txt | 15 ++++ .../tst_qhttpheadershelper.cpp | 76 +++++++++++++++++++ .../qnetworkrequest/tst_qnetworkrequest.cpp | 29 +++++++ 11 files changed, 259 insertions(+), 1 deletion(-) create mode 100644 src/network/access/qhttpheadershelper.cpp create mode 100644 src/network/access/qhttpheadershelper_p.h create mode 100644 tests/auto/network/access/qhttpheadershelper/CMakeLists.txt create mode 100644 tests/auto/network/access/qhttpheadershelper/tst_qhttpheadershelper.cpp diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt index 3f226a77dc..e977400245 100644 --- a/src/network/CMakeLists.txt +++ b/src/network/CMakeLists.txt @@ -22,6 +22,7 @@ qt_internal_add_module(Network access/qnetworkfile.cpp access/qnetworkfile_p.h access/qhttpheaders.cpp access/qhttpheaders.h access/qhttpheaderparser.cpp access/qhttpheaderparser_p.h + access/qhttpheadershelper.cpp access/qhttpheadershelper_p.h access/qnetworkreply.cpp access/qnetworkreply.h access/qnetworkreply_p.h access/qnetworkreplydataimpl.cpp access/qnetworkreplydataimpl_p.h access/qnetworkreplyfileimpl.cpp access/qnetworkreplyfileimpl_p.h diff --git a/src/network/access/qhttpheaders.h b/src/network/access/qhttpheaders.h index 97dc415e55..260df1421b 100644 --- a/src/network/access/qhttpheaders.h +++ b/src/network/access/qhttpheaders.h @@ -4,7 +4,8 @@ #ifndef QHTTPHEADERS_H #define QHTTPHEADERS_H -#include +#include +#include #include #include diff --git a/src/network/access/qhttpheadershelper.cpp b/src/network/access/qhttpheadershelper.cpp new file mode 100644 index 0000000000..d3cc9e439f --- /dev/null +++ b/src/network/access/qhttpheadershelper.cpp @@ -0,0 +1,25 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qhttpheadershelper_p.h" + +#include + +QT_BEGIN_NAMESPACE + +bool QHttpHeadersHelper::compareStrict(const QHttpHeaders &left, const QHttpHeaders &right) +{ + if (left.size() != right.size()) + return false; + + for (qsizetype i = 0; i < left.size(); ++i) { + if (left.nameAt(i) != right.nameAt(i)) + return false; + if (left.valueAt(i) != right.valueAt(i)) + return false; + } + + return true; +} + +QT_END_NAMESPACE diff --git a/src/network/access/qhttpheadershelper_p.h b/src/network/access/qhttpheadershelper_p.h new file mode 100644 index 0000000000..d1e38a1a8e --- /dev/null +++ b/src/network/access/qhttpheadershelper_p.h @@ -0,0 +1,30 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QHTTPHEADERSHELPER_H +#define QHTTPHEADERSHELPER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the Network Access API. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include + +QT_BEGIN_NAMESPACE + +class QHttpHeaders; + +namespace QHttpHeadersHelper { + Q_NETWORK_EXPORT bool compareStrict(const QHttpHeaders &left, const QHttpHeaders &right); +}; + +QT_END_NAMESPACE + +#endif // QHTTPHEADERSHELPER_H diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp index 6f5a7ff19a..ad83dd38e3 100644 --- a/src/network/access/qnetworkrequest.cpp +++ b/src/network/access/qnetworkrequest.cpp @@ -6,6 +6,7 @@ #include "qplatformdefs.h" #include "qnetworkcookie.h" #include "qsslconfiguration.h" +#include "qhttpheadershelper_p.h" #if QT_CONFIG(http) #include "qhttp1configuration.h" #include "qhttp2configuration.h" @@ -475,6 +476,7 @@ public: && decompressedSafetyCheckThreshold == other.decompressedSafetyCheckThreshold #endif && transferTimeout == other.transferTimeout + && QHttpHeadersHelper::compareStrict(httpHeaders, other.httpHeaders) ; // don't compare cookedHeaders } @@ -600,6 +602,43 @@ void QNetworkRequest::setUrl(const QUrl &url) d->url = url; } +/*! + \since 6.8 + + Returns headers that are set in this network request. + + \sa setHeaders() +*/ +QHttpHeaders QNetworkRequest::headers() const +{ + return d->headers(); +} + +/*! + \since 6.8 + + Sets \a newHeaders as headers in this network request, overriding + any previously set headers. + + If some headers correspond to the known headers, the values will + be parsed and the corresponding parsed form will also be set. + + \sa headers(), KnownHeaders +*/ +void QNetworkRequest::setHeaders(QHttpHeaders &&newHeaders) +{ + d->setHeaders(std::move(newHeaders)); +} + +/*! + \overload + \since 6.8 +*/ +void QNetworkRequest::setHeaders(const QHttpHeaders &newHeaders) +{ + d->setHeaders(newHeaders); +} + /*! Returns the value of the known network header \a header if it is present in this request. If it is not present, returns QVariant() @@ -1401,6 +1440,33 @@ void QNetworkHeadersPrivate::setCookedHeader(QNetworkRequest::KnownHeaders heade } } +QHttpHeaders QNetworkHeadersPrivate::headers() const +{ + return httpHeaders; +} + +void QNetworkHeadersPrivate::setHeaders(const QHttpHeaders &newHeaders) +{ + httpHeaders = newHeaders; +} + +void QNetworkHeadersPrivate::setHeaders(QHttpHeaders &&newHeaders) +{ + httpHeaders = std::move(newHeaders); +} + +void QNetworkHeadersPrivate::setHeader(QHttpHeaders::WellKnownHeader name, QByteArrayView value) +{ + httpHeaders.replaceOrAppend(name, value); +} + +void QNetworkHeadersPrivate::clearHeaders() +{ + httpHeaders.clear(); + rawHeaders.clear(); + cookedHeaders.clear(); +} + void QNetworkHeadersPrivate::setRawHeaderInternal(const QByteArray &key, const QByteArray &value) { auto firstEqualsKey = [&key](const RawHeaderPair &header) { diff --git a/src/network/access/qnetworkrequest.h b/src/network/access/qnetworkrequest.h index 3ca61a2ee3..875c787673 100644 --- a/src/network/access/qnetworkrequest.h +++ b/src/network/access/qnetworkrequest.h @@ -5,6 +5,7 @@ #define QNETWORKREQUEST_H #include +#include #include #include #include @@ -119,6 +120,10 @@ public: QUrl url() const; void setUrl(const QUrl &url); + QHttpHeaders headers() const; + void setHeaders(const QHttpHeaders &newHeaders); + void setHeaders(QHttpHeaders &&newHeaders); + // "cooked" headers QVariant header(KnownHeaders header) const; void setHeader(KnownHeaders header, const QVariant &value); diff --git a/src/network/access/qnetworkrequest_p.h b/src/network/access/qnetworkrequest_p.h index 48fcdcf1ed..88fb8cb246 100644 --- a/src/network/access/qnetworkrequest_p.h +++ b/src/network/access/qnetworkrequest_p.h @@ -16,6 +16,7 @@ // #include +#include #include "qnetworkrequest.h" #include "QtCore/qbytearray.h" #include "QtCore/qlist.h" @@ -36,6 +37,7 @@ public: typedef QHash AttributesMap; RawHeadersList rawHeaders; + QHttpHeaders httpHeaders; CookedHeadersMap cookedHeaders; AttributesMap attributes; QPointer originatingObject; @@ -47,6 +49,13 @@ public: void setAllRawHeaders(const RawHeadersList &list); void setCookedHeader(QNetworkRequest::KnownHeaders header, const QVariant &value); + QHttpHeaders headers() const; + void setHeaders(const QHttpHeaders &newHeaders); + void setHeaders(QHttpHeaders &&newHeaders); + void setHeader(QHttpHeaders::WellKnownHeader name, QByteArrayView value); + + void clearHeaders(); + static QDateTime fromHttpDate(const QByteArray &value); static QByteArray toHttpDate(const QDateTime &dt); diff --git a/tests/auto/network/access/CMakeLists.txt b/tests/auto/network/access/CMakeLists.txt index 44b7d5c1bb..13703cf89c 100644 --- a/tests/auto/network/access/CMakeLists.txt +++ b/tests/auto/network/access/CMakeLists.txt @@ -15,6 +15,7 @@ add_subdirectory(qrestaccessmanager) if(QT_FEATURE_private_tests) add_subdirectory(qhttp2connection) add_subdirectory(qhttpheaderparser) + add_subdirectory(qhttpheadershelper) add_subdirectory(qhttpnetworkconnection) add_subdirectory(qhttpnetworkreply) add_subdirectory(hpack) diff --git a/tests/auto/network/access/qhttpheadershelper/CMakeLists.txt b/tests/auto/network/access/qhttpheadershelper/CMakeLists.txt new file mode 100644 index 0000000000..75935d2844 --- /dev/null +++ b/tests/auto/network/access/qhttpheadershelper/CMakeLists.txt @@ -0,0 +1,15 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qhttpheadershelper LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qhttpheadershelper + SOURCES + tst_qhttpheadershelper.cpp + LIBRARIES + Qt::NetworkPrivate +) diff --git a/tests/auto/network/access/qhttpheadershelper/tst_qhttpheadershelper.cpp b/tests/auto/network/access/qhttpheadershelper/tst_qhttpheadershelper.cpp new file mode 100644 index 0000000000..b204d0cbe3 --- /dev/null +++ b/tests/auto/network/access/qhttpheadershelper/tst_qhttpheadershelper.cpp @@ -0,0 +1,76 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include +#include + +#include + +using namespace Qt::StringLiterals; + +class tst_QHttpHeadersHelper : public QObject +{ + Q_OBJECT + +private slots: + void testCompareStrict(); + +private: + static constexpr QAnyStringView n1{"name1"}; + static constexpr QAnyStringView n2{"name2"}; + static constexpr QAnyStringView v1{"value1"}; + static constexpr QAnyStringView v2{"value2"}; + static constexpr QAnyStringView N1{"NAME1"}; + static constexpr QAnyStringView N2{"NAME2"}; + static constexpr QAnyStringView V1{"VALUE1"}; + static constexpr QAnyStringView V2{"VALUE2"}; +}; + +void tst_QHttpHeadersHelper::testCompareStrict() +{ + using namespace QHttpHeadersHelper; + + // Basic comparisons + QHttpHeaders h1; + QHttpHeaders h2; + QVERIFY(compareStrict(h1, h2)); // empties + h1.append(n1, v1); + QVERIFY(compareStrict(h1, h1)); // self + h2.append(n1, v1); + QVERIFY(compareStrict(h1, h2)); + h1.append(n2, v2); + QVERIFY(!compareStrict(h1, h2)); + h1.removeAll(n2); + QVERIFY(compareStrict(h1, h2)); + + // Order-sensitivity + h1.clear(); + h2.clear(); + // Same headers but in different order + h1.append(n1, v1); + h1.append(n2, v2); + h2.append(n2, v2); + h2.append(n1, v1); + QVERIFY(!compareStrict(h1, h2)); + + // Different number of headers + h1.clear(); + h2.clear(); + h1.append(n1, v1); + h2.append(n1, v1); + h2.append(n2, v2); + QVERIFY(!compareStrict(h1, h2)); + + // Same header name, multiple values + h1.clear(); + h2.clear(); + h1.append(n1, v1); + h1.append(n1, v2); + h2.append(n1, v1); + QVERIFY(!compareStrict(h1, h2)); + h2.append(n1, v2); + QVERIFY(compareStrict(h1, h2)); +} + +QTEST_MAIN(tst_QHttpHeadersHelper) +#include "tst_qhttpheadershelper.moc" diff --git a/tests/auto/network/access/qnetworkrequest/tst_qnetworkrequest.cpp b/tests/auto/network/access/qnetworkrequest/tst_qnetworkrequest.cpp index bdef1115dd..ed978e6b0f 100644 --- a/tests/auto/network/access/qnetworkrequest/tst_qnetworkrequest.cpp +++ b/tests/auto/network/access/qnetworkrequest/tst_qnetworkrequest.cpp @@ -31,6 +31,8 @@ private slots: void rawHeaderParsing_data(); void rawHeaderParsing(); void originatingObject(); + void setHeaders_data(); + void setHeaders(); void removeHeader(); }; @@ -552,5 +554,32 @@ void tst_QNetworkRequest::originatingObject() QVERIFY(!request.originatingObject()); } +void tst_QNetworkRequest::setHeaders_data() +{ + QTest::addColumn("h"); + QTest::newRow("null") << QHttpHeaders(); + QHttpHeaders headers; + headers.append("name1", "value1"); + QTest::newRow("valid") << headers; +} + +void tst_QNetworkRequest::setHeaders() +{ + QFETCH(QHttpHeaders, h); + + QNetworkRequest r1; + + auto result = r1.headers(); + QVERIFY(result.isEmpty()); + + r1.setHeaders(h); + QCOMPARE(r1.headers().toListOfPairs(), h.toListOfPairs()); + + QNetworkRequest r2; + auto tmp = h; + r2.setHeaders(std::move(tmp)); + QCOMPARE(r2.headers().toListOfPairs(), h.toListOfPairs()); +} + QTEST_MAIN(tst_QNetworkRequest) #include "tst_qnetworkrequest.moc"