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