diff options
Diffstat (limited to 'tests/auto/network/access/qhttpheaders/tst_qhttpheaders.cpp')
-rw-r--r-- | tests/auto/network/access/qhttpheaders/tst_qhttpheaders.cpp | 552 |
1 files changed, 552 insertions, 0 deletions
diff --git a/tests/auto/network/access/qhttpheaders/tst_qhttpheaders.cpp b/tests/auto/network/access/qhttpheaders/tst_qhttpheaders.cpp new file mode 100644 index 0000000000..457d30feeb --- /dev/null +++ b/tests/auto/network/access/qhttpheaders/tst_qhttpheaders.cpp @@ -0,0 +1,552 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QtNetwork/qhttpheaders.h> + +#include <QtTest/qtest.h> + +#include <QtCore/qmap.h> +#include <QtCore/qset.h> + +using namespace Qt::StringLiterals; + +class tst_QHttpHeaders : public QObject +{ + Q_OBJECT + +private slots: + void constructors(); + void accessors(); + void wellKnownHeader(); + void headerNameField(); + void headerValueField(); + void valueEncoding(); + void replaceOrAppend(); + +private: + static constexpr QAnyStringView n1{"name1"}; + static constexpr QAnyStringView n2{"name2"}; + static constexpr QAnyStringView n3{"name3"}; + static constexpr QAnyStringView v1{"value1"}; + static constexpr QAnyStringView v2{"value2"}; + static constexpr QAnyStringView v3{"value3"}; + static constexpr QAnyStringView N1{"NAME1"}; + static constexpr QAnyStringView N2{"NAME2"}; + static constexpr QAnyStringView N3{"NAME3"}; + static constexpr QAnyStringView V1{"VALUE1"}; + static constexpr QAnyStringView V2{"VALUE2"}; + static constexpr QAnyStringView V3{"VALUE3"}; +}; + +void tst_QHttpHeaders::constructors() +{ + // Default ctor + QHttpHeaders h1; + QVERIFY(h1.isEmpty()); + + // Copy ctor + QHttpHeaders h2(h1); + QCOMPARE(h2.toListOfPairs(), h1.toListOfPairs()); + + // Copy assignment + QHttpHeaders h3; + h3 = h1; + QCOMPARE(h3.toListOfPairs(), h1.toListOfPairs()); + + // Move assignment + QHttpHeaders h4; + h4 = std::move(h2); + QCOMPARE(h4.toListOfPairs(), h1.toListOfPairs()); + + // Move ctor + QHttpHeaders h5(std::move(h4)); + QCOMPARE(h5.toListOfPairs(), h1.toListOfPairs()); + + // Constructors that are counterparts to 'toXXX()' conversion getters + const QByteArray nb1{"name1"}; + const QByteArray nb2{"name2"}; + const QByteArray nv1{"value1"}; + const QByteArray nv2{"value2"}; + // Initialize three QHttpHeaders with similar content, and verify that they have + // similar header entries +#define CONTAINS_HEADER(NAME, VALUE) \ + QVERIFY(hlist.contains(NAME) && hmap.contains(NAME) && hhash.contains(NAME)); \ + QCOMPARE(hlist.combinedValue(NAME), VALUE); \ + QCOMPARE(hmap.combinedValue(NAME), VALUE); \ + QCOMPARE(hhash.combinedValue(NAME), VALUE); \ + + QList<std::pair<QByteArray, QByteArray>> list{{nb1, nv1}, {nb2, nv2}, {nb2, nv2}}; + QMultiMap<QByteArray, QByteArray> map{{nb1, nv1}, {nb2, nv2}, {nb2, nv2}}; + QMultiHash<QByteArray, QByteArray> hash{{nb1, nv1}, {nb2, nv2}, {nb2, nv2}}; + QHttpHeaders hlist = QHttpHeaders::fromListOfPairs(list); + QHttpHeaders hmap = QHttpHeaders::fromMultiMap(map); + QHttpHeaders hhash = QHttpHeaders::fromMultiHash(hash); + CONTAINS_HEADER(nb1, v1); + CONTAINS_HEADER(nb2, nv2 + ", " + nv2) +#undef CONTAINS_HEADER +} + +void tst_QHttpHeaders::accessors() +{ + QHttpHeaders h1; + + // isEmpty(), clear(), size() + h1.append(n1,v1); + QVERIFY(!h1.isEmpty()); + QCOMPARE(h1.size(), 1); + QVERIFY(h1.append(n1, v1)); + QCOMPARE(h1.size(), 2); + h1.insert(0, n1, v1); + QCOMPARE(h1.size(), 3); + h1.clear(); + QVERIFY(h1.isEmpty()); + + // contains() + h1.append(n1, v1); + QVERIFY(h1.contains(n1)); + QVERIFY(h1.contains(N1)); + QVERIFY(!h1.contains(n2)); + QVERIFY(!h1.contains(QHttpHeaders::WellKnownHeader::Allow)); + h1.append(QHttpHeaders::WellKnownHeader::Accept, "nothing"); + QVERIFY(h1.contains(QHttpHeaders::WellKnownHeader::Accept)); + QVERIFY(h1.contains("accept")); + + // values()/value() +#define EXISTS_NOT(H, N) do { \ + QVERIFY(!H.contains(N)); \ + QCOMPARE(H.value(N, "ENOENT"), "ENOENT"); \ + const auto values = H.values(N); \ + QVERIFY(values.isEmpty()); \ + QVERIFY(H.combinedValue(N).isNull()); \ + } while (false) + +#define EXISTS_N_TIMES(X, H, N, ...) do { \ + const std::array expected = { __VA_ARGS__ }; \ + static_assert(std::tuple_size_v<decltype(expected)> == X); \ + QVERIFY(H.contains(N)); \ + QCOMPARE(H.value(N, "ENOENT"), expected.front()); \ + const auto values = H.values(N); \ + QCOMPARE(values.size(), X); \ + QCOMPARE(values.front(), expected.front()); \ + /* ignore in-between */ \ + QCOMPARE(values.back(), expected.back()); \ + QCOMPARE(H.combinedValue(N), values.join(", ")); \ + } while (false) + +#define EXISTS_ONCE(H, N, V) EXISTS_N_TIMES(1, H, N, V) + + EXISTS_ONCE(h1, n1, v1); + EXISTS_ONCE(h1, N1, v1); + EXISTS_ONCE(h1, QHttpHeaders::WellKnownHeader::Accept, "nothing"); + EXISTS_ONCE(h1, "Accept", "nothing"); + + EXISTS_NOT(h1, N2); + EXISTS_NOT(h1, QHttpHeaders::WellKnownHeader::Allow); + + h1.clear(); + + EXISTS_NOT(h1, n1); + + h1.append(n1, v1); + h1.append(n1, v2); + h1.append(n1, v3); + h1.append(n2, v2); + h1.append(n3, ""); // empty value + + EXISTS_N_TIMES(3, h1, n1, v1, v2, v3); + EXISTS_N_TIMES(3, h1, N1, v1, v2, v3); + EXISTS_ONCE(h1, n3, ""); // empty value + + h1.append(QHttpHeaders::WellKnownHeader::Accept, "nothing"); + h1.append(QHttpHeaders::WellKnownHeader::Accept, "ever"); + + EXISTS_N_TIMES(2, h1, QHttpHeaders::WellKnownHeader::Accept, "nothing", "ever"); + EXISTS_NOT(h1, "nonexistent"); + +#undef EXISTS_ONCE +#undef EXISTS_N_TIMES +#undef EXISTS_NOT + + // valueAt() + h1.clear(); + h1.append(n1, v1); + h1.append(n2, v2); + h1.append(n3, v3); + QCOMPARE(h1.valueAt(0), v1); + QCOMPARE(h1.valueAt(1), v2); + QCOMPARE(h1.valueAt(2), v3); + + // nameAt() + h1.clear(); + h1.append(n1, v1); + h1.append(n2, v2); + h1.append(n3, v3); + QCOMPARE(h1.nameAt(0), n1); + QCOMPARE(h1.nameAt(1), n2); + QCOMPARE(h1.nameAt(2), n3); + + // removeAll() + h1.clear(); + QVERIFY(h1.append(n1, v1)); + QVERIFY(h1.append(QHttpHeaders::WellKnownHeader::Accept, "nothing")); + QVERIFY(h1.append(n1, v1)); + QCOMPARE(h1.size(), 3); + h1.removeAll(n1); + QVERIFY(!h1.contains(n1)); + QCOMPARE(h1.size(), 1); + QVERIFY(h1.contains("accept")); + h1.removeAll(QHttpHeaders::WellKnownHeader::Accept); + QVERIFY(!h1.contains(QHttpHeaders::WellKnownHeader::Accept)); + + // removeAt() + h1.clear(); + h1.append(n1, v1); + h1.append(n2, v2); + h1.append(n3, v3); + + // Valid removals + QVERIFY(h1.contains(n3)); + h1.removeAt(2); + QVERIFY(!h1.contains(n3)); + QVERIFY(h1.contains(n1)); + h1.removeAt(0); + QVERIFY(!h1.contains(n1)); + QVERIFY(h1.contains(n2)); + h1.removeAt(0); + QVERIFY(!h1.contains(n2)); + QVERIFY(h1.isEmpty()); + + // toListOfPairs() + h1.clear(); + h1.append(n1, v1); + h1.append(n2, v2); + h1.append(N3, V3); // uppercase of n3 + auto list = h1.toListOfPairs(); + QCOMPARE(list.size(), h1.size()); + QCOMPARE(list.at(0).first, n1); + QCOMPARE(list.at(0).second, v1); + QCOMPARE(list.at(1).first, n2); + QCOMPARE(list.at(1).second, v2); + QCOMPARE(list.at(2).first, n3); // N3 has been lower-cased + QCOMPARE(list.at(2).second, V3); + + // toMultiMap() + auto map = h1.toMultiMap(); + QCOMPARE(map.size(), h1.size()); + QCOMPARE(map.value(n1.toString().toLatin1()), v1); + QCOMPARE(map.value(n2.toString().toLatin1()), v2); + QCOMPARE(map.value(n3.toString().toLatin1()), V3); + + // toMultiHash() + auto hash = h1.toMultiHash(); + QCOMPARE(hash.size(), h1.size()); + QCOMPARE(hash.value(n1.toString().toLatin1()), v1); + QCOMPARE(hash.value(n2.toString().toLatin1()), v2); + QCOMPARE(hash.value(n3.toString().toLatin1()), V3); + + // insert() + h1.clear(); + h1.append(n3, v3); + QVERIFY(h1.insert(0, n1, v1)); + list = h1.toListOfPairs(); + QCOMPARE(list.size(), 2); + QCOMPARE(list.at(0).first, n1); + QCOMPARE(list.at(0).second, v1); + QCOMPARE(list.at(1).first, n3); + QCOMPARE(list.at(1).second, v3); + QVERIFY(h1.insert(1, n2, v2)); + list = h1.toListOfPairs(); + QCOMPARE(list.size(), 3); + QCOMPARE(list.at(0).first, n1); + QCOMPARE(list.at(0).second, v1); + QCOMPARE(list.at(1).first, n2); + QCOMPARE(list.at(1).second, v2); + QCOMPARE(list.at(2).first, n3); + QCOMPARE(list.at(2).second, v3); + QVERIFY(h1.insert(1, QHttpHeaders::WellKnownHeader::Accept, "nothing")); + QCOMPARE(h1.size(), 4); + list = h1.toListOfPairs(); + QCOMPARE(list.at(1).first, "accept"); + QCOMPARE(list.at(1).second, "nothing"); + QVERIFY(h1.insert(list.size(), "LastName", "lastValue")); + QCOMPARE(h1.size(), 5); + list = h1.toListOfPairs(); + QCOMPARE(list.last().first, "lastname"); + QCOMPARE(list.last().second, "lastValue"); + // Failed insert + QRegularExpression re("HTTP header name contained*"); + QTest::ignoreMessage(QtMsgType::QtWarningMsg, re); + QVERIFY(!h1.insert(0, "a€", "b")); + + // replace + h1.clear(); + h1.append(n1, v1); + h1.append(n2, v2); + QCOMPARE(h1.size(), 2); + QVERIFY(h1.replace(0, n3, v3)); + QVERIFY(h1.replace(1, QHttpHeaders::WellKnownHeader::Accept, "nothing")); + QCOMPARE(h1.size(), 2); + list = h1.toListOfPairs(); + QCOMPARE(list.at(0).first, n3); + QCOMPARE(list.at(0).second, v3); + QCOMPARE(list.at(1).first, "accept"); + QCOMPARE(list.at(1).second, "nothing"); + QVERIFY(h1.replace(1, "ACCEPT", "NOTHING")); + QCOMPARE(h1.size(), 2); + list = h1.toListOfPairs(); + QCOMPARE(list.at(0).first, n3); + QCOMPARE(list.at(0).second, v3); + QCOMPARE(list.at(1).first, "accept"); + QCOMPARE(list.at(1).second, "NOTHING"); + // Failed replace + QTest::ignoreMessage(QtMsgType::QtWarningMsg, re); + QVERIFY(!h1.replace(0, "a€", "b")); + +} + +void tst_QHttpHeaders::wellKnownHeader() +{ + QByteArrayView view = QHttpHeaders::wellKnownHeaderName(QHttpHeaders::WellKnownHeader::AIM); + QCOMPARE(view, "a-im"); +} + +#define TEST_ILLEGAL_HEADER_NAME_CHARACTER(NAME) \ + QTest::ignoreMessage(QtMsgType::QtWarningMsg, re); \ + QVERIFY(!h1.append(NAME, v1)); \ + QVERIFY(h1.isEmpty()); \ + +void tst_QHttpHeaders::headerNameField() +{ + QHttpHeaders h1; + + // All allowed characters in different encodings and types + // const char[] + h1.append("abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%&'*+-.^_`|~", v1); + QCOMPARE(h1.size(), 1); + // UTF-8 + h1.append(u8"abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%&'*+-.^_`|~", + v1); + QCOMPARE(h1.size(), 2); + // UTF-16 + h1.append(u"abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%&'*+-.^_`|~", v1); + QCOMPARE(h1.size(), 3); + // QString (UTF-16) + h1.append(u"abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%&'*+-.^_`|~"_s, + v1); + QCOMPARE(h1.size(), 4); + QCOMPARE(h1.nameAt(0), h1.nameAt(1)); + QCOMPARE(h1.nameAt(1), h1.nameAt(2)); + QCOMPARE(h1.nameAt(2), h1.nameAt(3)); + h1.clear(); + + // Error cases + // Header name must contain at least 1 character + QTest::ignoreMessage(QtMsgType::QtWarningMsg, "HTTP header name cannot be empty"); + h1.append("", v1); + QVERIFY(h1.isEmpty()); + // Disallowed ASCII/extended ASCII characters (not exhaustive list) + QRegularExpression re("HTTP header name contained illegal character*"); + TEST_ILLEGAL_HEADER_NAME_CHARACTER("foo\x08" "bar"); // BS + TEST_ILLEGAL_HEADER_NAME_CHARACTER("foo\x7F" "bar"); // DEL + TEST_ILLEGAL_HEADER_NAME_CHARACTER("foo()" "bar"); // parantheses + TEST_ILLEGAL_HEADER_NAME_CHARACTER("foobar" "¿"); // extended ASCII + TEST_ILLEGAL_HEADER_NAME_CHARACTER("©" "foobar"); // extended ASCII + TEST_ILLEGAL_HEADER_NAME_CHARACTER("foo,bar"); // comma + // Disallowed UTF-8 characters + TEST_ILLEGAL_HEADER_NAME_CHARACTER(u8"€"); + TEST_ILLEGAL_HEADER_NAME_CHARACTER(u8"𝒜𝒴𝟘𝟡𐎀𐎜𐒀𐒐𝓐𝓩𝔸𝔹𝕀𝕁𝕌𝕍𓂀𓂁𓃀𓃁𓇋𓇌𓉐𓉑𓋴𓋵𓎡𓎢𓎣𓏏"); + // Disallowed UTF-16 characters + TEST_ILLEGAL_HEADER_NAME_CHARACTER(u"€"); + TEST_ILLEGAL_HEADER_NAME_CHARACTER(u"𝒜𝒴𝟘𝟡𐎀𐎜𐒀𐒐𝓐𝓩𝔸𝔹𝕀𝕁𝕌𝕍𓂀𓂁𓃀𓃁𓇋𓇌𓉐𓉑𓋴𓋵𓎡𓎢𓎣𓏏"); + + // Non-null-terminated name. The 'x' below is to make sure the strings don't + // null-terminate by happenstance + h1.clear(); + constexpr char L1Array[] = {'a','b','c','x'}; + const QLatin1StringView nonNullLatin1{L1Array, sizeof(L1Array) - 1}; // abc + + constexpr char UTF8Array[] = {0x64, 0x65, 0x66, 0x78}; + const QUtf8StringView nonNullUTF8(UTF8Array, sizeof(UTF8Array) - 1); // def + + constexpr QChar UTF16Array[] = {'g', 'h', 'i', 'x'}; + QStringView nonNullUTF16(UTF16Array, sizeof(UTF16Array) / sizeof(QChar) - 1); // ghi + + h1.append(nonNullLatin1, v1); + QCOMPARE(h1.size(), 1); + QVERIFY(h1.contains(nonNullLatin1)); + QCOMPARE(h1.combinedValue(nonNullLatin1), v1); + + h1.append(nonNullUTF8, v2); + QCOMPARE(h1.size(), 2); + QVERIFY(h1.contains(nonNullUTF8)); + QCOMPARE(h1.combinedValue(nonNullUTF8), v2); + + h1.append(nonNullUTF16, v3); + QCOMPARE(h1.size(), 3); + QVERIFY(h1.contains(nonNullUTF16)); + QCOMPARE(h1.combinedValue(nonNullUTF16), v3); +} + +#define TEST_ILLEGAL_HEADER_VALUE_CHARACTER(VALUE) \ +QTest::ignoreMessage(QtMsgType::QtWarningMsg, re); \ + QVERIFY(!h1.append(n1, VALUE)); \ + QVERIFY(h1.isEmpty()); \ + +void tst_QHttpHeaders::headerValueField() +{ + QHttpHeaders h1; + + // Visible ASCII characters and space and horizontal tab + // const char[] + h1.append(n1, "!\"#$%&'()*+,-./0123456789:; \t<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" + "`abcdefghijklmnopqrstuvwxyz{|}~"); + QCOMPARE(h1.size(), 1); + // UTF-8 + h1.append(n1, u8"!\"#$%&'()*+,-./0123456789:; \t<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" + "`abcdefghijklmnopqrstuvwxyz{|}~"); + QCOMPARE(h1.size(), 2); + // UTF-16 + h1.append(n1, u"!\"#$%&'()*+,-./0123456789:; \t<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" + "`abcdefghijklmnopqrstuvwxyz{|}~"); + QCOMPARE(h1.size(), 3); + // QString / UTF-16 + h1.append(n1, u"!\"#$%&'()*+,-./0123456789:; \t<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" + "`abcdefghijklmnopqrstuvwxyz{|}~"_s); + QCOMPARE(h1.size(), 4); + const auto values = h1.values(n1); + QVERIFY(!values.isEmpty() && values.size() == 4); + QVERIFY(values[0] == values[1] + && values[1] == values[2] + && values[2] == values[3]); + // Extended ASCII (explicit on Latin-1 to avoid UTF-8 interpretation) + h1.append(n1, "\x80\x09\xB2\xFF"_L1); + QCOMPARE(h1.size(), 5); + // Empty value + h1.append(n1, ""); + QCOMPARE(h1.size(), 6); + // Leading and trailing space + h1.clear(); + h1.append(n1, " foo "); + QCOMPARE(h1.combinedValue(n1), "foo"); + h1.append(n1, "\tbar\t"); + QCOMPARE(h1.combinedValue(n1), "foo, bar"); + QCOMPARE(h1.size(), 2); + + h1.clear(); + QRegularExpression re("HTTP header value contained illegal character*"); + TEST_ILLEGAL_HEADER_VALUE_CHARACTER("foo\x08" "bar"); // BS + TEST_ILLEGAL_HEADER_VALUE_CHARACTER("foo\x1B" "bar"); // ESC + // Disallowed UTF-8 characters + TEST_ILLEGAL_HEADER_VALUE_CHARACTER(u8"€"); + TEST_ILLEGAL_HEADER_VALUE_CHARACTER(u8"𝒜𝒴𝟘𝟡𐎀𐎜𐒀𐒐𝓐𝓩𝔸𝔹𝕀𝕁𝕌𝕍𓂀𓂁𓃀𓃁𓇋𓇌𓉐𓉑𓋴𓋵𓎡𓎢𓎣𓏏"); + // Disallowed UTF-16 characters + TEST_ILLEGAL_HEADER_VALUE_CHARACTER(u"€"); + TEST_ILLEGAL_HEADER_VALUE_CHARACTER(u"𝒜𝒴𝟘𝟡𐎀𐎜𐒀𐒐𝓐𝓩𝔸𝔹𝕀𝕁𝕌𝕍𓂀𓂁𓃀𓃁𓇋𓇌𓉐𓉑𓋴𓋵𓎡𓎢𓎣𓏏"); + + // Non-null-terminated value. The 'x' below is to make sure the strings don't + // null-terminate by happenstance + h1.clear(); + constexpr char L1Array[] = {'a','b','c','x'}; + const QLatin1StringView nonNullLatin1{L1Array, sizeof(L1Array) - 1}; // abc + + constexpr char UTF8Array[] = {0x64, 0x65, 0x66, 0x78}; + const QUtf8StringView nonNullUTF8(UTF8Array, sizeof(UTF8Array) - 1); // def + + constexpr QChar UTF16Array[] = {'g', 'h', 'i', 'x'}; + QStringView nonNullUTF16(UTF16Array, sizeof(UTF16Array) / sizeof(QChar) - 1); // ghi + + h1.append(n1, nonNullLatin1); + QCOMPARE(h1.size(), 1); + QVERIFY(h1.contains(n1)); + QCOMPARE(h1.combinedValue(n1), "abc"); + + h1.append(n2, nonNullUTF8); + QCOMPARE(h1.size(), 2); + QVERIFY(h1.contains(n2)); + QCOMPARE(h1.combinedValue(n2), "def"); + + h1.append(n3, nonNullUTF16); + QCOMPARE(h1.size(), 3); + QVERIFY(h1.contains(n3)); + QCOMPARE(h1.combinedValue(n3), "ghi"); +} + +void tst_QHttpHeaders::valueEncoding() +{ + // Test that common encodings are possible to set and not blocked by + // header value character filter (ie. don't contain disallowed characters as per RFC 9110) + QHttpHeaders h1; + // Within visible ASCII range + QVERIFY(h1.append(n1, "foo"_ba.toBase64())); + QCOMPARE(h1.values(n1).at(0), "Zm9v"); + h1.replace(0, n1, "foo"_ba.toPercentEncoding()); + QCOMPARE(h1.values(n1).at(0), "foo"); + + // Outside of ASCII/Latin-1 range (€) + h1.replace(0, n1, "foo€"_ba.toBase64()); + QCOMPARE(h1.values(n1).at(0), "Zm9v4oKs"); + h1.replace(0, n1, "foo€"_ba.toPercentEncoding()); + QCOMPARE(h1.values(n1).at(0), "foo%E2%82%AC"); +} + +void tst_QHttpHeaders::replaceOrAppend() +{ + QHttpHeaders h1; + +#define REPLACE_OR_APPEND(NAME, VALUE, INDEX, TOTALSIZE) \ + do { \ + QVERIFY(h1.replaceOrAppend(NAME, VALUE)); \ + QCOMPARE(h1.size(), TOTALSIZE); \ + QCOMPARE(h1.nameAt(INDEX), NAME); \ + QCOMPARE(h1.valueAt(INDEX), VALUE); \ + } while (false) + + // Append to empty container and replace it + REPLACE_OR_APPEND(n1, v1, 0, 1); // Appends + REPLACE_OR_APPEND(n1, v2, 0, 1); // Replaces + + // Replace at beginning, middle, and end + h1.clear(); + REPLACE_OR_APPEND(n1, v1, 0, 1); // Appends + REPLACE_OR_APPEND(n2, v2, 1, 2); // Appends + REPLACE_OR_APPEND(n3, v3, 2, 3); // Appends + REPLACE_OR_APPEND(n1, V1, 0, 3); // Replaces at beginning + REPLACE_OR_APPEND(n2, V2, 1, 3); // Replaces at middle + REPLACE_OR_APPEND(n3, V3, 2, 3); // Replaces at end + + // Pre-existing multiple values (n2) are removed + h1.clear(); + h1.append(n1, v1); + h1.append(n2, v2); // First n2 is at index 1 + h1.append(n2, v2); + h1.append(n3, v3); + h1.append(n2, v2); + QCOMPARE(h1.size(), 5); + QCOMPARE(h1.combinedValue(n2), "value2, value2, value2"); + REPLACE_OR_APPEND(n2, V2, 1, 3); // Replaces value at index 1, and removes the rest + QCOMPARE(h1.combinedValue(n2), "VALUE2"); +#undef REPLACE_OR_APPEND + + // Implicit sharing / detaching + h1.clear(); + h1.append(n1, v1); + QHttpHeaders h2 = h1; + QCOMPARE(h1.size(), h2.size()); + QCOMPARE(h1.valueAt(0), h2.valueAt(0)); // Iniially values are equal + h1.replaceOrAppend(n1, v2); // Change value in h1 => detaches h1 + QCOMPARE_NE(h1.valueAt(0), h2.valueAt(0)); // Values are no more equal + QCOMPARE(h1.valueAt(0), v2); // Value in h1 changed + QCOMPARE(h2.valueAt(0), v1); // Value in h2 remained + + // Failed attempts + h1.clear(); + h1.append(n1, v1); + QRegularExpression re("HTTP header*"); + QTest::ignoreMessage(QtMsgType::QtWarningMsg, re); + QVERIFY(!h1.replaceOrAppend("", V1)); + QTest::ignoreMessage(QtMsgType::QtWarningMsg, re); + QVERIFY(!h1.replaceOrAppend(v1, "foo\x08")); +} + +QTEST_MAIN(tst_QHttpHeaders) +#include "tst_qhttpheaders.moc" |