Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qnetworkrequest.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 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// Qt-Security score:significant reason:default
4
7#include "qplatformdefs.h"
11#if QT_CONFIG(http)
12#include "qhttp1configuration.h"
13#include "qhttp2configuration.h"
14#include "private/http2protocol_p.h"
15#endif
16
17#include "QtCore/qdatetime.h"
18#include "QtCore/qlocale.h"
19#include "QtCore/qshareddata.h"
20#include "QtCore/qtimezone.h"
21#include "QtCore/private/qduplicatetracker_p.h"
22#include "QtCore/private/qtools_p.h"
23
24#include <ctype.h>
25#if QT_CONFIG(datestring)
26# include <stdio.h>
27#endif
28
29#include <algorithm>
30#include <q20algorithm.h>
31
33
34using namespace Qt::StringLiterals;
35using namespace std::chrono_literals;
36
37constexpr std::chrono::milliseconds QNetworkRequest::DefaultTransferTimeout;
38
40QT_IMPL_METATYPE_EXTERN_TAGGED(QNetworkRequest::RedirectPolicy, QNetworkRequest__RedirectPolicy)
41
42/*!
43 \class QNetworkRequest
44 \since 4.4
45 \ingroup network
46 \ingroup shared
47 \inmodule QtNetwork
48
49 \brief The QNetworkRequest class holds a request to be sent with QNetworkAccessManager.
50
51 QNetworkRequest is part of the Network Access API and is the class
52 holding the information necessary to send a request over the
53 network. It contains a URL and some ancillary information that can
54 be used to modify the request.
55
56 \sa QNetworkReply, QNetworkAccessManager
57*/
58
59/*!
60 \enum QNetworkRequest::KnownHeaders
61
62 List of known header types that QNetworkRequest parses. Each known
63 header is also represented in raw form with its full HTTP name.
64
65 \value ContentDispositionHeader Corresponds to the HTTP
66 Content-Disposition header and contains a string containing the
67 disposition type (for instance, attachment) and a parameter (for
68 instance, filename).
69
70 \value ContentTypeHeader Corresponds to the HTTP Content-Type
71 header and contains a string containing the media (MIME) type and
72 any auxiliary data (for instance, charset).
73
74 \value ContentLengthHeader Corresponds to the HTTP Content-Length
75 header and contains the length in bytes of the data transmitted.
76
77 \value LocationHeader Corresponds to the HTTP Location
78 header and contains a URL representing the actual location of the
79 data, including the destination URL in case of redirections.
80
81 \value LastModifiedHeader Corresponds to the HTTP Last-Modified
82 header and contains a QDateTime representing the last modification
83 date of the contents.
84
85 \value IfModifiedSinceHeader Corresponds to the HTTP If-Modified-Since
86 header and contains a QDateTime. It is usually added to a
87 QNetworkRequest. The server shall send a 304 (Not Modified) response
88 if the resource has not changed since this time.
89
90 \value ETagHeader Corresponds to the HTTP ETag
91 header and contains a QString representing the last modification
92 state of the contents.
93
94 \value IfMatchHeader Corresponds to the HTTP If-Match
95 header and contains a QStringList. It is usually added to a
96 QNetworkRequest. The server shall send a 412 (Precondition Failed)
97 response if the resource does not match.
98
99 \value IfNoneMatchHeader Corresponds to the HTTP If-None-Match
100 header and contains a QStringList. It is usually added to a
101 QNetworkRequest. The server shall send a 304 (Not Modified) response
102 if the resource does match.
103
104 \value CookieHeader Corresponds to the HTTP Cookie header
105 and contains a QList<QNetworkCookie> representing the cookies to
106 be sent back to the server.
107
108 \value SetCookieHeader Corresponds to the HTTP Set-Cookie
109 header and contains a QList<QNetworkCookie> representing the
110 cookies sent by the server to be stored locally.
111
112 \value UserAgentHeader The User-Agent header sent by HTTP clients.
113
114 \value ServerHeader The Server header received by HTTP clients.
115
116 \omitvalue NumKnownHeaders
117
118 \sa header(), setHeader(), rawHeader(), setRawHeader()
119*/
120
121/*!
122 \enum QNetworkRequest::Attribute
123 \since 4.7
124
125 Attribute codes for the QNetworkRequest and QNetworkReply.
126
127 Attributes are extra meta-data that are used to control the
128 behavior of the request and to pass further information from the
129 reply back to the application. Attributes are also extensible,
130 allowing custom implementations to pass custom values.
131
132 The following table explains what the default attribute codes are,
133 the QVariant types associated, the default value if said attribute
134 is missing and whether it's used in requests or replies.
135
136 \value HttpStatusCodeAttribute
137 Replies only, type: QMetaType::Int (no default)
138 Indicates the HTTP status code received from the HTTP server
139 (like 200, 304, 404, 401, etc.). If the connection was not
140 HTTP-based, this attribute will not be present.
141
142 \value HttpReasonPhraseAttribute
143 Replies only, type: QMetaType::QByteArray (no default)
144 Indicates the HTTP reason phrase as received from the HTTP
145 server (like "Ok", "Found", "Not Found", "Access Denied",
146 etc.) This is the human-readable representation of the status
147 code (see above). If the connection was not HTTP-based, this
148 attribute will not be present. \e{Note:} The reason phrase is
149 not used when using HTTP/2.
150
151 \value RedirectionTargetAttribute
152 Replies only, type: QMetaType::QUrl (no default)
153 If present, it indicates that the server is redirecting the
154 request to a different URL. The Network Access API does follow
155 redirections by default, unless
156 QNetworkRequest::ManualRedirectPolicy is used. Additionally, if
157 QNetworkRequest::UserVerifiedRedirectPolicy is used, then this
158 attribute will be set if the redirect was not followed.
159 The returned URL might be relative. Use QUrl::resolved()
160 to create an absolute URL out of it.
161
162 \value ConnectionEncryptedAttribute
163 Replies only, type: QMetaType::Bool (default: false)
164 Indicates whether the data was obtained through an encrypted
165 (secure) connection.
166
167 \value CacheLoadControlAttribute
168 Requests only, type: QMetaType::Int (default: QNetworkRequest::PreferNetwork)
169 Controls how the cache should be accessed. The possible values
170 are those of QNetworkRequest::CacheLoadControl. Note that the
171 default QNetworkAccessManager implementation does not support
172 caching. However, this attribute may be used by certain
173 backends to modify their requests (for example, for caching proxies).
174
175 \value CacheSaveControlAttribute
176 Requests only, type: QMetaType::Bool (default: true)
177 Controls if the data obtained should be saved to cache for
178 future uses. If the value is false, the data obtained will not
179 be automatically cached. If true, data may be cached, provided
180 it is cacheable (what is cacheable depends on the protocol
181 being used).
182
183 \value SourceIsFromCacheAttribute
184 Replies only, type: QMetaType::Bool (default: false)
185 Indicates whether the data was obtained from cache
186 or not.
187
188 \value DoNotBufferUploadDataAttribute
189 Requests only, type: QMetaType::Bool (default: false)
190 Indicates whether the QNetworkAccessManager code is
191 allowed to buffer the upload data, e.g. when doing a HTTP POST.
192 When using this flag with sequential upload data, the ContentLengthHeader
193 header must be set.
194
195 \value HttpPipeliningAllowedAttribute
196 Requests only, type: QMetaType::Bool (default: false)
197 Indicates whether the QNetworkAccessManager code is
198 allowed to use HTTP pipelining with this request.
199
200 \value HttpPipeliningWasUsedAttribute
201 Replies only, type: QMetaType::Bool
202 Indicates whether the HTTP pipelining was used for receiving
203 this reply.
204
205 \value CustomVerbAttribute
206 Requests only, type: QMetaType::QByteArray
207 Holds the value for the custom HTTP verb to send (destined for usage
208 of other verbs than GET, POST, PUT and DELETE). This verb is set
209 when calling QNetworkAccessManager::sendCustomRequest().
210
211 \value CookieLoadControlAttribute
212 Requests only, type: QMetaType::Int (default: QNetworkRequest::Automatic)
213 Indicates whether to send 'Cookie' headers in the request.
214 This attribute is set to false by Qt WebKit when creating a cross-origin
215 XMLHttpRequest where withCredentials has not been set explicitly to true by the
216 Javascript that created the request.
217 See \l{http://www.w3.org/TR/XMLHttpRequest2/#credentials-flag}{here} for more information.
218 (This value was introduced in 4.7.)
219
220 \value CookieSaveControlAttribute
221 Requests only, type: QMetaType::Int (default: QNetworkRequest::Automatic)
222 Indicates whether to save 'Cookie' headers received from the server in reply
223 to the request.
224 This attribute is set to false by Qt WebKit when creating a cross-origin
225 XMLHttpRequest where withCredentials has not been set explicitly to true by the
226 Javascript that created the request.
227 See \l{http://www.w3.org/TR/XMLHttpRequest2/#credentials-flag} {here} for more information.
228 (This value was introduced in 4.7.)
229
230 \value AuthenticationReuseAttribute
231 Requests only, type: QMetaType::Int (default: QNetworkRequest::Automatic)
232 Indicates whether to use cached authorization credentials in the request,
233 if available. If this is set to QNetworkRequest::Manual and the authentication
234 mechanism is 'Basic' or 'Digest', Qt will not send an 'Authorization' HTTP
235 header with any cached credentials it may have for the request's URL.
236 This attribute is set to QNetworkRequest::Manual by Qt WebKit when creating a cross-origin
237 XMLHttpRequest where withCredentials has not been set explicitly to true by the
238 Javascript that created the request.
239 See \l{http://www.w3.org/TR/XMLHttpRequest2/#credentials-flag} {here} for more information.
240 (This value was introduced in 4.7.)
241
242 \omitvalue MaximumDownloadBufferSizeAttribute
243
244 \omitvalue DownloadBufferAttribute
245
246 \omitvalue SynchronousRequestAttribute
247
248 \value BackgroundRequestAttribute
249 Type: QMetaType::Bool (default: false)
250 Indicates that this is a background transfer, rather than a user initiated
251 transfer. Depending on the platform, background transfers may be subject
252 to different policies.
253
254 \value Http2AllowedAttribute
255 Requests only, type: QMetaType::Bool (default: true)
256 Indicates whether the QNetworkAccessManager code is
257 allowed to use HTTP/2 with this request. This applies
258 to SSL requests or 'cleartext' HTTP/2 if Http2CleartextAllowedAttribute
259 is set.
260
261 \value Http2WasUsedAttribute
262 Replies only, type: QMetaType::Bool (default: false)
263 Indicates whether HTTP/2 was used for receiving this reply.
264 (This value was introduced in 5.9.)
265
266 \value EmitAllUploadProgressSignalsAttribute
267 Requests only, type: QMetaType::Bool (default: false)
268 Indicates whether all upload signals should be emitted.
269 By default, the uploadProgress signal is emitted only
270 in 100 millisecond intervals.
271 (This value was introduced in 5.5.)
272
273 \value OriginalContentLengthAttribute
274 Replies only, type QMetaType::Int
275 Holds the original content-length attribute before being invalidated and
276 removed from the header when the data is compressed and the request was
277 marked to be decompressed automatically.
278 (This value was introduced in 5.9.)
279
280 \value RedirectPolicyAttribute
281 Requests only, type: QMetaType::Int, should be one of the
282 QNetworkRequest::RedirectPolicy values
283 (default: NoLessSafeRedirectPolicy).
284 (This value was introduced in 5.9.)
285
286 \value Http2DirectAttribute
287 Requests only, type: QMetaType::Bool (default: false)
288 If set, this attribute will force QNetworkAccessManager to use
289 HTTP/2 protocol without initial HTTP/2 protocol negotiation.
290 Use of this attribute implies prior knowledge that a particular
291 server supports HTTP/2. The attribute works with SSL or with 'cleartext'
292 HTTP/2 if Http2CleartextAllowedAttribute is set.
293 If a server turns out to not support HTTP/2, when HTTP/2 direct
294 was specified, QNetworkAccessManager gives up, without attempting to
295 fall back to HTTP/1.1. If both Http2AllowedAttribute and
296 Http2DirectAttribute are set, Http2DirectAttribute takes priority.
297 (This value was introduced in 5.11.)
298
299 \omitvalue ResourceTypeAttribute
300
301 \value AutoDeleteReplyOnFinishAttribute
302 Requests only, type: QMetaType::Bool (default: false)
303 If set, this attribute will make QNetworkAccessManager delete
304 the QNetworkReply after having emitted "finished".
305 (This value was introduced in 5.14.)
306
307 \value ConnectionCacheExpiryTimeoutSecondsAttribute
308 Requests only, type: QMetaType::Int
309 To set when the TCP connections to a server (HTTP1 and HTTP2) should
310 be closed after the last pending request had been processed.
311 (This value was introduced in 6.3.)
312
313 \value Http2CleartextAllowedAttribute
314 Requests only, type: QMetaType::Bool (default: false)
315 If set, this attribute will tell QNetworkAccessManager to attempt
316 an upgrade to HTTP/2 over cleartext (also known as h2c).
317 Until Qt 7 the default value for this attribute can be overridden
318 to true by setting the QT_NETWORK_H2C_ALLOWED environment variable.
319 This attribute is ignored if the Http2AllowedAttribute is not set.
320 (This value was introduced in 6.3.)
321
322 \value UseCredentialsAttribute
323 Requests only, type: QMetaType::Bool (default: false)
324 Indicates if the underlying XMLHttpRequest cross-site Access-Control
325 requests should be made using credentials. Has no effect on
326 same-origin requests. This only affects the WebAssembly platform.
327 (This value was introduced in 6.5.)
328
329 \value FullLocalServerNameAttribute
330 Requests only, type: QMetaType::String
331 Holds the full local server name to be used for the underlying
332 QLocalSocket. This attribute is used by the QNetworkAccessManager
333 to connect to a specific local server, when QLocalSocket's behavior for
334 a simple name isn't enough. The URL in the QNetworkRequest must still
335 use unix+http: or local+http: scheme. And the hostname in the URL will
336 be used for the Host header in the HTTP request.
337 (This value was introduced in 6.8.)
338
339 \value User
340 Special type. Additional information can be passed in
341 QVariants with types ranging from User to UserMax. The default
342 implementation of Network Access will ignore any request
343 attributes in this range and it will not produce any
344 attributes in this range in replies. The range is reserved for
345 extensions of QNetworkAccessManager.
346
347 \value UserMax
348 Special type. See User.
349*/
350
351/*!
352 \enum QNetworkRequest::CacheLoadControl
353
354 Controls the caching mechanism of QNetworkAccessManager.
355
356 \value AlwaysNetwork always load from network and do not
357 check if the cache has a valid entry (similar to the
358 "Reload" feature in browsers); in addition, force intermediate
359 caches to re-validate.
360
361 \value PreferNetwork default value; load from the network
362 if the cached entry is older than the network entry. This will never
363 return stale data from the cache, but revalidate resources that
364 have become stale.
365
366 \value PreferCache load from cache if available,
367 otherwise load from network. Note that this can return possibly
368 stale (but not expired) items from cache.
369
370 \value AlwaysCache only load from cache, indicating error
371 if the item was not cached (i.e., off-line mode)
372*/
373
374/*!
375 \enum QNetworkRequest::LoadControl
376 \since 4.7
377
378 Indicates if an aspect of the request's loading mechanism has been
379 manually overridden, e.g. by Qt WebKit.
380
381 \value Automatic default value: indicates default behaviour.
382
383 \value Manual indicates behaviour has been manually overridden.
384*/
385
386/*!
387 \enum QNetworkRequest::RedirectPolicy
388 \since 5.9
389
390 Indicates whether the Network Access API should automatically follow a
391 HTTP redirect response or not.
392
393 \value ManualRedirectPolicy Not following any redirects.
394
395 \value NoLessSafeRedirectPolicy Default value: Only "http"->"http",
396 "http" -> "https" or "https" -> "https" redirects
397 are allowed.
398
399 \value SameOriginRedirectPolicy Require the same protocol, host and port.
400 Note, http://example.com and http://example.com:80
401 will fail with this policy (implicit/explicit ports
402 are considered to be a mismatch).
403
404 \value UserVerifiedRedirectPolicy Client decides whether to follow each
405 redirect by handling the redirected()
406 signal, emitting redirectAllowed() on
407 the QNetworkReply object to allow
408 the redirect or aborting/finishing it to
409 reject the redirect. This can be used,
410 for example, to ask the user whether to
411 accept the redirect, or to decide
412 based on some app-specific configuration.
413
414 \note When Qt handles redirects it will, for legacy and compatibility
415 reasons, issue the redirected request using GET when the server returns
416 a 301 or 302 response, regardless of the original method used, unless it was
417 HEAD.
418*/
419
420/*!
421 \enum QNetworkRequest::TransferTimeoutConstant
422 \since 5.15
423
424 A constant that can be used for enabling transfer
425 timeouts with a preset value.
426
427 \value DefaultTransferTimeoutConstant The transfer timeout in milliseconds.
428 Used if setTransferTimeout() is called
429 without an argument.
430
431 \sa QNetworkRequest::DefaultTransferTimeout
432 */
433
434/*!
435 \variable QNetworkRequest::DefaultTransferTimeout
436
437 The transfer timeout with \l {QNetworkRequest::TransferTimeoutConstant}
438 milliseconds. Used if setTransferTimeout() is called without an
439 argument.
440 */
441
442class QNetworkRequestPrivate: public QSharedData, public QNetworkHeadersPrivate
443{
444public:
445 static const int maxRedirectCount = 50;
446 inline QNetworkRequestPrivate()
447 : priority(QNetworkRequest::NormalPriority)
448#ifndef QT_NO_SSL
449 , sslConfiguration(nullptr)
450#endif
451 , maxRedirectsAllowed(maxRedirectCount)
452 { qRegisterMetaType<QNetworkRequest>(); }
453 ~QNetworkRequestPrivate()
454 {
455#ifndef QT_NO_SSL
456 delete sslConfiguration;
457#endif
458 }
459
460
461 QNetworkRequestPrivate(const QNetworkRequestPrivate &other)
462 : QSharedData(other), QNetworkHeadersPrivate(other)
463 {
464 url = other.url;
465 priority = other.priority;
466 maxRedirectsAllowed = other.maxRedirectsAllowed;
467#ifndef QT_NO_SSL
468 sslConfiguration = nullptr;
469 if (other.sslConfiguration)
470 sslConfiguration = new QSslConfiguration(*other.sslConfiguration);
471#endif
472 peerVerifyName = other.peerVerifyName;
473#if QT_CONFIG(http)
474 h1Configuration = other.h1Configuration;
475 h2Configuration = other.h2Configuration;
476 decompressedSafetyCheckThreshold = other.decompressedSafetyCheckThreshold;
477#endif
478 transferTimeout = other.transferTimeout;
479 }
480
481 inline bool operator==(const QNetworkRequestPrivate &other) const
482 {
483 return url == other.url &&
484 priority == other.priority &&
485 attributes == other.attributes &&
486 maxRedirectsAllowed == other.maxRedirectsAllowed &&
487 peerVerifyName == other.peerVerifyName
488#if QT_CONFIG(http)
489 && h1Configuration == other.h1Configuration
490 && h2Configuration == other.h2Configuration
491 && decompressedSafetyCheckThreshold == other.decompressedSafetyCheckThreshold
492#endif
493 && transferTimeout == other.transferTimeout
494 && QHttpHeadersHelper::compareStrict(httpHeaders, other.httpHeaders)
495 ;
496 // don't compare cookedHeaders
497 }
498
499 QUrl url;
500 QNetworkRequest::Priority priority;
501#ifndef QT_NO_SSL
502 mutable QSslConfiguration *sslConfiguration;
503#endif
504 int maxRedirectsAllowed;
505 QString peerVerifyName;
506#if QT_CONFIG(http)
507 QHttp1Configuration h1Configuration;
508 QHttp2Configuration h2Configuration;
509 qint64 decompressedSafetyCheckThreshold = 10ll * 1024ll * 1024ll;
510#endif
511 std::chrono::milliseconds transferTimeout = 0ms;
512};
513
514/*!
515 Constructs a QNetworkRequest object with no URL to be requested.
516 Use setUrl() to set one.
517
518 \sa url(), setUrl()
519*/
520QNetworkRequest::QNetworkRequest()
521 : d(new QNetworkRequestPrivate)
522{
523#if QT_CONFIG(http)
524 // Initial values proposed by RFC 7540 are quite draconian, but we
525 // know about servers configured with this value as maximum possible,
526 // rejecting our SETTINGS frame and sending us a GOAWAY frame with the
527 // flow control error set. If this causes a problem - the app should
528 // set a proper configuration. We'll use our defaults, as documented.
529 d->h2Configuration.setStreamReceiveWindowSize(Http2::qtDefaultStreamReceiveWindowSize);
530 d->h2Configuration.setSessionReceiveWindowSize(Http2::maxSessionReceiveWindowSize);
531 d->h2Configuration.setServerPushEnabled(false);
532#endif // QT_CONFIG(http)
533}
534
535/*!
536 Constructs a QNetworkRequest object with \a url as the URL to be
537 requested.
538
539 \sa url(), setUrl()
540*/
541QNetworkRequest::QNetworkRequest(const QUrl &url)
542 : QNetworkRequest()
543{
544 d->url = url;
545}
546
547/*!
548 Creates a copy of \a other.
549*/
550QNetworkRequest::QNetworkRequest(const QNetworkRequest &other)
551 : d(other.d)
552{
553}
554
555/*!
556 Disposes of the QNetworkRequest object.
557*/
558QNetworkRequest::~QNetworkRequest()
559{
560 // QSharedDataPointer auto deletes
561 d = nullptr;
562}
563
564/*!
565 Returns \c true if this object is the same as \a other (i.e., if they
566 have the same URL, same headers and same meta-data settings).
567
568 \sa operator!=()
569*/
570bool QNetworkRequest::operator==(const QNetworkRequest &other) const
571{
572 return d == other.d || *d == *other.d;
573}
574
575/*!
576 \fn bool QNetworkRequest::operator!=(const QNetworkRequest &other) const
577
578 Returns \c false if this object is not the same as \a other.
579
580 \sa operator==()
581*/
582
583/*!
584 Creates a copy of \a other
585*/
586QNetworkRequest &QNetworkRequest::operator=(const QNetworkRequest &other)
587{
588 d = other.d;
589 return *this;
590}
591
592/*!
593 \fn void QNetworkRequest::swap(QNetworkRequest &other)
594 \since 5.0
595 \memberswap{network request}
596*/
597
598/*!
599 Returns the URL this network request is referring to.
600
601 \sa setUrl()
602*/
603QUrl QNetworkRequest::url() const
604{
605 return d->url;
606}
607
608/*!
609 Sets the URL this network request is referring to be \a url.
610
611 \sa url()
612*/
613void QNetworkRequest::setUrl(const QUrl &url)
614{
615 d->url = url;
616}
617
618/*!
619 \since 6.8
620
621 Returns headers that are set in this network request.
622
623 \sa setHeaders()
624*/
625QHttpHeaders QNetworkRequest::headers() const
626{
627 return d->headers();
628}
629
630/*!
631 \since 6.8
632
633 Sets \a newHeaders as headers in this network request, overriding
634 any previously set headers.
635
636 If some headers correspond to the known headers, the values will
637 be parsed and the corresponding parsed form will also be set.
638
639 \sa headers(), KnownHeaders
640*/
641void QNetworkRequest::setHeaders(QHttpHeaders &&newHeaders)
642{
643 d->setHeaders(std::move(newHeaders));
644}
645
646/*!
647 \overload
648 \since 6.8
649*/
650void QNetworkRequest::setHeaders(const QHttpHeaders &newHeaders)
651{
652 d->setHeaders(newHeaders);
653}
654
655/*!
656 Returns the value of the known network header \a header if it is
657 present in this request. If it is not present, returns QVariant()
658 (i.e., an invalid variant).
659
660 \sa KnownHeaders, rawHeader(), setHeader()
661*/
662QVariant QNetworkRequest::header(KnownHeaders header) const
663{
664 return d->cookedHeaders.value(header);
665}
666
667/*!
668 Sets the value of the known header \a header to be \a value,
669 overriding any previously set headers. This operation also sets
670 the equivalent raw HTTP header.
671
672 \sa KnownHeaders, setRawHeader(), header()
673*/
674void QNetworkRequest::setHeader(KnownHeaders header, const QVariant &value)
675{
676 d->setCookedHeader(header, value);
677}
678
679/*!
680 Returns \c true if the raw header \a headerName is present in this
681 network request.
682
683 \sa rawHeader(), setRawHeader()
684 \note In Qt versions prior to 6.7, this function took QByteArray only.
685*/
686bool QNetworkRequest::hasRawHeader(QAnyStringView headerName) const
687{
688 return d->headers().contains(headerName);
689}
690
691/*!
692 Returns the raw form of header \a headerName. If no such header is
693 present, an empty QByteArray is returned, which may be
694 indistinguishable from a header that is present but has no content
695 (use hasRawHeader() to find out if the header exists or not).
696
697 Raw headers can be set with setRawHeader() or with setHeader().
698
699 \sa header(), setRawHeader()
700 \note In Qt versions prior to 6.7, this function took QByteArray only.
701*/
702QByteArray QNetworkRequest::rawHeader(QAnyStringView headerName) const
703{
704 return d->rawHeader(headerName);
705}
706
707/*!
708 Returns a list of all raw headers that are set in this network
709 request. The list is in the order that the headers were set.
710
711 \sa hasRawHeader(), rawHeader()
712*/
713QList<QByteArray> QNetworkRequest::rawHeaderList() const
714{
715 return d->rawHeadersKeys();
716}
717
718/*!
719 Sets the header \a headerName to be of value \a headerValue. If \a
720 headerName corresponds to a known header (see
721 QNetworkRequest::KnownHeaders), the raw format will be parsed and
722 the corresponding "cooked" header will be set as well.
723
724 For example:
725 \snippet code/src_network_access_qnetworkrequest.cpp 0
726
727 will also set the known header LastModifiedHeader to be the
728 QDateTime object of the parsed date.
729
730 \note Setting the same header twice overrides the previous
731 setting. To accomplish the behaviour of multiple HTTP headers of
732 the same name, you should concatenate the two values, separating
733 them with a comma (",") and set one single raw header.
734
735 \note Since Qt 6.8, the header field names are normalized
736 by converting them to lowercase.
737
738 \sa KnownHeaders, setHeader(), hasRawHeader(), rawHeader()
739*/
740void QNetworkRequest::setRawHeader(const QByteArray &headerName, const QByteArray &headerValue)
741{
742 d->setRawHeader(headerName, headerValue);
743}
744
745/*!
746 Returns the attribute associated with the code \a code. If the
747 attribute has not been set, it returns \a defaultValue.
748
749 \note This function does not apply the defaults listed in
750 QNetworkRequest::Attribute.
751
752 \sa setAttribute(), QNetworkRequest::Attribute
753*/
754QVariant QNetworkRequest::attribute(Attribute code, const QVariant &defaultValue) const
755{
756 return d->attributes.value(code, defaultValue);
757}
758
759/*!
760 Sets the attribute associated with code \a code to be value \a
761 value. If the attribute is already set, the previous value is
762 discarded. In special, if \a value is an invalid QVariant, the
763 attribute is unset.
764
765 \sa attribute(), QNetworkRequest::Attribute
766*/
767void QNetworkRequest::setAttribute(Attribute code, const QVariant &value)
768{
769 if (value.isValid())
770 d->attributes.insert(code, value);
771 else
772 d->attributes.remove(code);
773}
774
775#ifndef QT_NO_SSL
776/*!
777 Returns this network request's SSL configuration. By default this is the same
778 as QSslConfiguration::defaultConfiguration().
779
780 \sa setSslConfiguration(), QSslConfiguration::defaultConfiguration()
781*/
782QSslConfiguration QNetworkRequest::sslConfiguration() const
783{
784 if (!d->sslConfiguration)
785 d->sslConfiguration = new QSslConfiguration(QSslConfiguration::defaultConfiguration());
786 return *d->sslConfiguration;
787}
788
789/*!
790 Sets this network request's SSL configuration to be \a config. The
791 settings that apply are the private key, the local certificate,
792 the TLS protocol (e.g. TLS 1.3), the CA certificates and the ciphers that
793 the SSL backend is allowed to use.
794
795 \sa sslConfiguration(), QSslConfiguration::defaultConfiguration()
796*/
797void QNetworkRequest::setSslConfiguration(const QSslConfiguration &config)
798{
799 if (!d->sslConfiguration)
800 d->sslConfiguration = new QSslConfiguration(config);
801 else
802 *d->sslConfiguration = config;
803}
804#endif
805
806/*!
807 \since 4.6
808
809 Allows setting a reference to the \a object initiating
810 the request.
811
812 For example Qt WebKit sets the originating object to the
813 QWebFrame that initiated the request.
814
815 \sa originatingObject()
816*/
817void QNetworkRequest::setOriginatingObject(QObject *object)
818{
819 d->originatingObject = object;
820}
821
822/*!
823 \since 4.6
824
825 Returns a reference to the object that initiated this
826 network request; returns \nullptr if not set or the object has
827 been destroyed.
828
829 \sa setOriginatingObject()
830*/
831QObject *QNetworkRequest::originatingObject() const
832{
833 return d->originatingObject.data();
834}
835
836/*!
837 \since 4.7
838
839 Return the priority of this request.
840
841 \sa setPriority()
842*/
843QNetworkRequest::Priority QNetworkRequest::priority() const
844{
845 return d->priority;
846}
847
848/*! \enum QNetworkRequest::Priority
849
850 \since 4.7
851
852 This enum lists the possible network request priorities.
853
854 \value HighPriority High priority
855 \value NormalPriority Normal priority
856 \value LowPriority Low priority
857 */
858
859/*!
860 \since 4.7
861
862 Set the priority of this request to \a priority.
863
864 \note The \a priority is only a hint to the network access
865 manager. It can use it or not. Currently it is used for HTTP to
866 decide which request should be sent first to a server.
867
868 \sa priority()
869*/
870void QNetworkRequest::setPriority(Priority priority)
871{
872 d->priority = priority;
873}
874
875/*!
876 \since 5.6
877
878 Returns the maximum number of redirects allowed to be followed for this
879 request.
880
881 \sa setMaximumRedirectsAllowed()
882*/
883int QNetworkRequest::maximumRedirectsAllowed() const
884{
885 return d->maxRedirectsAllowed;
886}
887
888/*!
889 \since 5.6
890
891 Sets the maximum number of redirects allowed to be followed for this
892 request to \a maxRedirectsAllowed.
893
894 \sa maximumRedirectsAllowed()
895*/
896void QNetworkRequest::setMaximumRedirectsAllowed(int maxRedirectsAllowed)
897{
898 d->maxRedirectsAllowed = maxRedirectsAllowed;
899}
900
901/*!
902 \since 5.13
903
904 Returns the host name set for the certificate validation, as set by
905 setPeerVerifyName. By default this returns a null string.
906
907 \sa setPeerVerifyName
908*/
909QString QNetworkRequest::peerVerifyName() const
910{
911 return d->peerVerifyName;
912}
913
914/*!
915 \since 5.13
916
917 Sets \a peerName as host name for the certificate validation, instead of the one used for the
918 TCP connection.
919
920 \sa peerVerifyName
921*/
922void QNetworkRequest::setPeerVerifyName(const QString &peerName)
923{
924 d->peerVerifyName = peerName;
925}
926
927#if QT_CONFIG(http)
928/*!
929 \since 6.5
930
931 Returns the current parameters that QNetworkAccessManager is
932 using for the underlying HTTP/1 connection of this request.
933
934 \sa setHttp1Configuration
935*/
936QHttp1Configuration QNetworkRequest::http1Configuration() const
937{
938 return d->h1Configuration;
939}
940/*!
941 \since 6.5
942
943 Sets request's HTTP/1 parameters from \a configuration.
944
945 \sa http1Configuration, QNetworkAccessManager, QHttp1Configuration
946*/
947void QNetworkRequest::setHttp1Configuration(const QHttp1Configuration &configuration)
948{
949 d->h1Configuration = configuration;
950}
951
952/*!
953 \since 5.14
954
955 Returns the current parameters that QNetworkAccessManager is
956 using for this request and its underlying HTTP/2 connection.
957 This is either a configuration previously set by an application
958 or a default configuration.
959
960 The default values that QNetworkAccessManager is using are:
961
962 \list
963 \li Window size for connection-level flowcontrol is 2147483647 octets
964 \li Window size for stream-level flowcontrol is 214748364 octets
965 \li Max frame size is 16384
966 \endlist
967
968 By default, server push is disabled, Huffman compression and
969 string indexing are enabled.
970
971 \sa setHttp2Configuration
972*/
973QHttp2Configuration QNetworkRequest::http2Configuration() const
974{
975 return d->h2Configuration;
976}
977
978/*!
979 \since 5.14
980
981 Sets request's HTTP/2 parameters from \a configuration.
982
983 \note The configuration must be set prior to making a request.
984 \note HTTP/2 multiplexes several streams in a single HTTP/2
985 connection. This implies that QNetworkAccessManager will use
986 the configuration found in the first request from a series
987 of requests sent to the same host.
988
989 \sa http2Configuration, QNetworkAccessManager, QHttp2Configuration
990*/
991void QNetworkRequest::setHttp2Configuration(const QHttp2Configuration &configuration)
992{
993 d->h2Configuration = configuration;
994}
995
996/*!
997 \since 6.2
998
999 Returns the threshold for archive bomb checks.
1000
1001 If the decompressed size of a reply is smaller than this, Qt will simply
1002 decompress it, without further checking.
1003
1004 \sa setDecompressedSafetyCheckThreshold()
1005*/
1006qint64 QNetworkRequest::decompressedSafetyCheckThreshold() const
1007{
1008 return d->decompressedSafetyCheckThreshold;
1009}
1010
1011/*!
1012 \since 6.2
1013
1014 Sets the \a threshold for archive bomb checks.
1015
1016 Some supported compression algorithms can, in a tiny compressed file, encode
1017 a spectacularly huge decompressed file. This is only possible if the
1018 decompressed content is extremely monotonous, which is seldom the case for
1019 real files being transmitted in good faith: files exercising such insanely
1020 high compression ratios are typically payloads of buffer-overrun attacks, or
1021 denial-of-service (by using up too much memory) attacks. Consequently, files
1022 that decompress to huge sizes, particularly from tiny compressed forms, are
1023 best rejected as suspected malware.
1024
1025 If a reply's decompressed size is bigger than this threshold (by default,
1026 10 MiB, i.e. 10 * 1024 * 1024), Qt will check the compression ratio: if that
1027 is unreasonably large (40:1 for GZip and Deflate, or 100:1 for Brotli and
1028 ZStandard), the reply will be treated as an error. Setting the threshold
1029 to \c{-1} disables this check.
1030
1031 \sa decompressedSafetyCheckThreshold()
1032*/
1033void QNetworkRequest::setDecompressedSafetyCheckThreshold(qint64 threshold)
1034{
1035 d->decompressedSafetyCheckThreshold = threshold;
1036}
1037#endif // QT_CONFIG(http)
1038
1039#if QT_CONFIG(http) || defined (Q_OS_WASM)
1040/*!
1041 \fn int QNetworkRequest::transferTimeout() const
1042 \since 5.15
1043
1044 Returns the timeout used for transfers, in milliseconds.
1045
1046 If transferTimeoutAsDuration().count() cannot be represented in \c{int},
1047 this function returns \c{INT_MAX}/\c{INT_MIN} instead.
1048
1049 \sa setTransferTimeout(), transferTimeoutAsDuration()
1050*/
1051
1052/*!
1053 \fn void QNetworkRequest::setTransferTimeout(int timeout)
1054 \since 5.15
1055
1056 Sets \a timeout as the transfer timeout in milliseconds.
1057
1058 \sa setTransferTimeout(std::chrono::milliseconds),
1059 transferTimeout(), transferTimeoutAsDuration()
1060*/
1061
1062/*!
1063 \since 6.7
1064
1065 Returns the timeout duration after which the transfer is aborted if no
1066 data is exchanged.
1067
1068 The default duration is zero, which means that the timeout is not used.
1069
1070 \sa setTransferTimeout(std::chrono::milliseconds)
1071*/
1072std::chrono::milliseconds QNetworkRequest::transferTimeoutAsDuration() const
1073{
1074 return d->transferTimeout;
1075}
1076
1077/*!
1078 \since 6.7
1079
1080 Sets the timeout \a duration to abort the transfer if no data is exchanged.
1081
1082 Transfers are aborted if no bytes are transferred before
1083 the timeout expires. Zero means no timer is set. If no
1084 argument is provided, the timeout is
1085 QNetworkRequest::DefaultTransferTimeout. If this function
1086 is not called, the timeout is disabled and has the
1087 value zero.
1088
1089 \sa transferTimeoutAsDuration()
1090*/
1091void QNetworkRequest::setTransferTimeout(std::chrono::milliseconds duration)
1092{
1093 d->transferTimeout = duration;
1094}
1095#endif // QT_CONFIG(http) || defined (Q_OS_WASM)
1096
1097namespace {
1098
1099struct HeaderPair {
1100 QHttpHeaders::WellKnownHeader wellKnownHeader;
1101 QNetworkRequest::KnownHeaders knownHeader;
1102};
1103
1104constexpr bool operator<(const HeaderPair &lhs, const HeaderPair &rhs)
1105{
1106 return lhs.wellKnownHeader < rhs.wellKnownHeader;
1107}
1108
1109constexpr bool operator<(const HeaderPair &lhs, QHttpHeaders::WellKnownHeader rhs)
1110{
1111 return lhs.wellKnownHeader < rhs;
1112}
1113
1114constexpr bool operator<(QHttpHeaders::WellKnownHeader lhs, const HeaderPair &rhs)
1115{
1116 return lhs < rhs.wellKnownHeader;
1117}
1118
1119} // anonymous namespace
1120
1121static constexpr HeaderPair knownHeadersArr[] = {
1122 { QHttpHeaders::WellKnownHeader::ContentDisposition, QNetworkRequest::KnownHeaders::ContentDispositionHeader },
1123 { QHttpHeaders::WellKnownHeader::ContentLength, QNetworkRequest::KnownHeaders::ContentLengthHeader },
1124 { QHttpHeaders::WellKnownHeader::ContentType, QNetworkRequest::KnownHeaders::ContentTypeHeader },
1125 { QHttpHeaders::WellKnownHeader::Cookie, QNetworkRequest::KnownHeaders::CookieHeader },
1126 { QHttpHeaders::WellKnownHeader::ETag, QNetworkRequest::KnownHeaders::ETagHeader },
1127 { QHttpHeaders::WellKnownHeader::IfMatch , QNetworkRequest::KnownHeaders::IfMatchHeader },
1128 { QHttpHeaders::WellKnownHeader::IfModifiedSince, QNetworkRequest::KnownHeaders::IfModifiedSinceHeader },
1129 { QHttpHeaders::WellKnownHeader::IfNoneMatch, QNetworkRequest::KnownHeaders::IfNoneMatchHeader },
1130 { QHttpHeaders::WellKnownHeader::LastModified, QNetworkRequest::KnownHeaders::LastModifiedHeader},
1131 { QHttpHeaders::WellKnownHeader::Location, QNetworkRequest::KnownHeaders::LocationHeader},
1132 { QHttpHeaders::WellKnownHeader::Server, QNetworkRequest::KnownHeaders::ServerHeader },
1133 { QHttpHeaders::WellKnownHeader::SetCookie, QNetworkRequest::KnownHeaders::SetCookieHeader },
1134 { QHttpHeaders::WellKnownHeader::UserAgent, QNetworkRequest::KnownHeaders::UserAgentHeader }
1135};
1136
1137static_assert(std::size(knownHeadersArr) == size_t(QNetworkRequest::KnownHeaders::NumKnownHeaders));
1138static_assert(q20::is_sorted(std::begin(knownHeadersArr), std::end(knownHeadersArr)));
1139
1140static std::optional<QNetworkRequest::KnownHeaders> toKnownHeader(QHttpHeaders::WellKnownHeader key)
1141{
1142 const auto it = std::lower_bound(std::begin(knownHeadersArr), std::end(knownHeadersArr), key);
1143 if (it == std::end(knownHeadersArr) || key < *it)
1144 return std::nullopt;
1145 return it->knownHeader;
1146}
1147
1148static std::optional<QHttpHeaders::WellKnownHeader> toWellKnownHeader(QNetworkRequest::KnownHeaders key)
1149{
1150 auto pred = [key](const HeaderPair &pair) { return pair.knownHeader == key; };
1151 const auto it = std::find_if(std::begin(knownHeadersArr), std::end(knownHeadersArr), pred);
1152 if (it == std::end(knownHeadersArr))
1153 return std::nullopt;
1154 return it->wellKnownHeader;
1155}
1156
1157static QByteArray makeCookieHeader(const QList<QNetworkCookie> &cookies,
1158 QNetworkCookie::RawForm type,
1159 QByteArrayView separator)
1160{
1161 QByteArray result;
1162 for (const QNetworkCookie &cookie : cookies) {
1163 result += cookie.toRawForm(type);
1164 result += separator;
1165 }
1166 if (!result.isEmpty())
1167 result.chop(separator.size());
1168 return result;
1169}
1170
1171static QByteArray makeCookieHeader(const QVariant &value, QNetworkCookie::RawForm type,
1172 QByteArrayView separator)
1173{
1174 const QList<QNetworkCookie> *cookies = get_if<QList<QNetworkCookie>>(&value);
1175 if (!cookies)
1176 return {};
1177 return makeCookieHeader(*cookies, type, separator);
1178}
1179
1180static QByteArray headerValue(QNetworkRequest::KnownHeaders header, const QVariant &value)
1181{
1182 switch (header) {
1183 case QNetworkRequest::ContentTypeHeader:
1184 case QNetworkRequest::ContentLengthHeader:
1185 case QNetworkRequest::ContentDispositionHeader:
1186 case QNetworkRequest::UserAgentHeader:
1187 case QNetworkRequest::ServerHeader:
1188 case QNetworkRequest::ETagHeader:
1189 case QNetworkRequest::IfMatchHeader:
1190 case QNetworkRequest::IfNoneMatchHeader:
1191 return value.toByteArray();
1192
1193 case QNetworkRequest::LocationHeader:
1194 switch (value.userType()) {
1195 case QMetaType::QUrl:
1196 return value.toUrl().toEncoded();
1197
1198 default:
1199 return value.toByteArray();
1200 }
1201
1202 case QNetworkRequest::LastModifiedHeader:
1203 case QNetworkRequest::IfModifiedSinceHeader:
1204 switch (value.userType()) {
1205 // Generate RFC 1123/822 dates:
1206 case QMetaType::QDate:
1207 return QNetworkHeadersPrivate::toHttpDate(value.toDate().startOfDay(QTimeZone::UTC));
1208 case QMetaType::QDateTime:
1209 return QNetworkHeadersPrivate::toHttpDate(value.toDateTime());
1210
1211 default:
1212 return value.toByteArray();
1213 }
1214
1215 case QNetworkRequest::CookieHeader:
1216 return makeCookieHeader(value, QNetworkCookie::NameAndValueOnly, "; ");
1217
1218 case QNetworkRequest::SetCookieHeader:
1219 return makeCookieHeader(value, QNetworkCookie::Full, ", ");
1220
1221 default:
1222 Q_UNREACHABLE_RETURN({});
1223 }
1224}
1225
1226static int parseHeaderName(QByteArrayView headerName)
1227{
1228 if (headerName.isEmpty())
1229 return -1;
1230
1231 auto is = [headerName](QByteArrayView what) {
1232 return headerName.compare(what, Qt::CaseInsensitive) == 0;
1233 };
1234
1235 switch (QtMiscUtils::toAsciiLower(headerName.front())) {
1236 case 'c':
1237 if (is("content-type"))
1238 return QNetworkRequest::ContentTypeHeader;
1239 else if (is("content-length"))
1240 return QNetworkRequest::ContentLengthHeader;
1241 else if (is("cookie"))
1242 return QNetworkRequest::CookieHeader;
1243 else if (is("content-disposition"))
1244 return QNetworkRequest::ContentDispositionHeader;
1245 break;
1246
1247 case 'e':
1248 if (is("etag"))
1249 return QNetworkRequest::ETagHeader;
1250 break;
1251
1252 case 'i':
1253 if (is("if-modified-since"))
1254 return QNetworkRequest::IfModifiedSinceHeader;
1255 if (is("if-match"))
1256 return QNetworkRequest::IfMatchHeader;
1257 if (is("if-none-match"))
1258 return QNetworkRequest::IfNoneMatchHeader;
1259 break;
1260
1261 case 'l':
1262 if (is("location"))
1263 return QNetworkRequest::LocationHeader;
1264 else if (is("last-modified"))
1265 return QNetworkRequest::LastModifiedHeader;
1266 break;
1267
1268 case 's':
1269 if (is("set-cookie"))
1270 return QNetworkRequest::SetCookieHeader;
1271 else if (is("server"))
1272 return QNetworkRequest::ServerHeader;
1273 break;
1274
1275 case 'u':
1276 if (is("user-agent"))
1277 return QNetworkRequest::UserAgentHeader;
1278 break;
1279 }
1280
1281 return -1; // nothing found
1282}
1283
1284static QVariant parseHttpDate(QByteArrayView raw)
1285{
1286 QDateTime dt = QNetworkHeadersPrivate::fromHttpDate(raw);
1287 if (dt.isValid())
1288 return dt;
1289 return QVariant(); // transform an invalid QDateTime into a null QVariant
1290}
1291
1292static QList<QNetworkCookie> parseCookieHeader(QByteArrayView raw)
1293{
1294 QList<QNetworkCookie> result;
1295 for (auto cookie : QLatin1StringView(raw).tokenize(';'_L1)) {
1296 QList<QNetworkCookie> parsed = QNetworkCookie::parseCookies(cookie.trimmed());
1297 if (parsed.size() != 1)
1298 return {}; // invalid Cookie: header
1299
1300 result += parsed;
1301 }
1302
1303 return result;
1304}
1305
1306static QVariant parseETag(QByteArrayView raw)
1307{
1308 const QByteArrayView trimmed = raw.trimmed();
1309 if (!trimmed.startsWith('"') && !trimmed.startsWith(R"(W/")"))
1310 return QVariant();
1311
1312 if (!trimmed.endsWith('"'))
1313 return QVariant();
1314
1315 return QString::fromLatin1(trimmed);
1316}
1317
1318template<typename T>
1319static QStringList parseMatchImpl(QByteArrayView raw, T op)
1320{
1321 const QByteArrayView trimmedRaw = raw.trimmed();
1322 if (trimmedRaw == "*")
1323 return QStringList(QStringLiteral("*"));
1324
1325 QStringList tags;
1326 for (auto &element : QLatin1StringView(trimmedRaw).tokenize(','_L1)) {
1327 if (const auto trimmed = element.trimmed(); op(trimmed))
1328 tags += QString::fromLatin1(trimmed);
1329 }
1330 return tags;
1331}
1332
1333
1334static QStringList parseIfMatch(QByteArrayView raw)
1335{
1336 return parseMatchImpl(raw, [](QByteArrayView element) {
1337 return element.startsWith('"') && element.endsWith('"');
1338 });
1339}
1340
1341static QStringList parseIfNoneMatch(QByteArrayView raw)
1342{
1343 return parseMatchImpl(raw, [](QByteArrayView element) {
1344 return (element.startsWith('"') || element.startsWith(R"(W/")")) && element.endsWith('"');
1345 });
1346}
1347
1348
1349static QVariant parseHeaderValue(QNetworkRequest::KnownHeaders header, QByteArrayView value)
1350{
1351 // header is always a valid value
1352 switch (header) {
1353 case QNetworkRequest::UserAgentHeader:
1354 case QNetworkRequest::ServerHeader:
1355 case QNetworkRequest::ContentTypeHeader:
1356 case QNetworkRequest::ContentDispositionHeader:
1357 // copy exactly, convert to QString
1358 return QString::fromLatin1(value);
1359
1360 case QNetworkRequest::ContentLengthHeader: {
1361 bool ok;
1362 qint64 result = QByteArrayView(value).trimmed().toLongLong(&ok);
1363 if (ok)
1364 return result;
1365 return QVariant();
1366 }
1367
1368 case QNetworkRequest::LocationHeader: {
1369 QUrl result = QUrl::fromEncoded(value, QUrl::StrictMode);
1370 if (result.isValid() && !result.scheme().isEmpty())
1371 return result;
1372 return QVariant();
1373 }
1374
1375 case QNetworkRequest::LastModifiedHeader:
1376 case QNetworkRequest::IfModifiedSinceHeader:
1377 return parseHttpDate(value);
1378
1379 case QNetworkRequest::ETagHeader:
1380 return parseETag(value);
1381
1382 case QNetworkRequest::IfMatchHeader:
1383 return parseIfMatch(value);
1384
1385 case QNetworkRequest::IfNoneMatchHeader:
1386 return parseIfNoneMatch(value);
1387
1388 case QNetworkRequest::CookieHeader:
1389 return QVariant::fromValue(parseCookieHeader(value));
1390
1391 case QNetworkRequest::SetCookieHeader:
1392 return QVariant::fromValue(QNetworkCookie::parseCookies(value));
1393
1394 default:
1395 Q_UNREACHABLE_RETURN({});
1396 }
1397}
1398
1399static QVariant parseHeaderValue(QNetworkRequest::KnownHeaders header, QList<QByteArray> values)
1400{
1401 if (values.empty())
1402 return QVariant();
1403
1404 // header is always a valid value
1405 switch (header) {
1406 case QNetworkRequest::IfMatchHeader: {
1407 QStringList res;
1408 for (const auto &val : values)
1409 res << parseIfMatch(val);
1410 return res;
1411 }
1412 case QNetworkRequest::IfNoneMatchHeader: {
1413 QStringList res;
1414 for (const auto &val : values)
1415 res << parseIfNoneMatch(val);
1416 return res;
1417 }
1418 case QNetworkRequest::CookieHeader: {
1419 auto listOpt = QNetworkHeadersPrivate::toCookieList(values);
1420 return listOpt.has_value() ? QVariant::fromValue(listOpt.value()) : QVariant();
1421 }
1422 case QNetworkRequest::SetCookieHeader: {
1423 QList<QNetworkCookie> res;
1424 for (const auto &val : values)
1425 res << QNetworkCookie::parseCookies(val);
1426 return QVariant::fromValue(res);
1427 }
1428 default:
1429 return parseHeaderValue(header, values.first());
1430 }
1431 return QVariant();
1432}
1433
1434static bool isSetCookie(QByteArrayView name)
1435{
1436 return name.compare(QHttpHeaders::wellKnownHeaderName(QHttpHeaders::WellKnownHeader::SetCookie),
1437 Qt::CaseInsensitive) == 0;
1438}
1439
1440static bool isSetCookie(QHttpHeaders::WellKnownHeader name)
1441{
1442 return name == QHttpHeaders::WellKnownHeader::SetCookie;
1443}
1444
1445template<class HeaderName>
1446static void setFromRawHeader(QHttpHeaders &headers, HeaderName header,
1447 QByteArrayView value)
1448{
1449 headers.removeAll(header);
1450
1451 if (value.isNull())
1452 // only wanted to erase key
1453 return;
1454
1455 if (isSetCookie(header)) {
1456 for (auto cookie : QLatin1StringView(value).tokenize('\n'_L1))
1457 headers.append(QHttpHeaders::WellKnownHeader::SetCookie, cookie);
1458 } else {
1459 headers.append(header, value);
1460 }
1461}
1462
1464{
1465 if (rawHeaderCache.isCached)
1466 return rawHeaderCache.headersList;
1467
1468 rawHeaderCache.headersList = fromHttpToRaw(httpHeaders);
1469 rawHeaderCache.isCached = true;
1470 return rawHeaderCache.headersList;
1471}
1472
1474{
1475 if (httpHeaders.isEmpty())
1476 return {};
1477
1478 QList<QByteArray> result;
1479 result.reserve(httpHeaders.size());
1480 QDuplicateTracker<QByteArray> seen(httpHeaders.size());
1481
1482 for (qsizetype i = 0; i < httpHeaders.size(); i++) {
1483 const auto nameL1 = httpHeaders.nameAt(i);
1484 const auto name = QByteArray(nameL1.data(), nameL1.size());
1485 if (seen.hasSeen(name))
1486 continue;
1487
1488 result << name;
1489 }
1490
1491 return result;
1492}
1493
1494QByteArray QNetworkHeadersPrivate::rawHeader(QAnyStringView headerName) const
1495{
1496 QByteArrayView setCookieStr = QHttpHeaders::wellKnownHeaderName(
1497 QHttpHeaders::WellKnownHeader::SetCookie);
1498 if (QAnyStringView::compare(headerName, setCookieStr, Qt::CaseInsensitive) != 0)
1499 return httpHeaders.combinedValue(headerName);
1500
1501 QByteArray result;
1502 const char* separator = "";
1503 for (qsizetype i = 0; i < httpHeaders.size(); ++i) {
1504 if (QAnyStringView::compare(httpHeaders.nameAt(i), headerName, Qt::CaseInsensitive) == 0) {
1505 result.append(separator);
1506 result.append(httpHeaders.valueAt(i));
1507 separator = "\n";
1508 }
1509 }
1510 return result;
1511}
1512
1513void QNetworkHeadersPrivate::setRawHeader(const QByteArray &key, const QByteArray &value)
1514{
1515 if (key.isEmpty())
1516 // refuse to accept an empty raw header
1517 return;
1518
1519 setFromRawHeader(httpHeaders, key, value);
1520 parseAndSetHeader(key, value);
1521
1522 invalidateHeaderCache();
1523}
1524
1525void QNetworkHeadersPrivate::setCookedHeader(QNetworkRequest::KnownHeaders header,
1526 const QVariant &value)
1527{
1528 const auto wellKnownOpt = toWellKnownHeader(header);
1529 if (!wellKnownOpt) {
1530 // verifies that \a header is a known value
1531 qWarning("QNetworkRequest::setHeader: invalid header value KnownHeader(%d) received", header);
1532 return;
1533 }
1534
1535 if (value.isNull()) {
1536 httpHeaders.removeAll(wellKnownOpt.value());
1537 cookedHeaders.remove(header);
1538 } else {
1539 QByteArray rawValue = headerValue(header, value);
1540 if (rawValue.isEmpty()) {
1541 qWarning("QNetworkRequest::setHeader: QVariant of type %s cannot be used with header %s",
1542 value.typeName(),
1543 QHttpHeaders::wellKnownHeaderName(wellKnownOpt.value()).constData());
1544 return;
1545 }
1546
1547 setFromRawHeader(httpHeaders, wellKnownOpt.value(), rawValue);
1548 cookedHeaders.insert(header, value);
1549 }
1550
1551 invalidateHeaderCache();
1552}
1553
1555{
1556 return httpHeaders;
1557}
1558
1559void QNetworkHeadersPrivate::setHeaders(const QHttpHeaders &newHeaders)
1560{
1561 httpHeaders = newHeaders;
1562 setCookedFromHttp(httpHeaders);
1563 invalidateHeaderCache();
1564}
1565
1566void QNetworkHeadersPrivate::setHeaders(QHttpHeaders &&newHeaders)
1567{
1568 httpHeaders = std::move(newHeaders);
1569 setCookedFromHttp(httpHeaders);
1570 invalidateHeaderCache();
1571}
1572
1573void QNetworkHeadersPrivate::setHeader(QHttpHeaders::WellKnownHeader name, QByteArrayView value)
1574{
1575 httpHeaders.replaceOrAppend(name, value);
1576
1577 // set cooked header
1578 const auto knownHeaderOpt = toKnownHeader(name);
1579 if (knownHeaderOpt)
1580 parseAndSetHeader(knownHeaderOpt.value(), value);
1581
1582 invalidateHeaderCache();
1583}
1584
1586{
1587 httpHeaders.clear();
1588 cookedHeaders.clear();
1589 invalidateHeaderCache();
1590}
1591
1592void QNetworkHeadersPrivate::parseAndSetHeader(QByteArrayView key, QByteArrayView value)
1593{
1594 // is it a known header?
1595 const int parsedKeyAsInt = parseHeaderName(key);
1596 if (parsedKeyAsInt != -1) {
1597 const QNetworkRequest::KnownHeaders parsedKey
1598 = static_cast<QNetworkRequest::KnownHeaders>(parsedKeyAsInt);
1599 parseAndSetHeader(parsedKey, value);
1600 }
1601}
1602
1603void QNetworkHeadersPrivate::parseAndSetHeader(QNetworkRequest::KnownHeaders key,
1604 QByteArrayView value)
1605{
1606 if (value.isNull()) {
1607 cookedHeaders.remove(key);
1608 } else if (key == QNetworkRequest::ContentLengthHeader
1609 && cookedHeaders.contains(QNetworkRequest::ContentLengthHeader)) {
1610 // Only set the cooked header "Content-Length" once.
1611 // See bug QTBUG-15311
1612 } else {
1613 cookedHeaders.insert(key, parseHeaderValue(key, value));
1614 }
1615}
1616
1617// Fast month string to int conversion. This code
1618// assumes that the Month name is correct and that
1619// the string is at least three chars long.
1620static int name_to_month(const char* month_str)
1621{
1622 switch (month_str[0]) {
1623 case 'J':
1624 switch (month_str[1]) {
1625 case 'a':
1626 return 1;
1627 case 'u':
1628 switch (month_str[2] ) {
1629 case 'n':
1630 return 6;
1631 case 'l':
1632 return 7;
1633 }
1634 }
1635 break;
1636 case 'F':
1637 return 2;
1638 case 'M':
1639 switch (month_str[2] ) {
1640 case 'r':
1641 return 3;
1642 case 'y':
1643 return 5;
1644 }
1645 break;
1646 case 'A':
1647 switch (month_str[1]) {
1648 case 'p':
1649 return 4;
1650 case 'u':
1651 return 8;
1652 }
1653 break;
1654 case 'O':
1655 return 10;
1656 case 'S':
1657 return 9;
1658 case 'N':
1659 return 11;
1660 case 'D':
1661 return 12;
1662 }
1663
1664 return 0;
1665}
1666
1667QDateTime QNetworkHeadersPrivate::fromHttpDate(QByteArrayView value)
1668{
1669 // HTTP dates have three possible formats:
1670 // RFC 1123/822 - ddd, dd MMM yyyy hh:mm:ss "GMT"
1671 // RFC 850 - dddd, dd-MMM-yy hh:mm:ss "GMT"
1672 // ANSI C's asctime - ddd MMM d hh:mm:ss yyyy
1673 // We only handle them exactly. If they deviate, we bail out.
1674
1675 int pos = value.indexOf(',');
1676 QDateTime dt;
1677#if QT_CONFIG(datestring)
1678 if (pos == -1) {
1679 // no comma -> asctime(3) format
1680 dt = QDateTime::fromString(QString::fromLatin1(value), Qt::TextDate);
1681 } else {
1682 // Use sscanf over QLocal/QDateTimeParser for speed reasons. See the
1683 // Qt WebKit performance benchmarks to get an idea.
1684 if (pos == 3) {
1685 char month_name[4];
1686 int day, year, hour, minute, second;
1687#ifdef Q_CC_MSVC
1688 // Use secure version to avoid compiler warning
1689 if (sscanf_s(value.constData(), "%*3s, %d %3s %d %d:%d:%d 'GMT'", &day, month_name, 4, &year, &hour, &minute, &second) == 6)
1690#else
1691 // The POSIX secure mode is %ms (which allocates memory), too bleeding edge for now
1692 // In any case this is already safe as field width is specified.
1693 if (sscanf(value.constData(), "%*3s, %d %3s %d %d:%d:%d 'GMT'", &day, month_name, &year, &hour, &minute, &second) == 6)
1694#endif
1695 dt = QDateTime(QDate(year, name_to_month(month_name), day), QTime(hour, minute, second));
1696 } else {
1697 QLocale c = QLocale::c();
1698 // eat the weekday, the comma and the space following it
1699 QString sansWeekday = QString::fromLatin1(value.constData() + pos + 2);
1700 // must be RFC 850 date
1701 dt = c.toDateTime(sansWeekday, "dd-MMM-yy hh:mm:ss 'GMT'"_L1);
1702 }
1703 }
1704#endif // datestring
1705
1706 if (dt.isValid())
1707 dt.setTimeZone(QTimeZone::UTC);
1708 return dt;
1709}
1710
1711QByteArray QNetworkHeadersPrivate::toHttpDate(const QDateTime &dt)
1712{
1713 return QLocale::c().toString(dt.toUTC(), u"ddd, dd MMM yyyy hh:mm:ss 'GMT'").toLatin1();
1714}
1715
1717 const QHttpHeaders &headers)
1718{
1719 if (headers.isEmpty())
1720 return {};
1721
1723 QHash<QByteArray, qsizetype> nameToIndex;
1724 list.reserve(headers.size());
1725 nameToIndex.reserve(headers.size());
1726
1727 for (qsizetype i = 0; i < headers.size(); ++i) {
1728 const auto nameL1 = headers.nameAt(i);
1729 const auto value = headers.valueAt(i);
1730
1731 const bool isSetCookie = nameL1 == QHttpHeaders::wellKnownHeaderName(
1732 QHttpHeaders::WellKnownHeader::SetCookie);
1733
1734 const auto name = QByteArray(nameL1.data(), nameL1.size());
1735 if (auto it = nameToIndex.find(name); it != nameToIndex.end()) {
1736 list[it.value()].second += isSetCookie ? "\n" : ", ";
1737 list[it.value()].second += value;
1738 } else {
1739 nameToIndex[name] = list.size();
1740 list.emplaceBack(name, value.toByteArray());
1741 }
1742 }
1743
1744 return list;
1745}
1746
1747QHttpHeaders QNetworkHeadersPrivate::fromRawToHttp(const RawHeadersList &raw)
1748{
1749 if (raw.empty())
1750 return {};
1751
1752 QHttpHeaders headers;
1753 headers.reserve(raw.size());
1754
1755 for (const auto &[key, value] : raw) {
1756 const bool isSetCookie = key.compare(QHttpHeaders::wellKnownHeaderName(
1757 QHttpHeaders::WellKnownHeader::SetCookie),
1758 Qt::CaseInsensitive) == 0;
1759 if (isSetCookie) {
1760 for (auto header : QLatin1StringView(value).tokenize('\n'_L1))
1761 headers.append(key, header);
1762 } else {
1763 headers.append(key, value);
1764 }
1765 }
1766
1767 return headers;
1768}
1769
1770std::optional<qint64> QNetworkHeadersPrivate::toInt(QByteArrayView value)
1771{
1772 if (value.empty())
1773 return std::nullopt;
1774
1775 bool ok;
1776 qint64 res = value.toLongLong(&ok);
1777 if (ok)
1778 return res;
1779 return std::nullopt;
1780}
1781
1782std::optional<QNetworkHeadersPrivate::NetworkCookieList> QNetworkHeadersPrivate::toSetCookieList(
1783 const QList<QByteArray> &values)
1784{
1785 if (values.empty())
1786 return std::nullopt;
1787
1788 QList<QNetworkCookie> cookies;
1789 for (const auto &s : values)
1790 cookies += QNetworkCookie::parseCookies(s);
1791
1792 if (cookies.empty())
1793 return std::nullopt;
1794 return cookies;
1795}
1796
1797QByteArray QNetworkHeadersPrivate::fromCookieList(const QList<QNetworkCookie> &cookies)
1798{
1799 return makeCookieHeader(cookies, QNetworkCookie::NameAndValueOnly, "; ");
1800}
1801
1802std::optional<QNetworkHeadersPrivate::NetworkCookieList> QNetworkHeadersPrivate::toCookieList(
1803 const QList<QByteArray> &values)
1804{
1805 if (values.empty())
1806 return std::nullopt;
1807
1808 QList<QNetworkCookie> cookies;
1809 for (const auto &s : values)
1810 cookies += parseCookieHeader(s);
1811
1812 if (cookies.empty())
1813 return std::nullopt;
1814 return cookies;
1815}
1816
1817void QNetworkHeadersPrivate::invalidateHeaderCache()
1818{
1819 rawHeaderCache.headersList.clear();
1820 rawHeaderCache.isCached = false;
1821}
1822
1823void QNetworkHeadersPrivate::setCookedFromHttp(const QHttpHeaders &newHeaders)
1824{
1825 cookedHeaders.clear();
1826
1827 QMap<QNetworkRequest::KnownHeaders, QList<QByteArray>> multipleHeadersMap;
1828 for (int i = 0; i < newHeaders.size(); ++i) {
1829 const auto name = newHeaders.nameAt(i);
1830 const auto value = newHeaders.valueAt(i);
1831
1832 const int parsedKeyAsInt = parseHeaderName(name);
1833 if (parsedKeyAsInt == -1)
1834 continue;
1835
1836 const QNetworkRequest::KnownHeaders parsedKey
1837 = static_cast<QNetworkRequest::KnownHeaders>(parsedKeyAsInt);
1838
1839 auto &list = multipleHeadersMap[parsedKey];
1840 list.append(value.toByteArray());
1841 }
1842
1843 for (auto i = multipleHeadersMap.cbegin(), end = multipleHeadersMap.cend(); i != end; ++i)
1844 cookedHeaders.insert(i.key(), parseHeaderValue(i.key(), i.value()));
1845}
1846
1847QT_END_NAMESPACE
1848
1849#include "moc_qnetworkrequest.cpp"
QByteArray rawHeader(QAnyStringView headerName) const
void setCookedHeader(QNetworkRequest::KnownHeaders header, const QVariant &value)
QList< QByteArray > rawHeadersKeys() const
QHttpHeaders headers() const
void setHeaders(QHttpHeaders &&newHeaders)
void setHeaders(const QHttpHeaders &newHeaders)
const RawHeadersList & allRawHeaders() const
void setHeader(QHttpHeaders::WellKnownHeader name, QByteArrayView value)
QList< RawHeaderPair > RawHeadersList
CookedHeadersMap cookedHeaders
The QNetworkRequest class holds a request to be sent with QNetworkAccessManager.
static int name_to_month(const char *month_str)
static QStringList parseMatchImpl(QByteArrayView raw, T op)
static bool isSetCookie(QByteArrayView name)
static QList< QNetworkCookie > parseCookieHeader(QByteArrayView raw)
static int parseHeaderName(QByteArrayView headerName)
static std::optional< QNetworkRequest::KnownHeaders > toKnownHeader(QHttpHeaders::WellKnownHeader key)
static QVariant parseETag(QByteArrayView raw)
static QVariant parseHeaderValue(QNetworkRequest::KnownHeaders header, QByteArrayView value)
static QVariant parseHttpDate(QByteArrayView raw)
static QByteArray headerValue(QNetworkRequest::KnownHeaders header, const QVariant &value)
static QByteArray makeCookieHeader(const QList< QNetworkCookie > &cookies, QNetworkCookie::RawForm type, QByteArrayView separator)
static QStringList parseIfMatch(QByteArrayView raw)
static QStringList parseIfNoneMatch(QByteArrayView raw)
static void setFromRawHeader(QHttpHeaders &headers, HeaderName header, QByteArrayView value)
static std::optional< QHttpHeaders::WellKnownHeader > toWellKnownHeader(QNetworkRequest::KnownHeaders key)
static constexpr HeaderPair knownHeadersArr[]