Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qhttpheaders.cpp
Go to the documentation of this file.
1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qhttpheaders.h"
5
6#include <private/qoffsetstringarray_p.h>
7
8#include <QtCore/qcompare.h>
9#include <QtCore/qhash.h>
10#include <QtCore/qloggingcategory.h>
11#include <QtCore/qmap.h>
12#include <QtCore/qset.h>
13#include <QtCore/qttypetraits.h>
14
15#include <q20algorithm.h>
16#include <string_view>
17#include <variant>
18
20
21Q_LOGGING_CATEGORY(lcQHttpHeaders, "qt.network.http.headers");
22
82// This list is from IANA HTTP Field Name Registry
83// https://www.iana.org/assignments/http-fields
84// It contains entries that are either "permanent"
85// or "deprecated" as of October 2023.
86// Usage relies on enum values keeping in same order.
87// ### Qt7 check if some of these headers have been obsoleted,
88// and also check if the enums benefit from reordering
89static constexpr auto headerNames = qOffsetStringArray(
90 // IANA Permanent status:
91 "a-im",
92 "accept",
93 "accept-additions",
94 "accept-ch",
95 "accept-datetime",
96 "accept-encoding",
97 "accept-features",
98 "accept-language",
99 "accept-patch",
100 "accept-post",
101 "accept-ranges",
102 "accept-signature",
103 "access-control-allow-credentials",
104 "access-control-allow-headers",
105 "access-control-allow-methods",
106 "access-control-allow-origin",
107 "access-control-expose-headers",
108 "access-control-max-age",
109 "access-control-request-headers",
110 "access-control-request-method",
111 "age",
112 "allow",
113 "alpn",
114 "alt-svc",
115 "alt-used",
116 "alternates",
117 "apply-to-redirect-ref",
118 "authentication-control",
119 "authentication-info",
120 "authorization",
121 "cache-control",
122 "cache-status",
123 "cal-managed-id",
124 "caldav-timezones",
125 "capsule-protocol",
126 "cdn-cache-control",
127 "cdn-loop",
128 "cert-not-after",
129 "cert-not-before",
130 "clear-site-data",
131 "client-cert",
132 "client-cert-chain",
133 "close",
134 "connection",
135 "content-digest",
136 "content-disposition",
137 "content-encoding",
138 "content-id",
139 "content-language",
140 "content-length",
141 "content-location",
142 "content-range",
143 "content-security-policy",
144 "content-security-policy-report-only",
145 "content-type",
146 "cookie",
147 "cross-origin-embedder-policy",
148 "cross-origin-embedder-policy-report-only",
149 "cross-origin-opener-policy",
150 "cross-origin-opener-policy-report-only",
151 "cross-origin-resource-policy",
152 "dasl",
153 "date",
154 "dav",
155 "delta-base",
156 "depth",
157 "destination",
158 "differential-id",
159 "dpop",
160 "dpop-nonce",
161 "early-data",
162 "etag",
163 "expect",
164 "expect-ct",
165 "expires",
166 "forwarded",
167 "from",
168 "hobareg",
169 "host",
170 "if",
171 "if-match",
172 "if-modified-since",
173 "if-none-match",
174 "if-range",
175 "if-schedule-tag-match",
176 "if-unmodified-since",
177 "im",
178 "include-referred-token-binding-id",
179 "keep-alive",
180 "label",
181 "last-event-id",
182 "last-modified",
183 "link",
184 "location",
185 "lock-token",
186 "max-forwards",
187 "memento-datetime",
188 "meter",
189 "mime-version",
190 "negotiate",
191 "nel",
192 "odata-entityid",
193 "odata-isolation",
194 "odata-maxversion",
195 "odata-version",
196 "optional-www-authenticate",
197 "ordering-type",
198 "origin",
199 "origin-agent-cluster",
200 "oscore",
201 "oslc-core-version",
202 "overwrite",
203 "ping-from",
204 "ping-to",
205 "position",
206 "prefer",
207 "preference-applied",
208 "priority",
209 "proxy-authenticate",
210 "proxy-authentication-info",
211 "proxy-authorization",
212 "proxy-status",
213 "public-key-pins",
214 "public-key-pins-report-only",
215 "range",
216 "redirect-ref",
217 "referer",
218 "refresh",
219 "replay-nonce",
220 "repr-digest",
221 "retry-after",
222 "schedule-reply",
223 "schedule-tag",
224 "sec-purpose",
225 "sec-token-binding",
226 "sec-websocket-accept",
227 "sec-websocket-extensions",
228 "sec-websocket-key",
229 "sec-websocket-protocol",
230 "sec-websocket-version",
231 "server",
232 "server-timing",
233 "set-cookie",
234 "signature",
235 "signature-input",
236 "slug",
237 "soapaction",
238 "status-uri",
239 "strict-transport-security",
240 "sunset",
241 "surrogate-capability",
242 "surrogate-control",
243 "tcn",
244 "te",
245 "timeout",
246 "topic",
247 "traceparent",
248 "tracestate",
249 "trailer",
250 "transfer-encoding",
251 "ttl",
252 "upgrade",
253 "urgency",
254 "user-agent",
255 "variant-vary",
256 "vary",
257 "via",
258 "want-content-digest",
259 "want-repr-digest",
260 "www-authenticate",
261 "x-content-type-options",
262 "x-frame-options",
263 // IANA Deprecated status:
264 "accept-charset",
265 "c-pep-info",
266 "pragma",
267 "protocol-info",
268 "protocol-query"
269 // If you append here, regenerate the index table
270);
271
272namespace {
273struct ByIndirectHeaderName
274{
275 constexpr bool operator()(quint8 lhs, quint8 rhs) const noexcept
276 {
277 return (*this)(map(lhs), map(rhs));
278 }
279 constexpr bool operator()(quint8 lhs, QByteArrayView rhs) const noexcept
280 {
281 return (*this)(map(lhs), rhs);
282 }
283 constexpr bool operator()(QByteArrayView lhs, quint8 rhs) const noexcept
284 {
285 return (*this)(lhs, map(rhs));
286 }
287 constexpr bool operator()(QByteArrayView lhs, QByteArrayView rhs) const noexcept
288 {
289 // ### just `lhs < rhs` when QByteArrayView relational operators are constexpr
290 return std::string_view(lhs) < std::string_view(rhs);
291 }
292private:
293 static constexpr QByteArrayView map(quint8 i) noexcept
294 {
295 return headerNames.viewAt(i);
296 }
297};
298} // unnamed namespace
299
300// This index table contains the indexes of 'headerNames' entries (above) in alphabetical order.
301// This allows a more efficient binary search for the names [O(logN)]. The 'headerNames' itself
302// cannot be guaranteed to be in alphabetical order, as it must keep the same order as the
303// WellKnownHeader enum, which may get appended over time.
304//
305// Note: when appending new enums, this must be regenerated
306static constexpr quint8 orderedHeaderNameIndexes[] = {
307 0, // a-im
308 1, // accept
309 2, // accept-additions
310 3, // accept-ch
311 172, // accept-charset
312 4, // accept-datetime
313 5, // accept-encoding
314 6, // accept-features
315 7, // accept-language
316 8, // accept-patch
317 9, // accept-post
318 10, // accept-ranges
319 11, // accept-signature
320 12, // access-control-allow-credentials
321 13, // access-control-allow-headers
322 14, // access-control-allow-methods
323 15, // access-control-allow-origin
324 16, // access-control-expose-headers
325 17, // access-control-max-age
326 18, // access-control-request-headers
327 19, // access-control-request-method
328 20, // age
329 21, // allow
330 22, // alpn
331 23, // alt-svc
332 24, // alt-used
333 25, // alternates
334 26, // apply-to-redirect-ref
335 27, // authentication-control
336 28, // authentication-info
337 29, // authorization
338 173, // c-pep-info
339 30, // cache-control
340 31, // cache-status
341 32, // cal-managed-id
342 33, // caldav-timezones
343 34, // capsule-protocol
344 35, // cdn-cache-control
345 36, // cdn-loop
346 37, // cert-not-after
347 38, // cert-not-before
348 39, // clear-site-data
349 40, // client-cert
350 41, // client-cert-chain
351 42, // close
352 43, // connection
353 44, // content-digest
354 45, // content-disposition
355 46, // content-encoding
356 47, // content-id
357 48, // content-language
358 49, // content-length
359 50, // content-location
360 51, // content-range
361 52, // content-security-policy
362 53, // content-security-policy-report-only
363 54, // content-type
364 55, // cookie
365 56, // cross-origin-embedder-policy
366 57, // cross-origin-embedder-policy-report-only
367 58, // cross-origin-opener-policy
368 59, // cross-origin-opener-policy-report-only
369 60, // cross-origin-resource-policy
370 61, // dasl
371 62, // date
372 63, // dav
373 64, // delta-base
374 65, // depth
375 66, // destination
376 67, // differential-id
377 68, // dpop
378 69, // dpop-nonce
379 70, // early-data
380 71, // etag
381 72, // expect
382 73, // expect-ct
383 74, // expires
384 75, // forwarded
385 76, // from
386 77, // hobareg
387 78, // host
388 79, // if
389 80, // if-match
390 81, // if-modified-since
391 82, // if-none-match
392 83, // if-range
393 84, // if-schedule-tag-match
394 85, // if-unmodified-since
395 86, // im
396 87, // include-referred-token-binding-id
397 88, // keep-alive
398 89, // label
399 90, // last-event-id
400 91, // last-modified
401 92, // link
402 93, // location
403 94, // lock-token
404 95, // max-forwards
405 96, // memento-datetime
406 97, // meter
407 98, // mime-version
408 99, // negotiate
409 100, // nel
410 101, // odata-entityid
411 102, // odata-isolation
412 103, // odata-maxversion
413 104, // odata-version
414 105, // optional-www-authenticate
415 106, // ordering-type
416 107, // origin
417 108, // origin-agent-cluster
418 109, // oscore
419 110, // oslc-core-version
420 111, // overwrite
421 112, // ping-from
422 113, // ping-to
423 114, // position
424 174, // pragma
425 115, // prefer
426 116, // preference-applied
427 117, // priority
428 175, // protocol-info
429 176, // protocol-query
430 118, // proxy-authenticate
431 119, // proxy-authentication-info
432 120, // proxy-authorization
433 121, // proxy-status
434 122, // public-key-pins
435 123, // public-key-pins-report-only
436 124, // range
437 125, // redirect-ref
438 126, // referer
439 127, // refresh
440 128, // replay-nonce
441 129, // repr-digest
442 130, // retry-after
443 131, // schedule-reply
444 132, // schedule-tag
445 133, // sec-purpose
446 134, // sec-token-binding
447 135, // sec-websocket-accept
448 136, // sec-websocket-extensions
449 137, // sec-websocket-key
450 138, // sec-websocket-protocol
451 139, // sec-websocket-version
452 140, // server
453 141, // server-timing
454 142, // set-cookie
455 143, // signature
456 144, // signature-input
457 145, // slug
458 146, // soapaction
459 147, // status-uri
460 148, // strict-transport-security
461 149, // sunset
462 150, // surrogate-capability
463 151, // surrogate-control
464 152, // tcn
465 153, // te
466 154, // timeout
467 155, // topic
468 156, // traceparent
469 157, // tracestate
470 158, // trailer
471 159, // transfer-encoding
472 160, // ttl
473 161, // upgrade
474 162, // urgency
475 163, // user-agent
476 164, // variant-vary
477 165, // vary
478 166, // via
479 167, // want-content-digest
480 168, // want-repr-digest
481 169, // www-authenticate
482 170, // x-content-type-options
483 171, // x-frame-options
484};
485static_assert(std::size(orderedHeaderNameIndexes) == size_t(headerNames.count()));
486static_assert(q20::is_sorted(std::begin(orderedHeaderNameIndexes),
487 std::end(orderedHeaderNameIndexes),
488 ByIndirectHeaderName{}));
489
676{
677 return QByteArray(s.data(), s.size());
678}
679
681{
682 return QByteArray(s.data(), s.size());
683}
684
686{
687 return s.toLatin1();
688}
689
691{
692 return name.visit([](auto name){ return fieldToByteArray(name); }).toLower();
693}
694
696{
700
702 {
703 auto nname = normalizedName(name);
704 if (auto h = HeaderName::toWellKnownHeader(nname))
705 data = *h;
706 else
707 data = std::move(nname);
708 }
709
710 // Returns an enum corresponding with the 'name' if possible. Uses binary search (O(logN)).
711 // The function doesn't normalize the data; needs to be done by the caller if needed
712 static std::optional<QHttpHeaders::WellKnownHeader> toWellKnownHeader(QByteArrayView name) noexcept
713 {
714 auto indexesBegin = std::cbegin(orderedHeaderNameIndexes);
715 auto indexesEnd = std::cend(orderedHeaderNameIndexes);
716
717 auto result = std::lower_bound(indexesBegin, indexesEnd, name, ByIndirectHeaderName{});
718
719 if (result != indexesEnd && name == headerNames[*result])
720 return static_cast<QHttpHeaders::WellKnownHeader>(*result);
721 return std::nullopt;
722 }
723
724 QByteArrayView asView() const noexcept
725 {
726 return std::visit([](const auto &arg) -> QByteArrayView {
727 using T = decltype(arg);
728 if constexpr (std::is_same_v<T, const QByteArray &>)
729 return arg;
730 else if constexpr (std::is_same_v<T, const QHttpHeaders::WellKnownHeader &>)
731 return headerNames.viewAt(qToUnderlying(arg));
732 else
733 static_assert(QtPrivate::type_dependent_false<T>());
734 }, data);
735 }
736
737 QByteArray asByteArray() const noexcept
738 {
739 return std::visit([](const auto &arg) -> QByteArray {
740 using T = decltype(arg);
741 if constexpr (std::is_same_v<T, const QByteArray &>) {
742 return arg;
743 } else if constexpr (std::is_same_v<T, const QHttpHeaders::WellKnownHeader &>) {
744 const auto view = headerNames.viewAt(qToUnderlying(arg));
745 return QByteArray::fromRawData(view.constData(), view.size());
746 } else {
747 static_assert(QtPrivate::type_dependent_false<T>());
748 }
749 }, data);
750 }
751
752private:
753 // Store the data as 'enum' whenever possible; more performant, and comparison relies on that
754 std::variant<QHttpHeaders::WellKnownHeader, QByteArray> data;
755
756 friend bool comparesEqual(const HeaderName &lhs, const HeaderName &rhs) noexcept
757 {
758 // Here we compare two std::variants, which will return false if the types don't match.
759 // That is beneficial here because we avoid unnecessary comparisons; but it also means
760 // we must always store the data as WellKnownHeader when possible (in other words, if
761 // we get a string that is mappable to a WellKnownHeader). To guard against accidental
762 // misuse, the 'data' is private and the constructors must be used.
763 return lhs.data == rhs.data;
764 }
766};
767
768// A clarification on case-sensitivity:
769// - Header *names* are case-insensitive; Content-Type and content-type are considered equal
770// - Header *values* are case-sensitive
771// (In addition, the HTTP/2 and HTTP/3 standards mandate that all headers must be lower-cased when
772// encoded into transmission)
777
779{
780 return [&name](const Header &header) { return header.name == name; };
781}
782
784{
785public:
787
788 // The 'Self' is supplied as parameter to static functions so that
789 // we can define common methods which 'detach()' the private itself.
790 using Self = QExplicitlySharedDataPointer<QHttpHeadersPrivate>;
791 static void removeAll(Self &d, const HeaderName &name);
792 static void replaceOrAppend(Self &d, const HeaderName &name, const QByteArray &value);
793
794 void combinedValue(const HeaderName &name, QByteArray &result) const;
795 void values(const HeaderName &name, QList<QByteArray> &result) const;
796 QByteArrayView value(const HeaderName &name, QByteArrayView defaultValue) const noexcept;
797
798 QList<Header> headers;
799};
800
803{
804 if (!d) {
805 d = new QHttpHeadersPrivate();
806 d->ref.ref();
807 } else if (d->ref.loadRelaxed() != 1) {
808 detach_helper();
809 }
810}
811
813{
814 const auto it = std::find_if(d->headers.cbegin(), d->headers.cend(), headerNameMatches(name));
815
816 if (it != d->headers.cend()) {
817 // Found something to remove, calculate offset so we can proceed from the match-location
818 const auto matchOffset = it - d->headers.cbegin();
819 d.detach();
820 // Rearrange all matches to the end and erase them
821 d->headers.erase(std::remove_if(d->headers.begin() + matchOffset, d->headers.end(),
823 d->headers.end());
824 }
825}
826
828{
829 const char* separator = "";
830 for (const auto &h : std::as_const(headers)) {
831 if (h.name == name) {
832 result.append(separator);
833 result.append(h.value);
834 separator = ", ";
835 }
836 }
837}
838
839void QHttpHeadersPrivate::values(const HeaderName &name, QList<QByteArray> &result) const
840{
841 for (const auto &h : std::as_const(headers)) {
842 if (h.name == name)
843 result.append(h.value);
844 }
845}
846
848{
849 for (const auto &h : std::as_const(headers)) {
850 if (h.name == name)
851 return h.value;
852 }
853 return defaultValue;
854}
855
857{
858 d.detach();
859 auto it = std::find_if(d->headers.begin(), d->headers.end(), headerNameMatches(name));
860 if (it != d->headers.end()) {
861 // Found something to replace => replace, and then rearrange any remaining
862 // matches to the end and erase them
863 it->value = value;
864 d->headers.erase(
865 std::remove_if(it + 1, d->headers.end(), headerNameMatches(name)),
866 d->headers.end());
867 } else {
868 // Found nothing to replace => append
869 d->headers.append(Header{name, value});
870 }
871}
872
877{
878}
879
886QHttpHeaders QHttpHeaders::fromListOfPairs(const QList<std::pair<QByteArray, QByteArray>> &headers)
887{
889 h.reserve(headers.size());
890 for (const auto &header : headers)
891 h.append(header.first, header.second);
892 return h;
893}
894
901QHttpHeaders QHttpHeaders::fromMultiMap(const QMultiMap<QByteArray, QByteArray> &headers)
902{
904 h.reserve(headers.size());
905 for (const auto &[name,value] : headers.asKeyValueRange())
906 h.append(name, value);
907 return h;
908}
909
916QHttpHeaders QHttpHeaders::fromMultiHash(const QMultiHash<QByteArray, QByteArray> &headers)
917{
919 h.reserve(headers.size());
920 for (const auto &[name,value] : headers.asKeyValueRange())
921 h.append(name, value);
922 return h;
923}
924
929 = default;
930
935 = default;
936
941 = default;
942
965#ifndef QT_NO_DEBUG_STREAM
973{
974 const QDebugStateSaver saver(debug);
975 debug.resetFormat().nospace();
976
977 debug << "QHttpHeaders(";
978 if (headers.d) {
979 debug << "headers = ";
980 const char *separator = "";
981 for (const auto &h : headers.d->headers) {
982 debug << separator << h.name.asView() << ':' << h.value;
983 separator = " | ";
984 }
985 }
986 debug << ")";
987 return debug;
988}
989#endif
990
991// A clarification on string encoding:
992// Setters and getters only accept names and values that are Latin-1 representable:
993// Either they are directly ASCII/Latin-1, or if they are UTF-X, they only use first 256
994// of the unicode points. For example using a '€' (U+20AC) in value would yield a warning
995// and the call is ignored.
996// Furthermore the 'name' has more strict rules than the 'value'
997
998// TODO FIXME REMOVEME once this is merged:
999// https://codereview.qt-project.org/c/qt/qtbase/+/508829
1001{
1002 // L1 encoded in UTF8 has at most the form
1003 // - 0b0XXX'XXXX - US-ASCII
1004 // - 0b1100'00XX 0b10XX'XXXX - at most 8 non-zero LSB bits allowed in L1
1005 bool inMultibyte = false;
1006 for (unsigned char c : s) {
1007 if (c < 128) { // US-ASCII
1008 if (inMultibyte)
1009 return false; // invalid sequence
1010 } else {
1011 // decode as UTF-8:
1012 if ((c & 0b1110'0000) == 0b1100'0000) { // two-octet UTF-8 leader
1013 if (inMultibyte)
1014 return false; // invalid sequence
1015 inMultibyte = true;
1016 const auto bits_7_to_11 = c & 0b0001'1111;
1017 if (bits_7_to_11 < 0b10)
1018 return false; // invalid sequence (US-ASCII encoded in two octets)
1019 if (bits_7_to_11 > 0b11) // more than the two LSB
1020 return false; // outside L1
1021 } else if ((c & 0b1100'0000) == 0b1000'0000) { // trailing UTF-8 octet
1022 if (!inMultibyte)
1023 return false; // invalid sequence
1024 inMultibyte = false; // only one continuation allowed
1025 } else {
1026 return false; // invalid sequence or outside of L1
1027 }
1028 }
1029 }
1030 if (inMultibyte)
1031 return false; // invalid sequence: premature end
1032 return true;
1033}
1034
1035static constexpr auto isValidHttpHeaderNameChar = [](uchar c) noexcept
1036{
1037 // RFC 9110 Chapters "5.1 Field Names" and "5.6.2 Tokens"
1038 // field-name = token
1039 // token = 1*tchar
1040 // tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" /
1041 // "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
1042 // / DIGIT / ALPHA
1043 // ; any VCHAR, except delimiters
1044 // (for explanation on VCHAR see isValidHttpHeaderValueChar)
1045 return (('A' <= c && c <= 'Z')
1046 || ('a' <= c && c <= 'z')
1047 || ('0' <= c && c <= '9')
1048 || ('#' <= c && c <= '\'')
1049 || ('^' <= c && c <= '`')
1050 || c == '|' || c == '~' || c == '!' || c == '*' || c == '+' || c == '-' || c == '.');
1051};
1052
1054{
1055 return std::all_of(name.begin(), name.end(), isValidHttpHeaderNameChar);
1056}
1057
1059{
1060 // Traversing the UTF-8 string char-by-char is fine in this case as
1061 // the isValidHttpHeaderNameChar rejects any value above 0x7E. UTF-8
1062 // only has bytes <= 0x7F if they truly represent that ASCII character.
1064}
1065
1067{
1068 return std::all_of(name.begin(), name.end(), [](QChar c) {
1069 return isValidHttpHeaderNameChar(c.toLatin1());
1070 });
1071}
1072
1074{
1075 if (name.isEmpty()) {
1076 qCWarning(lcQHttpHeaders, "HTTP header name cannot be empty");
1077 return false;
1078 }
1079 const bool valid = name.visit([](auto name){ return headerNameValidImpl(name); });
1080 if (!valid)
1081 qCWarning(lcQHttpHeaders, "HTTP header name contained illegal character(s)");
1082 return valid;
1083}
1084
1085static constexpr auto isValidHttpHeaderValueChar = [](uchar c) noexcept
1086{
1087 // RFC 9110 Chapter 5.5, Field Values
1088 // field-value = *field-content
1089 // field-content = field-vchar
1090 // [ 1*( SP / HTAB / field-vchar ) field-vchar ]
1091 // field-vchar = VCHAR / obs-text
1092 // obs-text = %x80-FF
1093 // VCHAR is defined as "any visible US-ASCII character", and RFC 5234 B.1.
1094 // defines it as %x21-7E
1095 // Note: The ABNF above states that field-content and thus field-value cannot
1096 // start or end with SP/HTAB. The caller should handle this.
1097 return (c >= 0x80 // obs-text (extended ASCII)
1098 || (0x20 <= c && c <= 0x7E) // SP (0x20) + VCHAR
1099 || (c == 0x09)); // HTAB
1100};
1101
1103{
1104 return std::all_of(value.begin(), value.end(), isValidHttpHeaderValueChar);
1105}
1106
1108{
1109 if (!isUtf8Latin1Representable(value)) // TODO FIXME see the function
1110 return false;
1111 return std::all_of(value.begin(), value.end(), isValidHttpHeaderValueChar);
1112}
1113
1115{
1116 return std::all_of(value.begin(), value.end(), [](QChar c) {
1117 return isValidHttpHeaderValueChar(c.toLatin1());
1118 });
1119}
1120
1122{
1123 const bool valid = value.visit([](auto value){ return headerValueValidImpl(value); });
1124 if (!valid)
1125 qCWarning(lcQHttpHeaders, "HTTP header value contained illegal character(s)");
1126 return valid;
1127}
1128
1130{
1131 // Note on trimming away any leading or trailing whitespace of 'value':
1132 // RFC 9110 (HTTP 1.1, 2022, Chapter 5.5) does not allow leading or trailing whitespace
1133 // RFC 7230 (HTTP 1.1, 2014, Chapter 3.2) allows them optionally, but also mandates that
1134 // they are ignored during processing
1135 // RFC 7540 (HTTP/2) does not seem explicit about it
1136 // => for maximum compatibility, trim away any leading or trailing whitespace
1137 return value.visit([](auto value){ return fieldToByteArray(value); }).trimmed();
1138}
1139
1148{
1150 return false;
1151
1152 d.detach();
1153 d->headers.push_back({HeaderName{name}, normalizedValue(value)});
1154 return true;
1155}
1156
1161{
1163 return false;
1164
1165 d.detach();
1166 d->headers.push_back({HeaderName{name}, normalizedValue(value)});
1167 return true;
1168}
1169
1179{
1180 verify(i, 0);
1182 return false;
1183
1184 d.detach();
1185 d->headers.insert(i, {HeaderName{name}, normalizedValue(value)});
1186 return true;
1187}
1188
1193{
1194 verify(i, 0);
1196 return false;
1197
1198 d.detach();
1199 d->headers.insert(i, {HeaderName{name}, normalizedValue(value)});
1200 return true;
1201}
1202
1213{
1214 verify(i);
1216 return false;
1217
1218 d.detach();
1219 d->headers.replace(i, {HeaderName{name}, normalizedValue(newValue)});
1220 return true;
1221}
1222
1227{
1228 verify(i);
1229 if (!isValidHttpHeaderValueField(newValue))
1230 return false;
1231
1232 d.detach();
1233 d->headers.replace(i, {HeaderName{name}, normalizedValue(newValue)});
1234 return true;
1235}
1236
1252{
1253 if (isEmpty())
1254 return append(name, newValue);
1255
1256 if (!isValidHttpHeaderValueField(newValue))
1257 return false;
1258
1260 return true;
1261}
1262
1267{
1268 if (isEmpty())
1269 return append(name, newValue);
1270
1272 return false;
1273
1275 return true;
1276}
1277
1284{
1285 if (isEmpty())
1286 return false;
1287
1288 return std::any_of(d->headers.cbegin(), d->headers.cend(), headerNameMatches(HeaderName{name}));
1289}
1290
1295{
1296 if (isEmpty())
1297 return false;
1298
1299 return std::any_of(d->headers.cbegin(), d->headers.cend(), headerNameMatches(HeaderName{name}));
1300}
1301
1308{
1309 if (isEmpty())
1310 return;
1311
1313}
1314
1319{
1320 if (isEmpty())
1321 return;
1322
1324}
1325
1334{
1335 verify(i);
1336 d.detach();
1337 d->headers.removeAt(i);
1338}
1339
1347{
1348 if (isEmpty())
1349 return defaultValue;
1350
1351 return d->value(HeaderName{name}, defaultValue);
1352}
1353
1358{
1359 if (isEmpty())
1360 return defaultValue;
1361
1362 return d->value(HeaderName{name}, defaultValue);
1363}
1364
1372{
1373 QList<QByteArray> result;
1374 if (isEmpty())
1375 return result;
1376
1377 d->values(HeaderName{name}, result);
1378 return result;
1379}
1380
1385{
1386 QList<QByteArray> result;
1387 if (isEmpty())
1388 return result;
1389
1390 d->values(HeaderName{name}, result);
1391 return result;
1392}
1393
1401{
1402 verify(i);
1403 return d->headers.at(i).value;
1404}
1405
1415{
1416 verify(i);
1417 return QLatin1StringView{d->headers.at(i).name.asView()};
1418}
1419
1433{
1435 if (isEmpty())
1436 return result;
1437
1438 d->combinedValue(HeaderName{name}, result);
1439 return result;
1440}
1441
1446{
1448 if (isEmpty())
1449 return result;
1450
1451 d->combinedValue(HeaderName{name}, result);
1452 return result;
1453}
1454
1459{
1460 if (!d)
1461 return 0;
1462 return d->headers.size();
1463}
1464
1473{
1474 d.detach();
1475 d->headers.reserve(size);
1476}
1477
1493
1498QList<std::pair<QByteArray, QByteArray>> QHttpHeaders::toListOfPairs() const
1499{
1500 QList<std::pair<QByteArray, QByteArray>> list;
1501 if (isEmpty())
1502 return list;
1503 list.reserve(size());
1504 for (const auto & h : std::as_const(d->headers))
1505 list.append({h.name.asByteArray(), h.value});
1506 return list;
1507}
1508
1513QMultiMap<QByteArray, QByteArray> QHttpHeaders::toMultiMap() const
1514{
1515 QMultiMap<QByteArray, QByteArray> map;
1516 if (isEmpty())
1517 return map;
1518 for (const auto &h : std::as_const(d->headers))
1519 map.insert(h.name.asByteArray(), h.value);
1520 return map;
1521}
1522
1527QMultiHash<QByteArray, QByteArray> QHttpHeaders::toMultiHash() const
1528{
1529 QMultiHash<QByteArray, QByteArray> hash;
1530 if (isEmpty())
1531 return hash;
1532 hash.reserve(size());
1533 for (const auto &h : std::as_const(d->headers))
1534 hash.insert(h.name.asByteArray(), h.value);
1535 return hash;
1536}
1537
1544{
1545 if (isEmpty())
1546 return;
1547 d.detach();
1548 d->headers.clear();
1549}
1550
\inmodule QtCore
\inmodule QtCore
Definition qbytearray.h:57
static QByteArray fromRawData(const char *data, qsizetype size)
Constructs a QByteArray that uses the first size bytes of the data array.
Definition qbytearray.h:409
\inmodule QtCore
\inmodule QtCore
\inmodule QtCore
void detach()
If the shared data object's reference count is greater than 1, this function creates a deep copy of t...
static void replaceOrAppend(Self &d, const HeaderName &name, const QByteArray &value)
QHttpHeadersPrivate()=default
QByteArrayView value(const HeaderName &name, QByteArrayView defaultValue) const noexcept
void combinedValue(const HeaderName &name, QByteArray &result) const
static void removeAll(Self &d, const HeaderName &name)
QList< Header > headers
void values(const HeaderName &name, QList< QByteArray > &result) const
Q_NETWORK_EXPORT qsizetype size() const noexcept
Returns the number of header entries.
Q_NETWORK_EXPORT void clear()
Clears all header entries.
Q_NETWORK_EXPORT bool insert(qsizetype i, QAnyStringView name, QAnyStringView value)
Inserts a header entry at index i, with name and value.
Q_NETWORK_EXPORT bool contains(QAnyStringView name) const
Returns whether the headers contain header with name.
Q_NETWORK_EXPORT ~QHttpHeaders()
Disposes of the headers object.
Q_NETWORK_EXPORT QMultiHash< QByteArray, QByteArray > toMultiHash() const
Returns the header entries as a hash from name to value(s).
Q_NETWORK_EXPORT bool append(QAnyStringView name, QAnyStringView value)
Appends a header entry with name and value and returns true if successful.
Q_NETWORK_EXPORT void removeAll(QAnyStringView name)
Removes the header name.
static Q_NETWORK_EXPORT QHttpHeaders fromListOfPairs(const QList< std::pair< QByteArray, QByteArray > > &headers)
Creates a new QHttpHeaders object that is populated with headers.
Q_NETWORK_EXPORT QMultiMap< QByteArray, QByteArray > toMultiMap() const
Returns the header entries as a map from name to value(s).
Q_NETWORK_EXPORT QHttpHeaders & operator=(const QHttpHeaders &other)
Assigns the contents of other and returns a reference to this object.
Q_NETWORK_EXPORT QHttpHeaders() noexcept
Creates a new QHttpHeaders object.
Q_NETWORK_EXPORT QList< QByteArray > values(QAnyStringView name) const
Returns the values of header name in a list.
Q_NETWORK_EXPORT bool replaceOrAppend(QAnyStringView name, QAnyStringView newValue)
Q_NETWORK_EXPORT QLatin1StringView nameAt(qsizetype i) const noexcept
Returns the header name at index i.
Q_NETWORK_EXPORT bool replace(qsizetype i, QAnyStringView name, QAnyStringView newValue)
Replaces the header entry at index i, with name and newValue.
static Q_NETWORK_EXPORT QHttpHeaders fromMultiMap(const QMultiMap< QByteArray, QByteArray > &headers)
Creates a new QHttpHeaders object that is populated with headers.
WellKnownHeader
List of well known headers as per \l {https://www.iana.org/assignments/http-fields}{IANA registry}.
Q_NETWORK_EXPORT void reserve(qsizetype size)
Attempts to allocate memory for at least size header entries.
Q_NETWORK_EXPORT void removeAt(qsizetype i)
Removes the header at index i.
Q_NETWORK_EXPORT QByteArrayView value(QAnyStringView name, QByteArrayView defaultValue={}) const noexcept
Returns the value of the (first) header name, or defaultValue if it doesn't exist.
Q_NETWORK_EXPORT QByteArray combinedValue(QAnyStringView name) const
Returns the values of header name in a comma-combined string.
Q_NETWORK_EXPORT QByteArrayView valueAt(qsizetype i) const noexcept
Returns the header value at index i.
Q_NETWORK_EXPORT QList< std::pair< QByteArray, QByteArray > > toListOfPairs() const
Returns the header entries as a list of (name, value) pairs.
static Q_NETWORK_EXPORT QHttpHeaders fromMultiHash(const QMultiHash< QByteArray, QByteArray > &headers)
Creates a new QHttpHeaders object that is populated with headers.
static Q_NETWORK_EXPORT QByteArrayView wellKnownHeaderName(WellKnownHeader name) noexcept
Returns a header name corresponding to the provided name as a view.
constexpr QLatin1Char at(qsizetype i) const
Definition qlist.h:75
void reserve(qsizetype size)
Definition qlist.h:753
void append(parameter_type t)
Definition qlist.h:458
iterator insert(const Key &key, const T &value)
Definition qmap.h:688
iterator end()
Definition qset.h:140
const_iterator cend() const noexcept
Definition qset.h:142
const_iterator cbegin() const noexcept
Definition qset.h:138
\inmodule QtCore
Definition qshareddata.h:19
\inmodule QtCore
Definition qstringview.h:78
QHash< int, QWidget * > hash
[35multi]
QMap< QString, QString > map
[6]
list append(new Employee("Blackpool", "Stephen"))
QSet< QString >::iterator it
Combined button and popup list for selecting options.
constexpr bool is_sorted(ForwardIterator first, ForwardIterator last, BinaryPredicate p={})
#define Q_DECLARE_EQUALITY_COMPARABLE(...)
static QString header(const QString &name)
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
static bool isUtf8Latin1Representable(QUtf8StringView s) noexcept
static QByteArray normalizedValue(QAnyStringView value)
static constexpr quint8 orderedHeaderNameIndexes[]
static bool isValidHttpHeaderValueField(QAnyStringView value) noexcept
auto headerNameMatches(const HeaderName &name)
static QByteArray normalizedName(QAnyStringView name)
static QByteArray fieldToByteArray(QLatin1StringView s) noexcept
static constexpr auto isValidHttpHeaderNameChar
static constexpr auto isValidHttpHeaderValueChar
static bool headerNameValidImpl(QLatin1StringView name) noexcept
static bool isValidHttpHeaderNameField(QAnyStringView name) noexcept
static constexpr auto headerNames
static bool headerValueValidImpl(QLatin1StringView value) noexcept
#define Q_LOGGING_CATEGORY(name,...)
#define qCWarning(category,...)
constexpr auto qOffsetStringArray(const char(&...strings)[Nx]) noexcept
GLenum GLsizei GLsizei GLint * values
[15]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint name
GLfloat GLfloat GLfloat GLfloat h
GLdouble s
[6]
Definition qopenglext.h:235
const GLubyte * c
GLuint64EXT * result
[6]
#define QT_DEFINE_QESDP_SPECIALIZATION_DTOR(Class)
SSL_CTX int void * arg
QT_BEGIN_NAMESPACE constexpr std::underlying_type_t< Enum > qToUnderlying(Enum e) noexcept
unsigned char uchar
Definition qtypes.h:32
ptrdiff_t qsizetype
Definition qtypes.h:165
unsigned char quint8
Definition qtypes.h:46
QList< int > list
[14]
QDataStream & operator<<(QDataStream &out, const MyClass &myObj)
[4]
QSharedPointer< T > other(t)
[5]
QQuickView * view
[0]
HeaderName(QHttpHeaders::WellKnownHeader name)
QByteArray asByteArray() const noexcept
friend bool comparesEqual(const HeaderName &lhs, const HeaderName &rhs) noexcept
HeaderName(QAnyStringView name)
QByteArrayView asView() const noexcept
static std::optional< QHttpHeaders::WellKnownHeader > toWellKnownHeader(QByteArrayView name) noexcept
QByteArray value
HeaderName name