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 <juha.vuolle@qt.io> Reviewed-by: Marc Mutz <marc.mutz@qt.io>
This commit is contained in:
parent
23fd2cfb01
commit
ed341fcdd1
@ -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.
|
||||
*/
|
||||
@ -1410,6 +1434,163 @@ QByteArray QHttpHeaders::combinedValue(WellKnownHeader name) const
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
\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.
|
||||
*/
|
||||
|
@ -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; }
|
||||
|
@ -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"
|
||||
|
Loading…
x
Reference in New Issue
Block a user