summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLena Biliaieva <[email protected]>2024-03-14 22:48:25 +0200
committerMagdalena Stojek <[email protected]>2025-03-27 13:05:22 +0100
commited341fcdd18fb4bb58ea4fa9df0be6350a81578c (patch)
tree4ac2c1cca331058054e70a86f6eded1f04a032d7
parent23fd2cfb015226d6894b9cbe5fa50b43f0134e99 (diff)
Add QHttpHeaders convenience methods for converting to known types
Introduce new methods in QHttpHeaders to convert header values to common types, including integers and date-times. These functions return std::optional<T> to indicate conversion success or failure. During the porting of QtNetwork internals to QHttpHeaders (specifically replacing cooked headers with QHttpHeaders in QNetworkRequest), these methods proved to be necessary. [ChangeLog][QtNetwork][QHttpHeaders] Added convenience methods for parsing header values to integers and date-times. Task-number: QTBUG-124766 Change-Id: I95cc8c29c522b23b027d5a79beb9b882aeffa105 Reviewed-by: Juha Vuolle <[email protected]> Reviewed-by: Marc Mutz <[email protected]>
-rw-r--r--src/network/access/qhttpheaders.cpp181
-rw-r--r--src/network/access/qhttpheaders.h18
-rw-r--r--tests/auto/network/access/qhttpheaders/tst_qhttpheaders.cpp87
3 files changed, 286 insertions, 0 deletions
diff --git a/src/network/access/qhttpheaders.cpp b/src/network/access/qhttpheaders.cpp
index ec109df5c3a..3786a17ae23 100644
--- a/src/network/access/qhttpheaders.cpp
+++ b/src/network/access/qhttpheaders.cpp
@@ -3,6 +3,8 @@
#include "qhttpheaders.h"
+#include <QtNetwork/private/qnetworkrequest_p.h>
+
#include <private/qoffsetstringarray_p.h>
#include <QtCore/qcompare.h>
@@ -11,6 +13,7 @@
#include <QtCore/qmap.h>
#include <QtCore/qset.h>
#include <QtCore/qttypetraits.h>
+#include <QtCore/qxpfunctional.h>
#include <q20algorithm.h>
#include <string_view>
@@ -796,6 +799,9 @@ public:
void combinedValue(const HeaderName &name, QByteArray &result) const;
void values(const HeaderName &name, QList<QByteArray> &result) const;
QByteArrayView value(const HeaderName &name, QByteArrayView defaultValue) const noexcept;
+ void forEachHeader(QAnyStringView name,
+ qxp::function_ref<void(QByteArrayView)> yield);
+ std::optional<QByteArrayView> findValue(const HeaderName &name) const noexcept;
QList<Header> headers;
};
@@ -872,6 +878,24 @@ void QHttpHeadersPrivate::replaceOrAppend(Self &d, const HeaderName &name, QByte
}
}
+void QHttpHeadersPrivate::forEachHeader(QAnyStringView name,
+ qxp::function_ref<void(QByteArrayView)> yield)
+{
+ for (const auto &h : std::as_const(headers)) {
+ if (h.name.asView() == name)
+ yield(h.value);
+ }
+}
+
+std::optional<QByteArrayView> QHttpHeadersPrivate::findValue(const HeaderName &name) const noexcept
+{
+ for (const auto &h : headers) {
+ if (h.name == name)
+ return h.value;
+ }
+ return std::nullopt;
+}
+
/*!
Creates a new QHttpHeaders object.
*/
@@ -1411,6 +1435,163 @@ QByteArray QHttpHeaders::combinedValue(WellKnownHeader name) const
}
/*!
+ \since 6.10
+
+ Returns the value of the first valid header \a name interpreted as a
+ 64-bit integer.
+ If the header does not exist or cannot be parsed as an integer, returns
+ \c std::nullopt.
+
+ \sa intValues(QAnyStringView name), intValueAt(qsizetype i)
+*/
+std::optional<qint64> QHttpHeaders::intValue(QAnyStringView name) const noexcept
+{
+ std::optional<QByteArrayView> v = d->findValue(HeaderName{name});
+ if (!v)
+ return std::nullopt;
+ bool ok = false;
+ const qint64 result = v->toLongLong(&ok);
+ if (ok)
+ return result;
+ return std::nullopt;
+}
+
+/*!
+ \since 6.10
+ \overload intValue(QAnyStringView)
+*/
+std::optional<qint64> QHttpHeaders::intValue(WellKnownHeader name) const noexcept
+{
+ return intValue(wellKnownHeaderName(name));
+}
+
+/*!
+ \since 6.10
+
+ Returns the values of the header \a name interpreted as 64-bit integer
+ in a list. If the header does not exist or cannot be parsed as an integer,
+ returns \c std::nullopt.
+
+ \sa intValue(QAnyStringView name), intValueAt(qsizetype i)
+*/
+std::optional<QList<qint64>> QHttpHeaders::intValues(QAnyStringView name) const
+{
+ QList<qint64> results;
+ d->forEachHeader(name, [&](QByteArrayView value) {
+ bool ok = false;
+ qint64 result = value.toLongLong(&ok);
+ if (ok)
+ results.append(result);
+ });
+ return results.isEmpty() ? std::nullopt :
+ std::make_optional(std::move(results));
+}
+
+/*!
+ \since 6.10
+ \overload intValues(QAnyStringView)
+*/
+std::optional<QList<qint64>> QHttpHeaders::intValues(WellKnownHeader name) const
+{
+ return intValues(wellKnownHeaderName(name));
+}
+
+/*!
+ \since 6.10
+
+ Returns the header value interpreted as 64-bit integer at index \a i.
+ The index \a i must be valid.
+
+ \sa intValues(QAnyStringView name), intValue(QAnyStringView name)
+*/
+std::optional<qint64> QHttpHeaders::intValueAt(qsizetype i) const noexcept
+{
+ verify(i);
+ QByteArrayView v = valueAt(i);
+ if (v.isEmpty())
+ return std::nullopt;
+ bool ok = false;
+ const qint64 result = v.toLongLong(&ok);
+ return ok ? std::optional<qint64>(result) :
+ std::nullopt;
+}
+
+/*!
+ \since 6.10
+
+ Converts the first found header value of \a name to a QDateTime object, following
+ the standard HTTP date formats. If the header does not exist or contains an invalid
+ QDateTime, returns \c std::nullopt.
+
+ \sa dateTimeValues(QAnyStringView name), dateTimeValueAt(qsizetype i)
+*/
+std::optional<QDateTime> QHttpHeaders::dateTimeValue(QAnyStringView name) const
+{
+ std::optional<QByteArrayView> v = d->findValue(HeaderName{name});
+ if (!v)
+ return std::nullopt;
+ QDateTime dt = QNetworkHeadersPrivate::fromHttpDate(*v);
+ if (dt.isValid())
+ return std::move(dt);
+ return std::nullopt;
+}
+
+/*!
+ \since 6.10
+ \overload dateTimeValue(QAnyStringView)
+*/
+std::optional<QDateTime> QHttpHeaders::dateTimeValue(WellKnownHeader name) const
+{
+ return dateTimeValue(wellKnownHeaderName(name));
+}
+
+/*!
+ \since 6.10
+
+ Returns all the header values of \a name in a list of QDateTime objects, following
+ the standard HTTP date formats. If no valid date-time values are found, returns
+ \c std::nullopt.
+
+ \sa dateTimeValue(QAnyStringView name), dateTimeValueAt(qsizetype i)
+*/
+std::optional<QList<QDateTime>> QHttpHeaders::dateTimeValues(QAnyStringView name) const
+{
+ QList<QDateTime> results;
+ d->forEachHeader(name, [&](QByteArrayView value) {
+ QDateTime dt = QNetworkHeadersPrivate::fromHttpDate(value);
+ if (dt.isValid())
+ results.append(std::move(dt));
+ });
+ return results.isEmpty() ? std::nullopt :
+ std::make_optional(std::move(results));
+}
+
+/*!
+ \since 6.10
+ \overload dateTimeValues(QAnyStringView)
+*/
+std::optional<QList<QDateTime>> QHttpHeaders::dateTimeValues(WellKnownHeader name) const
+{
+ return dateTimeValues(wellKnownHeaderName(name));
+}
+
+/*!
+ \since 6.10
+
+ Converts the header value at index \a i to a QDateTime object following the standard
+ HTTP date formats. The index \a i must be valid.
+
+ \sa dateTimeValue(QAnyStringView name), dateTimeValues(QAnyStringView name)
+*/
+std::optional<QDateTime> QHttpHeaders::dateTimeValueAt(qsizetype i) const
+{
+ verify(i);
+ QDateTime dt = QNetworkHeadersPrivate::fromHttpDate(valueAt(i));
+ return dt.isValid() ? std::make_optional(std::move(dt)) :
+ std::nullopt;
+}
+
+/*!
Returns the number of header entries.
*/
qsizetype QHttpHeaders::size() const noexcept
diff --git a/src/network/access/qhttpheaders.h b/src/network/access/qhttpheaders.h
index 760d9037c29..c9087724a64 100644
--- a/src/network/access/qhttpheaders.h
+++ b/src/network/access/qhttpheaders.h
@@ -6,6 +6,8 @@
#include <QtNetwork/qtnetworkglobal.h>
+#include <QtCore/qdatetime.h>
+#include <QtCore/qmetaobject.h>
#include <QtCore/qobjectdefs.h>
#include <QtCore/qshareddata.h>
#include <QtCore/qcontainerfwd.h>
@@ -244,6 +246,22 @@ public:
Q_NETWORK_EXPORT QByteArray combinedValue(QAnyStringView name) const;
Q_NETWORK_EXPORT QByteArray combinedValue(WellKnownHeader name) const;
+ Q_NETWORK_EXPORT std::optional<qint64> intValue(QAnyStringView name) const noexcept;
+ Q_NETWORK_EXPORT std::optional<qint64> intValue(WellKnownHeader name) const noexcept;
+
+ Q_NETWORK_EXPORT std::optional<QList<qint64>> intValues(QAnyStringView name) const;
+ Q_NETWORK_EXPORT std::optional<QList<qint64>> intValues(WellKnownHeader name) const;
+
+ Q_NETWORK_EXPORT std::optional<qint64> intValueAt(qsizetype i) const noexcept;
+
+ Q_NETWORK_EXPORT std::optional<QDateTime> dateTimeValue(QAnyStringView name) const;
+ Q_NETWORK_EXPORT std::optional<QDateTime> dateTimeValue(WellKnownHeader name) const;
+
+ Q_NETWORK_EXPORT std::optional<QList<QDateTime>> dateTimeValues(QAnyStringView name) const;
+ Q_NETWORK_EXPORT std::optional<QList<QDateTime>> dateTimeValues(WellKnownHeader name) const;
+
+ Q_NETWORK_EXPORT std::optional<QDateTime> dateTimeValueAt(qsizetype i) const;
+
Q_NETWORK_EXPORT qsizetype size() const noexcept;
Q_NETWORK_EXPORT void reserve(qsizetype size);
bool isEmpty() const noexcept { return size() == 0; }
diff --git a/tests/auto/network/access/qhttpheaders/tst_qhttpheaders.cpp b/tests/auto/network/access/qhttpheaders/tst_qhttpheaders.cpp
index 5b6be3c7b4c..dc21bf2dab3 100644
--- a/tests/auto/network/access/qhttpheaders/tst_qhttpheaders.cpp
+++ b/tests/auto/network/access/qhttpheaders/tst_qhttpheaders.cpp
@@ -22,6 +22,8 @@ private slots:
void headerValueField();
void valueEncoding();
void replaceOrAppend();
+ void intValues();
+ void dateTimeValues();
private:
static constexpr QAnyStringView n1{"name1"};
@@ -545,5 +547,90 @@ void tst_QHttpHeaders::replaceOrAppend()
QVERIFY(!h1.replaceOrAppend(v1, "foo\x08"));
}
+void tst_QHttpHeaders::intValues()
+{
+ QHttpHeaders h1;
+ h1.append("Content-Length", "12345");
+ h1.append(QHttpHeaders::WellKnownHeader::ContentLength, "67890");
+
+ std::optional<qint64> intValueString = h1.intValue("content-length");
+ QCOMPARE(intValueString, 12345);
+
+ std::optional<qint64> intValueWKH =
+ h1.intValue(QHttpHeaders::WellKnownHeader::ContentLength);
+ QCOMPARE(intValueWKH, 12345);
+
+ std::optional<QList<qint64>> intStringValuesList = h1.intValues("content-length");
+ QVERIFY(intStringValuesList);
+ QCOMPARE(intStringValuesList->size(), 2);
+ QCOMPARE(intStringValuesList->at(0), 12345);
+ QCOMPARE(intStringValuesList->at(1), 67890);
+
+ std::optional<QList<qint64>> intWKHValuesList =
+ h1.intValues(QHttpHeaders::WellKnownHeader::ContentLength);
+ QVERIFY(intWKHValuesList);
+ QCOMPARE(intWKHValuesList->size(), 2);
+ QCOMPARE(intWKHValuesList->at(0), 12345);
+ QCOMPARE(intWKHValuesList->at(1), 67890);
+
+ std::optional<qint64> intValueAtIndex = h1.intValueAt(1);
+ QCOMPARE(intValueAtIndex, 67890);
+
+ h1.clear();
+ h1.append("Content-Length", "Invalid Number");
+ h1.append("Content-Length", "");
+ QCOMPARE(h1.intValueAt(0), std::nullopt);
+ QCOMPARE(h1.intValue("content-length"), std::nullopt);
+ QCOMPARE(h1.intValue("non-existing-header"), std::nullopt);
+ QCOMPARE(h1.intValues("content-length"), std::nullopt);
+}
+
+void tst_QHttpHeaders::dateTimeValues()
+{
+ QHttpHeaders h1;
+ h1.append("Date", "Tue, 25 Feb 2025 10:10:10 GMT");
+ h1.append(QHttpHeaders::WellKnownHeader::Date, "Mon, 24 Feb 2025 11:11:11 GMT");
+
+ std::optional<QDateTime> dateTimeString = h1.dateTimeValue("date");
+ QVERIFY(dateTimeString);
+ QCOMPARE(dateTimeString->date(), QDate(2025, 2, 25));
+ QCOMPARE(dateTimeString->time(), QTime(10, 10, 10, 0));
+ std::optional<QDateTime> dateTimeWKH =
+ h1.dateTimeValue(QHttpHeaders::WellKnownHeader::Date);
+ QVERIFY(dateTimeWKH);
+ QCOMPARE(dateTimeWKH->date(), QDate(2025, 2, 25));
+ QCOMPARE(dateTimeWKH->time(), QTime(10, 10, 10, 0));
+
+ std::optional<QList<QDateTime>> dateTimeStringValueList = h1.dateTimeValues("date");
+ QVERIFY(dateTimeStringValueList);
+ QCOMPARE(dateTimeStringValueList->size(), 2);
+ QCOMPARE(dateTimeStringValueList->at(0).date(), QDate(2025, 2, 25));
+ QCOMPARE(dateTimeStringValueList->at(0).time(), QTime(10, 10, 10, 0));
+ QCOMPARE(dateTimeStringValueList->at(1).date(), QDate(2025, 2, 24));
+ QCOMPARE(dateTimeStringValueList->at(1).time(), QTime(11, 11, 11, 0));
+
+ std::optional<QList<QDateTime>> dateTimeWHKValueList =
+ h1.dateTimeValues(QHttpHeaders::WellKnownHeader::Date);
+ QVERIFY(dateTimeWHKValueList);
+ QCOMPARE(dateTimeWHKValueList->size(), 2);
+ QCOMPARE(dateTimeWHKValueList->at(0).date(), QDate(2025, 2, 25));
+ QCOMPARE(dateTimeWHKValueList->at(0).time(), QTime(10, 10, 10, 0));
+ QCOMPARE(dateTimeWHKValueList->at(1).date(), QDate(2025, 2, 24));
+ QCOMPARE(dateTimeWHKValueList->at(1).time(), QTime(11, 11, 11, 0));
+
+ std::optional<QDateTime> dateTimeValueAtIndex = h1.dateTimeValueAt(1);
+ QVERIFY(dateTimeValueAtIndex);
+ QCOMPARE(dateTimeValueAtIndex->date(), QDate(2025, 2, 24));
+ QCOMPARE(dateTimeValueAtIndex->time(), QTime(11, 11, 11, 0));
+
+ h1.clear();
+ h1.append("Date", "InvalidDateFormat");
+ h1.append("Date", "");
+ QCOMPARE(h1.dateTimeValueAt(0), std::nullopt);
+ QCOMPARE(h1.dateTimeValue("date"), std::nullopt);
+ QCOMPARE(h1.dateTimeValue("non-existing-header"), std::nullopt);
+ QCOMPARE(h1.dateTimeValues("date"), std::nullopt);
+}
+
QTEST_MAIN(tst_QHttpHeaders)
#include "tst_qhttpheaders.moc"