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