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
6#include "qplatformdefs.h"
10#if QT_CONFIG(http)
11#include "qhttp1configuration.h"
12#include "qhttp2configuration.h"
13#include "private/http2protocol_p.h"
14#endif
15
16#include "QtCore/qdatetime.h"
17#include "QtCore/qlocale.h"
18#include "QtCore/qshareddata.h"
19#include "QtCore/qtimezone.h"
20#include "QtCore/private/qduplicatetracker_p.h"
21#include "QtCore/private/qtools_p.h"
22
23#include <ctype.h>
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 setTimeout() 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 }
479
480 inline bool operator==(const QNetworkRequestPrivate &other) const
481 {
482 return url == other.url &&
483 priority == other.priority &&
484 attributes == other.attributes &&
485 maxRedirectsAllowed == other.maxRedirectsAllowed &&
486 peerVerifyName == other.peerVerifyName
487#if QT_CONFIG(http)
488 && h1Configuration == other.h1Configuration
489 && h2Configuration == other.h2Configuration
490 && decompressedSafetyCheckThreshold == other.decompressedSafetyCheckThreshold
491#endif
492 && transferTimeout == other.transferTimeout
493 && QHttpHeadersHelper::compareStrict(httpHeaders, other.httpHeaders)
494 ;
495 // don't compare cookedHeaders
496 }
497
498 QUrl url;
499 QNetworkRequest::Priority priority;
500#ifndef QT_NO_SSL
501 mutable QSslConfiguration *sslConfiguration;
502#endif
503 int maxRedirectsAllowed;
504 QString peerVerifyName;
505#if QT_CONFIG(http)
506 QHttp1Configuration h1Configuration;
507 QHttp2Configuration h2Configuration;
508 qint64 decompressedSafetyCheckThreshold = 10ll * 1024ll * 1024ll;
509#endif
510 std::chrono::milliseconds transferTimeout = 0ms;
511};
512
513/*!
514 Constructs a QNetworkRequest object with no URL to be requested.
515 Use setUrl() to set one.
516
517 \sa url(), setUrl()
518*/
519QNetworkRequest::QNetworkRequest()
520 : d(new QNetworkRequestPrivate)
521{
522#if QT_CONFIG(http)
523 // Initial values proposed by RFC 7540 are quite draconian, but we
524 // know about servers configured with this value as maximum possible,
525 // rejecting our SETTINGS frame and sending us a GOAWAY frame with the
526 // flow control error set. If this causes a problem - the app should
527 // set a proper configuration. We'll use our defaults, as documented.
528 d->h2Configuration.setStreamReceiveWindowSize(Http2::qtDefaultStreamReceiveWindowSize);
529 d->h2Configuration.setSessionReceiveWindowSize(Http2::maxSessionReceiveWindowSize);
530 d->h2Configuration.setServerPushEnabled(false);
531#endif // QT_CONFIG(http)
532}
533
534/*!
535 Constructs a QNetworkRequest object with \a url as the URL to be
536 requested.
537
538 \sa url(), setUrl()
539*/
540QNetworkRequest::QNetworkRequest(const QUrl &url)
541 : QNetworkRequest()
542{
543 d->url = url;
544}
545
546/*!
547 Creates a copy of \a other.
548*/
549QNetworkRequest::QNetworkRequest(const QNetworkRequest &other)
550 : d(other.d)
551{
552}
553
554/*!
555 Disposes of the QNetworkRequest object.
556*/
557QNetworkRequest::~QNetworkRequest()
558{
559 // QSharedDataPointer auto deletes
560 d = nullptr;
561}
562
563/*!
564 Returns \c true if this object is the same as \a other (i.e., if they
565 have the same URL, same headers and same meta-data settings).
566
567 \sa operator!=()
568*/
569bool QNetworkRequest::operator==(const QNetworkRequest &other) const
570{
571 return d == other.d || *d == *other.d;
572}
573
574/*!
575 \fn bool QNetworkRequest::operator!=(const QNetworkRequest &other) const
576
577 Returns \c false if this object is not the same as \a other.
578
579 \sa operator==()
580*/
581
582/*!
583 Creates a copy of \a other
584*/
585QNetworkRequest &QNetworkRequest::operator=(const QNetworkRequest &other)
586{
587 d = other.d;
588 return *this;
589}
590
591/*!
592 \fn void QNetworkRequest::swap(QNetworkRequest &other)
593 \since 5.0
594 \memberswap{network request}
595*/
596
597/*!
598 Returns the URL this network request is referring to.
599
600 \sa setUrl()
601*/
602QUrl QNetworkRequest::url() const
603{
604 return d->url;
605}
606
607/*!
608 Sets the URL this network request is referring to be \a url.
609
610 \sa url()
611*/
612void QNetworkRequest::setUrl(const QUrl &url)
613{
614 d->url = url;
615}
616
617/*!
618 \since 6.8
619
620 Returns headers that are set in this network request.
621
622 \sa setHeaders()
623*/
624QHttpHeaders QNetworkRequest::headers() const
625{
626 return d->headers();
627}
628
629/*!
630 \since 6.8
631
632 Sets \a newHeaders as headers in this network request, overriding
633 any previously set headers.
634
635 If some headers correspond to the known headers, the values will
636 be parsed and the corresponding parsed form will also be set.
637
638 \sa headers(), KnownHeaders
639*/
640void QNetworkRequest::setHeaders(QHttpHeaders &&newHeaders)
641{
642 d->setHeaders(std::move(newHeaders));
643}
644
645/*!
646 \overload
647 \since 6.8
648*/
649void QNetworkRequest::setHeaders(const QHttpHeaders &newHeaders)
650{
651 d->setHeaders(newHeaders);
652}
653
654/*!
655 Returns the value of the known network header \a header if it is
656 present in this request. If it is not present, returns QVariant()
657 (i.e., an invalid variant).
658
659 \sa KnownHeaders, rawHeader(), setHeader()
660*/
661QVariant QNetworkRequest::header(KnownHeaders header) const
662{
663 return d->cookedHeaders.value(header);
664}
665
666/*!
667 Sets the value of the known header \a header to be \a value,
668 overriding any previously set headers. This operation also sets
669 the equivalent raw HTTP header.
670
671 \sa KnownHeaders, setRawHeader(), header()
672*/
673void QNetworkRequest::setHeader(KnownHeaders header, const QVariant &value)
674{
675 d->setCookedHeader(header, value);
676}
677
678/*!
679 Returns \c true if the raw header \a headerName is present in this
680 network request.
681
682 \sa rawHeader(), setRawHeader()
683 \note In Qt versions prior to 6.7, this function took QByteArray only.
684*/
685bool QNetworkRequest::hasRawHeader(QAnyStringView headerName) const
686{
687 return d->headers().contains(headerName);
688}
689
690/*!
691 Returns the raw form of header \a headerName. If no such header is
692 present, an empty QByteArray is returned, which may be
693 indistinguishable from a header that is present but has no content
694 (use hasRawHeader() to find out if the header exists or not).
695
696 Raw headers can be set with setRawHeader() or with setHeader().
697
698 \sa header(), setRawHeader()
699 \note In Qt versions prior to 6.7, this function took QByteArray only.
700*/
701QByteArray QNetworkRequest::rawHeader(QAnyStringView headerName) const
702{
703 return d->rawHeader(headerName);
704}
705
706/*!
707 Returns a list of all raw headers that are set in this network
708 request. The list is in the order that the headers were set.
709
710 \sa hasRawHeader(), rawHeader()
711*/
712QList<QByteArray> QNetworkRequest::rawHeaderList() const
713{
714 return d->rawHeadersKeys();
715}
716
717/*!
718 Sets the header \a headerName to be of value \a headerValue. If \a
719 headerName corresponds to a known header (see
720 QNetworkRequest::KnownHeaders), the raw format will be parsed and
721 the corresponding "cooked" header will be set as well.
722
723 For example:
724 \snippet code/src_network_access_qnetworkrequest.cpp 0
725
726 will also set the known header LastModifiedHeader to be the
727 QDateTime object of the parsed date.
728
729 \note Setting the same header twice overrides the previous
730 setting. To accomplish the behaviour of multiple HTTP headers of
731 the same name, you should concatenate the two values, separating
732 them with a comma (",") and set one single raw header.
733
734 \note Since Qt 6.8, the header field names are normalized
735 by converting them to lowercase.
736
737 \sa KnownHeaders, setHeader(), hasRawHeader(), rawHeader()
738*/
739void QNetworkRequest::setRawHeader(const QByteArray &headerName, const QByteArray &headerValue)
740{
741 d->setRawHeader(headerName, headerValue);
742}
743
744/*!
745 Returns the attribute associated with the code \a code. If the
746 attribute has not been set, it returns \a defaultValue.
747
748 \note This function does not apply the defaults listed in
749 QNetworkRequest::Attribute.
750
751 \sa setAttribute(), QNetworkRequest::Attribute
752*/
753QVariant QNetworkRequest::attribute(Attribute code, const QVariant &defaultValue) const
754{
755 return d->attributes.value(code, defaultValue);
756}
757
758/*!
759 Sets the attribute associated with code \a code to be value \a
760 value. If the attribute is already set, the previous value is
761 discarded. In special, if \a value is an invalid QVariant, the
762 attribute is unset.
763
764 \sa attribute(), QNetworkRequest::Attribute
765*/
766void QNetworkRequest::setAttribute(Attribute code, const QVariant &value)
767{
768 if (value.isValid())
769 d->attributes.insert(code, value);
770 else
771 d->attributes.remove(code);
772}
773
774#ifndef QT_NO_SSL
775/*!
776 Returns this network request's SSL configuration. By default this is the same
777 as QSslConfiguration::defaultConfiguration().
778
779 \sa setSslConfiguration(), QSslConfiguration::defaultConfiguration()
780*/
781QSslConfiguration QNetworkRequest::sslConfiguration() const
782{
783 if (!d->sslConfiguration)
784 d->sslConfiguration = new QSslConfiguration(QSslConfiguration::defaultConfiguration());
785 return *d->sslConfiguration;
786}
787
788/*!
789 Sets this network request's SSL configuration to be \a config. The
790 settings that apply are the private key, the local certificate,
791 the TLS protocol (e.g. TLS 1.3), the CA certificates and the ciphers that
792 the SSL backend is allowed to use.
793
794 \sa sslConfiguration(), QSslConfiguration::defaultConfiguration()
795*/
796void QNetworkRequest::setSslConfiguration(const QSslConfiguration &config)
797{
798 if (!d->sslConfiguration)
799 d->sslConfiguration = new QSslConfiguration(config);
800 else
801 *d->sslConfiguration = config;
802}
803#endif
804
805/*!
806 \since 4.6
807
808 Allows setting a reference to the \a object initiating
809 the request.
810
811 For example Qt WebKit sets the originating object to the
812 QWebFrame that initiated the request.
813
814 \sa originatingObject()
815*/
816void QNetworkRequest::setOriginatingObject(QObject *object)
817{
818 d->originatingObject = object;
819}
820
821/*!
822 \since 4.6
823
824 Returns a reference to the object that initiated this
825 network request; returns \nullptr if not set or the object has
826 been destroyed.
827
828 \sa setOriginatingObject()
829*/
830QObject *QNetworkRequest::originatingObject() const
831{
832 return d->originatingObject.data();
833}
834
835/*!
836 \since 4.7
837
838 Return the priority of this request.
839
840 \sa setPriority()
841*/
842QNetworkRequest::Priority QNetworkRequest::priority() const
843{
844 return d->priority;
845}
846
847/*! \enum QNetworkRequest::Priority
848
849 \since 4.7
850
851 This enum lists the possible network request priorities.
852
853 \value HighPriority High priority
854 \value NormalPriority Normal priority
855 \value LowPriority Low priority
856 */
857
858/*!
859 \since 4.7
860
861 Set the priority of this request to \a priority.
862
863 \note The \a priority is only a hint to the network access
864 manager. It can use it or not. Currently it is used for HTTP to
865 decide which request should be sent first to a server.
866
867 \sa priority()
868*/
869void QNetworkRequest::setPriority(Priority priority)
870{
871 d->priority = priority;
872}
873
874/*!
875 \since 5.6
876
877 Returns the maximum number of redirects allowed to be followed for this
878 request.
879
880 \sa setMaximumRedirectsAllowed()
881*/
882int QNetworkRequest::maximumRedirectsAllowed() const
883{
884 return d->maxRedirectsAllowed;
885}
886
887/*!
888 \since 5.6
889
890 Sets the maximum number of redirects allowed to be followed for this
891 request to \a maxRedirectsAllowed.
892
893 \sa maximumRedirectsAllowed()
894*/
895void QNetworkRequest::setMaximumRedirectsAllowed(int maxRedirectsAllowed)
896{
897 d->maxRedirectsAllowed = maxRedirectsAllowed;
898}
899
900/*!
901 \since 5.13
902
903 Returns the host name set for the certificate validation, as set by
904 setPeerVerifyName. By default this returns a null string.
905
906 \sa setPeerVerifyName
907*/
908QString QNetworkRequest::peerVerifyName() const
909{
910 return d->peerVerifyName;
911}
912
913/*!
914 \since 5.13
915
916 Sets \a peerName as host name for the certificate validation, instead of the one used for the
917 TCP connection.
918
919 \sa peerVerifyName
920*/
921void QNetworkRequest::setPeerVerifyName(const QString &peerName)
922{
923 d->peerVerifyName = peerName;
924}
925
926#if QT_CONFIG(http)
927/*!
928 \since 6.5
929
930 Returns the current parameters that QNetworkAccessManager is
931 using for the underlying HTTP/1 connection of this request.
932
933 \sa setHttp1Configuration
934*/
935QHttp1Configuration QNetworkRequest::http1Configuration() const
936{
937 return d->h1Configuration;
938}
939/*!
940 \since 6.5
941
942 Sets request's HTTP/1 parameters from \a configuration.
943
944 \sa http1Configuration, QNetworkAccessManager, QHttp1Configuration
945*/
946void QNetworkRequest::setHttp1Configuration(const QHttp1Configuration &configuration)
947{
948 d->h1Configuration = configuration;
949}
950
951/*!
952 \since 5.14
953
954 Returns the current parameters that QNetworkAccessManager is
955 using for this request and its underlying HTTP/2 connection.
956 This is either a configuration previously set by an application
957 or a default configuration.
958
959 The default values that QNetworkAccessManager is using are:
960
961 \list
962 \li Window size for connection-level flowcontrol is 2147483647 octets
963 \li Window size for stream-level flowcontrol is 214748364 octets
964 \li Max frame size is 16384
965 \endlist
966
967 By default, server push is disabled, Huffman compression and
968 string indexing are enabled.
969
970 \sa setHttp2Configuration
971*/
972QHttp2Configuration QNetworkRequest::http2Configuration() const
973{
974 return d->h2Configuration;
975}
976
977/*!
978 \since 5.14
979
980 Sets request's HTTP/2 parameters from \a configuration.
981
982 \note The configuration must be set prior to making a request.
983 \note HTTP/2 multiplexes several streams in a single HTTP/2
984 connection. This implies that QNetworkAccessManager will use
985 the configuration found in the first request from a series
986 of requests sent to the same host.
987
988 \sa http2Configuration, QNetworkAccessManager, QHttp2Configuration
989*/
990void QNetworkRequest::setHttp2Configuration(const QHttp2Configuration &configuration)
991{
992 d->h2Configuration = configuration;
993}
994
995/*!
996 \since 6.2
997
998 Returns the threshold for archive bomb checks.
999
1000 If the decompressed size of a reply is smaller than this, Qt will simply
1001 decompress it, without further checking.
1002
1003 \sa setDecompressedSafetyCheckThreshold()
1004*/
1005qint64 QNetworkRequest::decompressedSafetyCheckThreshold() const
1006{
1007 return d->decompressedSafetyCheckThreshold;
1008}
1009
1010/*!
1011 \since 6.2
1012
1013 Sets the \a threshold for archive bomb checks.
1014
1015 Some supported compression algorithms can, in a tiny compressed file, encode
1016 a spectacularly huge decompressed file. This is only possible if the
1017 decompressed content is extremely monotonous, which is seldom the case for
1018 real files being transmitted in good faith: files exercising such insanely
1019 high compression ratios are typically payloads of buffer-overrun attacks, or
1020 denial-of-service (by using up too much memory) attacks. Consequently, files
1021 that decompress to huge sizes, particularly from tiny compressed forms, are
1022 best rejected as suspected malware.
1023
1024 If a reply's decompressed size is bigger than this threshold (by default,
1025 10 MiB, i.e. 10 * 1024 * 1024), Qt will check the compression ratio: if that
1026 is unreasonably large (40:1 for GZip and Deflate, or 100:1 for Brotli and
1027 ZStandard), the reply will be treated as an error. Setting the threshold
1028 to \c{-1} disables this check.
1029
1030 \sa decompressedSafetyCheckThreshold()
1031*/
1032void QNetworkRequest::setDecompressedSafetyCheckThreshold(qint64 threshold)
1033{
1034 d->decompressedSafetyCheckThreshold = threshold;
1035}
1036#endif // QT_CONFIG(http)
1037
1038#if QT_CONFIG(http) || defined (Q_OS_WASM)
1039/*!
1040 \fn int QNetworkRequest::transferTimeout() const
1041 \since 5.15
1042
1043 Returns the timeout used for transfers, in milliseconds.
1044
1045 If transferTimeoutAsDuration().count() cannot be represented in \c{int},
1046 this function returns \c{INT_MAX}/\c{INT_MIN} instead.
1047
1048 \sa setTransferTimeout(), transferTimeoutAsDuration()
1049*/
1050
1051/*!
1052 \fn void QNetworkRequest::setTransferTimeout(int timeout)
1053 \since 5.15
1054
1055 Sets \a timeout as the transfer timeout in milliseconds.
1056
1057 \sa setTransferTimeout(std::chrono::milliseconds),
1058 transferTimeout(), transferTimeoutAsDuration()
1059*/
1060
1061/*!
1062 \since 6.7
1063
1064 Returns the timeout duration after which the transfer is aborted if no
1065 data is exchanged.
1066
1067 The default duration is zero, which means that the timeout is not used.
1068
1069 \sa setTransferTimeout(std::chrono::milliseconds)
1070*/
1071std::chrono::milliseconds QNetworkRequest::transferTimeoutAsDuration() const
1072{
1073 return d->transferTimeout;
1074}
1075
1076/*!
1077 \since 6.7
1078
1079 Sets the timeout \a duration to abort the transfer if no data is exchanged.
1080
1081 Transfers are aborted if no bytes are transferred before
1082 the timeout expires. Zero means no timer is set. If no
1083 argument is provided, the timeout is
1084 QNetworkRequest::DefaultTransferTimeout. If this function
1085 is not called, the timeout is disabled and has the
1086 value zero.
1087
1088 \sa transferTimeoutAsDuration()
1089*/
1090void QNetworkRequest::setTransferTimeout(std::chrono::milliseconds duration)
1091{
1092 d->transferTimeout = duration;
1093}
1094#endif // QT_CONFIG(http) || defined (Q_OS_WASM)
1095
1096namespace {
1097
1098struct HeaderPair {
1099 QHttpHeaders::WellKnownHeader wellKnownHeader;
1100 QNetworkRequest::KnownHeaders knownHeader;
1101};
1102
1103constexpr bool operator<(const HeaderPair &lhs, const HeaderPair &rhs)
1104{
1105 return lhs.wellKnownHeader < rhs.wellKnownHeader;
1106}
1107
1108constexpr bool operator<(const HeaderPair &lhs, QHttpHeaders::WellKnownHeader rhs)
1109{
1110 return lhs.wellKnownHeader < rhs;
1111}
1112
1113constexpr bool operator<(QHttpHeaders::WellKnownHeader lhs, const HeaderPair &rhs)
1114{
1115 return lhs < rhs.wellKnownHeader;
1116}
1117
1118} // anonymous namespace
1119
1120static constexpr HeaderPair knownHeadersArr[] = {
1121 { QHttpHeaders::WellKnownHeader::ContentDisposition, QNetworkRequest::KnownHeaders::ContentDispositionHeader },
1122 { QHttpHeaders::WellKnownHeader::ContentLength, QNetworkRequest::KnownHeaders::ContentLengthHeader },
1123 { QHttpHeaders::WellKnownHeader::ContentType, QNetworkRequest::KnownHeaders::ContentTypeHeader },
1124 { QHttpHeaders::WellKnownHeader::Cookie, QNetworkRequest::KnownHeaders::CookieHeader },
1125 { QHttpHeaders::WellKnownHeader::ETag, QNetworkRequest::KnownHeaders::ETagHeader },
1126 { QHttpHeaders::WellKnownHeader::IfMatch , QNetworkRequest::KnownHeaders::IfMatchHeader },
1127 { QHttpHeaders::WellKnownHeader::IfModifiedSince, QNetworkRequest::KnownHeaders::IfModifiedSinceHeader },
1128 { QHttpHeaders::WellKnownHeader::IfNoneMatch, QNetworkRequest::KnownHeaders::IfNoneMatchHeader },
1129 { QHttpHeaders::WellKnownHeader::LastModified, QNetworkRequest::KnownHeaders::LastModifiedHeader},
1130 { QHttpHeaders::WellKnownHeader::Location, QNetworkRequest::KnownHeaders::LocationHeader},
1131 { QHttpHeaders::WellKnownHeader::Server, QNetworkRequest::KnownHeaders::ServerHeader },
1132 { QHttpHeaders::WellKnownHeader::SetCookie, QNetworkRequest::KnownHeaders::SetCookieHeader },
1133 { QHttpHeaders::WellKnownHeader::UserAgent, QNetworkRequest::KnownHeaders::UserAgentHeader }
1134};
1135
1136static_assert(std::size(knownHeadersArr) == size_t(QNetworkRequest::KnownHeaders::NumKnownHeaders));
1137static_assert(q20::is_sorted(std::begin(knownHeadersArr), std::end(knownHeadersArr)));
1138
1139static std::optional<QNetworkRequest::KnownHeaders> toKnownHeader(QHttpHeaders::WellKnownHeader key)
1140{
1141 const auto it = std::lower_bound(std::begin(knownHeadersArr), std::end(knownHeadersArr), key);
1142 if (it == std::end(knownHeadersArr) || key < *it)
1143 return std::nullopt;
1144 return it->knownHeader;
1145}
1146
1147static std::optional<QHttpHeaders::WellKnownHeader> toWellKnownHeader(QNetworkRequest::KnownHeaders key)
1148{
1149 auto pred = [key](const HeaderPair &pair) { return pair.knownHeader == key; };
1150 const auto it = std::find_if(std::begin(knownHeadersArr), std::end(knownHeadersArr), pred);
1151 if (it == std::end(knownHeadersArr))
1152 return std::nullopt;
1153 return it->wellKnownHeader;
1154}
1155
1156static QByteArray makeCookieHeader(const QList<QNetworkCookie> &cookies,
1157 QNetworkCookie::RawForm type,
1158 QByteArrayView separator)
1159{
1160 QByteArray result;
1161 for (const QNetworkCookie &cookie : cookies) {
1162 result += cookie.toRawForm(type);
1163 result += separator;
1164 }
1165 if (!result.isEmpty())
1166 result.chop(separator.size());
1167 return result;
1168}
1169
1170static QByteArray makeCookieHeader(const QVariant &value, QNetworkCookie::RawForm type,
1171 QByteArrayView separator)
1172{
1173 const QList<QNetworkCookie> *cookies = get_if<QList<QNetworkCookie>>(&value);
1174 if (!cookies)
1175 return {};
1176 return makeCookieHeader(*cookies, type, separator);
1177}
1178
1179static QByteArray headerValue(QNetworkRequest::KnownHeaders header, const QVariant &value)
1180{
1181 switch (header) {
1182 case QNetworkRequest::ContentTypeHeader:
1183 case QNetworkRequest::ContentLengthHeader:
1184 case QNetworkRequest::ContentDispositionHeader:
1185 case QNetworkRequest::UserAgentHeader:
1186 case QNetworkRequest::ServerHeader:
1187 case QNetworkRequest::ETagHeader:
1188 case QNetworkRequest::IfMatchHeader:
1189 case QNetworkRequest::IfNoneMatchHeader:
1190 return value.toByteArray();
1191
1192 case QNetworkRequest::LocationHeader:
1193 switch (value.userType()) {
1194 case QMetaType::QUrl:
1195 return value.toUrl().toEncoded();
1196
1197 default:
1198 return value.toByteArray();
1199 }
1200
1201 case QNetworkRequest::LastModifiedHeader:
1202 case QNetworkRequest::IfModifiedSinceHeader:
1203 switch (value.userType()) {
1204 // Generate RFC 1123/822 dates:
1205 case QMetaType::QDate:
1206 return QNetworkHeadersPrivate::toHttpDate(value.toDate().startOfDay(QTimeZone::UTC));
1207 case QMetaType::QDateTime:
1208 return QNetworkHeadersPrivate::toHttpDate(value.toDateTime());
1209
1210 default:
1211 return value.toByteArray();
1212 }
1213
1214 case QNetworkRequest::CookieHeader:
1215 return makeCookieHeader(value, QNetworkCookie::NameAndValueOnly, "; ");
1216
1217 case QNetworkRequest::SetCookieHeader:
1218 return makeCookieHeader(value, QNetworkCookie::Full, ", ");
1219
1220 default:
1221 Q_UNREACHABLE_RETURN({});
1222 }
1223}
1224
1225static int parseHeaderName(QByteArrayView headerName)
1226{
1227 if (headerName.isEmpty())
1228 return -1;
1229
1230 auto is = [headerName](QByteArrayView what) {
1231 return headerName.compare(what, Qt::CaseInsensitive) == 0;
1232 };
1233
1234 switch (QtMiscUtils::toAsciiLower(headerName.front())) {
1235 case 'c':
1236 if (is("content-type"))
1237 return QNetworkRequest::ContentTypeHeader;
1238 else if (is("content-length"))
1239 return QNetworkRequest::ContentLengthHeader;
1240 else if (is("cookie"))
1241 return QNetworkRequest::CookieHeader;
1242 else if (is("content-disposition"))
1243 return QNetworkRequest::ContentDispositionHeader;
1244 break;
1245
1246 case 'e':
1247 if (is("etag"))
1248 return QNetworkRequest::ETagHeader;
1249 break;
1250
1251 case 'i':
1252 if (is("if-modified-since"))
1253 return QNetworkRequest::IfModifiedSinceHeader;
1254 if (is("if-match"))
1255 return QNetworkRequest::IfMatchHeader;
1256 if (is("if-none-match"))
1257 return QNetworkRequest::IfNoneMatchHeader;
1258 break;
1259
1260 case 'l':
1261 if (is("location"))
1262 return QNetworkRequest::LocationHeader;
1263 else if (is("last-modified"))
1264 return QNetworkRequest::LastModifiedHeader;
1265 break;
1266
1267 case 's':
1268 if (is("set-cookie"))
1269 return QNetworkRequest::SetCookieHeader;
1270 else if (is("server"))
1271 return QNetworkRequest::ServerHeader;
1272 break;
1273
1274 case 'u':
1275 if (is("user-agent"))
1276 return QNetworkRequest::UserAgentHeader;
1277 break;
1278 }
1279
1280 return -1; // nothing found
1281}
1282
1283static QVariant parseHttpDate(QByteArrayView raw)
1284{
1285 QDateTime dt = QNetworkHeadersPrivate::fromHttpDate(raw);
1286 if (dt.isValid())
1287 return dt;
1288 return QVariant(); // transform an invalid QDateTime into a null QVariant
1289}
1290
1291static QList<QNetworkCookie> parseCookieHeader(QByteArrayView raw)
1292{
1293 QList<QNetworkCookie> result;
1294 for (auto cookie : QLatin1StringView(raw).tokenize(';'_L1)) {
1295 QList<QNetworkCookie> parsed = QNetworkCookie::parseCookies(cookie.trimmed());
1296 if (parsed.size() != 1)
1297 return {}; // invalid Cookie: header
1298
1299 result += parsed;
1300 }
1301
1302 return result;
1303}
1304
1305static QVariant parseETag(QByteArrayView raw)
1306{
1307 const QByteArrayView trimmed = raw.trimmed();
1308 if (!trimmed.startsWith('"') && !trimmed.startsWith(R"(W/")"))
1309 return QVariant();
1310
1311 if (!trimmed.endsWith('"'))
1312 return QVariant();
1313
1314 return QString::fromLatin1(trimmed);
1315}
1316
1317template<typename T>
1318static QStringList parseMatchImpl(QByteArrayView raw, T op)
1319{
1320 const QByteArrayView trimmedRaw = raw.trimmed();
1321 if (trimmedRaw == "*")
1322 return QStringList(QStringLiteral("*"));
1323
1324 QStringList tags;
1325 for (auto &element : QLatin1StringView(trimmedRaw).tokenize(','_L1)) {
1326 if (const auto trimmed = element.trimmed(); op(trimmed))
1327 tags += QString::fromLatin1(trimmed);
1328 }
1329 return tags;
1330}
1331
1332
1333static QStringList parseIfMatch(QByteArrayView raw)
1334{
1335 return parseMatchImpl(raw, [](QByteArrayView element) {
1336 return element.startsWith('"') && element.endsWith('"');
1337 });
1338}
1339
1340static QStringList parseIfNoneMatch(QByteArrayView raw)
1341{
1342 return parseMatchImpl(raw, [](QByteArrayView element) {
1343 return (element.startsWith('"') || element.startsWith(R"(W/")")) && element.endsWith('"');
1344 });
1345}
1346
1347
1348static QVariant parseHeaderValue(QNetworkRequest::KnownHeaders header, QByteArrayView value)
1349{
1350 // header is always a valid value
1351 switch (header) {
1352 case QNetworkRequest::UserAgentHeader:
1353 case QNetworkRequest::ServerHeader:
1354 case QNetworkRequest::ContentTypeHeader:
1355 case QNetworkRequest::ContentDispositionHeader:
1356 // copy exactly, convert to QString
1357 return QString::fromLatin1(value);
1358
1359 case QNetworkRequest::ContentLengthHeader: {
1360 bool ok;
1361 qint64 result = QByteArrayView(value).trimmed().toLongLong(&ok);
1362 if (ok)
1363 return result;
1364 return QVariant();
1365 }
1366
1367 case QNetworkRequest::LocationHeader: {
1368 QUrl result = QUrl::fromEncoded(value, QUrl::StrictMode);
1369 if (result.isValid() && !result.scheme().isEmpty())
1370 return result;
1371 return QVariant();
1372 }
1373
1374 case QNetworkRequest::LastModifiedHeader:
1375 case QNetworkRequest::IfModifiedSinceHeader:
1376 return parseHttpDate(value);
1377
1378 case QNetworkRequest::ETagHeader:
1379 return parseETag(value);
1380
1381 case QNetworkRequest::IfMatchHeader:
1382 return parseIfMatch(value);
1383
1384 case QNetworkRequest::IfNoneMatchHeader:
1385 return parseIfNoneMatch(value);
1386
1387 case QNetworkRequest::CookieHeader:
1388 return QVariant::fromValue(parseCookieHeader(value));
1389
1390 case QNetworkRequest::SetCookieHeader:
1391 return QVariant::fromValue(QNetworkCookie::parseCookies(value));
1392
1393 default:
1394 Q_UNREACHABLE_RETURN({});
1395 }
1396}
1397
1398static QVariant parseHeaderValue(QNetworkRequest::KnownHeaders header, QList<QByteArray> values)
1399{
1400 if (values.empty())
1401 return QVariant();
1402
1403 // header is always a valid value
1404 switch (header) {
1405 case QNetworkRequest::IfMatchHeader: {
1406 QStringList res;
1407 for (const auto &val : values)
1408 res << parseIfMatch(val);
1409 return res;
1410 }
1411 case QNetworkRequest::IfNoneMatchHeader: {
1412 QStringList res;
1413 for (const auto &val : values)
1414 res << parseIfNoneMatch(val);
1415 return res;
1416 }
1417 case QNetworkRequest::CookieHeader: {
1418 auto listOpt = QNetworkHeadersPrivate::toCookieList(values);
1419 return listOpt.has_value() ? QVariant::fromValue(listOpt.value()) : QVariant();
1420 }
1421 case QNetworkRequest::SetCookieHeader: {
1422 QList<QNetworkCookie> res;
1423 for (const auto &val : values)
1424 res << QNetworkCookie::parseCookies(val);
1425 return QVariant::fromValue(res);
1426 }
1427 default:
1428 return parseHeaderValue(header, values.first());
1429 }
1430 return QVariant();
1431}
1432
1433static bool isSetCookie(QByteArrayView name)
1434{
1435 return name.compare(QHttpHeaders::wellKnownHeaderName(QHttpHeaders::WellKnownHeader::SetCookie),
1436 Qt::CaseInsensitive) == 0;
1437}
1438
1439static bool isSetCookie(QHttpHeaders::WellKnownHeader name)
1440{
1441 return name == QHttpHeaders::WellKnownHeader::SetCookie;
1442}
1443
1444template<class HeaderName>
1445static void setFromRawHeader(QHttpHeaders &headers, HeaderName header,
1446 QByteArrayView value)
1447{
1448 headers.removeAll(header);
1449
1450 if (value.isNull())
1451 // only wanted to erase key
1452 return;
1453
1454 if (isSetCookie(header)) {
1455 for (auto cookie : QLatin1StringView(value).tokenize('\n'_L1))
1456 headers.append(QHttpHeaders::WellKnownHeader::SetCookie, cookie);
1457 } else {
1458 headers.append(header, value);
1459 }
1460}
1461
1463{
1464 if (rawHeaderCache.isCached)
1465 return rawHeaderCache.headersList;
1466
1467 rawHeaderCache.headersList = fromHttpToRaw(httpHeaders);
1468 rawHeaderCache.isCached = true;
1469 return rawHeaderCache.headersList;
1470}
1471
1473{
1474 if (httpHeaders.isEmpty())
1475 return {};
1476
1477 QList<QByteArray> result;
1478 result.reserve(httpHeaders.size());
1479 QDuplicateTracker<QByteArray> seen(httpHeaders.size());
1480
1481 for (qsizetype i = 0; i < httpHeaders.size(); i++) {
1482 const auto nameL1 = httpHeaders.nameAt(i);
1483 const auto name = QByteArray(nameL1.data(), nameL1.size());
1484 if (seen.hasSeen(name))
1485 continue;
1486
1487 result << name;
1488 }
1489
1490 return result;
1491}
1492
1493QByteArray QNetworkHeadersPrivate::rawHeader(QAnyStringView headerName) const
1494{
1495 QByteArrayView setCookieStr = QHttpHeaders::wellKnownHeaderName(
1496 QHttpHeaders::WellKnownHeader::SetCookie);
1497 if (QAnyStringView::compare(headerName, setCookieStr, Qt::CaseInsensitive) != 0)
1498 return httpHeaders.combinedValue(headerName);
1499
1500 QByteArray result;
1501 const char* separator = "";
1502 for (qsizetype i = 0; i < httpHeaders.size(); ++i) {
1503 if (QAnyStringView::compare(httpHeaders.nameAt(i), headerName, Qt::CaseInsensitive) == 0) {
1504 result.append(separator);
1505 result.append(httpHeaders.valueAt(i));
1506 separator = "\n";
1507 }
1508 }
1509 return result;
1510}
1511
1512void QNetworkHeadersPrivate::setRawHeader(const QByteArray &key, const QByteArray &value)
1513{
1514 if (key.isEmpty())
1515 // refuse to accept an empty raw header
1516 return;
1517
1518 setFromRawHeader(httpHeaders, key, value);
1519 parseAndSetHeader(key, value);
1520
1521 invalidateHeaderCache();
1522}
1523
1524void QNetworkHeadersPrivate::setCookedHeader(QNetworkRequest::KnownHeaders header,
1525 const QVariant &value)
1526{
1527 const auto wellKnownOpt = toWellKnownHeader(header);
1528 if (!wellKnownOpt) {
1529 // verifies that \a header is a known value
1530 qWarning("QNetworkRequest::setHeader: invalid header value KnownHeader(%d) received", header);
1531 return;
1532 }
1533
1534 if (value.isNull()) {
1535 httpHeaders.removeAll(wellKnownOpt.value());
1536 cookedHeaders.remove(header);
1537 } else {
1538 QByteArray rawValue = headerValue(header, value);
1539 if (rawValue.isEmpty()) {
1540 qWarning("QNetworkRequest::setHeader: QVariant of type %s cannot be used with header %s",
1541 value.typeName(),
1542 QHttpHeaders::wellKnownHeaderName(wellKnownOpt.value()).constData());
1543 return;
1544 }
1545
1546 setFromRawHeader(httpHeaders, wellKnownOpt.value(), rawValue);
1547 cookedHeaders.insert(header, value);
1548 }
1549
1550 invalidateHeaderCache();
1551}
1552
1554{
1555 return httpHeaders;
1556}
1557
1558void QNetworkHeadersPrivate::setHeaders(const QHttpHeaders &newHeaders)
1559{
1560 httpHeaders = newHeaders;
1561 setCookedFromHttp(httpHeaders);
1562 invalidateHeaderCache();
1563}
1564
1565void QNetworkHeadersPrivate::setHeaders(QHttpHeaders &&newHeaders)
1566{
1567 httpHeaders = std::move(newHeaders);
1568 setCookedFromHttp(httpHeaders);
1569 invalidateHeaderCache();
1570}
1571
1572void QNetworkHeadersPrivate::setHeader(QHttpHeaders::WellKnownHeader name, QByteArrayView value)
1573{
1574 httpHeaders.replaceOrAppend(name, value);
1575
1576 // set cooked header
1577 const auto knownHeaderOpt = toKnownHeader(name);
1578 if (knownHeaderOpt)
1579 parseAndSetHeader(knownHeaderOpt.value(), value);
1580
1581 invalidateHeaderCache();
1582}
1583
1585{
1586 httpHeaders.clear();
1587 cookedHeaders.clear();
1588 invalidateHeaderCache();
1589}
1590
1591void QNetworkHeadersPrivate::parseAndSetHeader(QByteArrayView key, QByteArrayView value)
1592{
1593 // is it a known header?
1594 const int parsedKeyAsInt = parseHeaderName(key);
1595 if (parsedKeyAsInt != -1) {
1596 const QNetworkRequest::KnownHeaders parsedKey
1597 = static_cast<QNetworkRequest::KnownHeaders>(parsedKeyAsInt);
1598 parseAndSetHeader(parsedKey, value);
1599 }
1600}
1601
1602void QNetworkHeadersPrivate::parseAndSetHeader(QNetworkRequest::KnownHeaders key,
1603 QByteArrayView value)
1604{
1605 if (value.isNull()) {
1606 cookedHeaders.remove(key);
1607 } else if (key == QNetworkRequest::ContentLengthHeader
1608 && cookedHeaders.contains(QNetworkRequest::ContentLengthHeader)) {
1609 // Only set the cooked header "Content-Length" once.
1610 // See bug QTBUG-15311
1611 } else {
1612 cookedHeaders.insert(key, parseHeaderValue(key, value));
1613 }
1614}
1615
1616// Fast month string to int conversion. This code
1617// assumes that the Month name is correct and that
1618// the string is at least three chars long.
1619static int name_to_month(const char* month_str)
1620{
1621 switch (month_str[0]) {
1622 case 'J':
1623 switch (month_str[1]) {
1624 case 'a':
1625 return 1;
1626 case 'u':
1627 switch (month_str[2] ) {
1628 case 'n':
1629 return 6;
1630 case 'l':
1631 return 7;
1632 }
1633 }
1634 break;
1635 case 'F':
1636 return 2;
1637 case 'M':
1638 switch (month_str[2] ) {
1639 case 'r':
1640 return 3;
1641 case 'y':
1642 return 5;
1643 }
1644 break;
1645 case 'A':
1646 switch (month_str[1]) {
1647 case 'p':
1648 return 4;
1649 case 'u':
1650 return 8;
1651 }
1652 break;
1653 case 'O':
1654 return 10;
1655 case 'S':
1656 return 9;
1657 case 'N':
1658 return 11;
1659 case 'D':
1660 return 12;
1661 }
1662
1663 return 0;
1664}
1665
1666QDateTime QNetworkHeadersPrivate::fromHttpDate(QByteArrayView value)
1667{
1668 // HTTP dates have three possible formats:
1669 // RFC 1123/822 - ddd, dd MMM yyyy hh:mm:ss "GMT"
1670 // RFC 850 - dddd, dd-MMM-yy hh:mm:ss "GMT"
1671 // ANSI C's asctime - ddd MMM d hh:mm:ss yyyy
1672 // We only handle them exactly. If they deviate, we bail out.
1673
1674 int pos = value.indexOf(',');
1675 QDateTime dt;
1676#if QT_CONFIG(datestring)
1677 if (pos == -1) {
1678 // no comma -> asctime(3) format
1679 dt = QDateTime::fromString(QString::fromLatin1(value), Qt::TextDate);
1680 } else {
1681 // Use sscanf over QLocal/QDateTimeParser for speed reasons. See the
1682 // Qt WebKit performance benchmarks to get an idea.
1683 if (pos == 3) {
1684 char month_name[4];
1685 int day, year, hour, minute, second;
1686#ifdef Q_CC_MSVC
1687 // Use secure version to avoid compiler warning
1688 if (sscanf_s(value.constData(), "%*3s, %d %3s %d %d:%d:%d 'GMT'", &day, month_name, 4, &year, &hour, &minute, &second) == 6)
1689#else
1690 // The POSIX secure mode is %ms (which allocates memory), too bleeding edge for now
1691 // In any case this is already safe as field width is specified.
1692 if (sscanf(value.constData(), "%*3s, %d %3s %d %d:%d:%d 'GMT'", &day, month_name, &year, &hour, &minute, &second) == 6)
1693#endif
1694 dt = QDateTime(QDate(year, name_to_month(month_name), day), QTime(hour, minute, second));
1695 } else {
1696 QLocale c = QLocale::c();
1697 // eat the weekday, the comma and the space following it
1698 QString sansWeekday = QString::fromLatin1(value.constData() + pos + 2);
1699 // must be RFC 850 date
1700 dt = c.toDateTime(sansWeekday, "dd-MMM-yy hh:mm:ss 'GMT'"_L1);
1701 }
1702 }
1703#endif // datestring
1704
1705 if (dt.isValid())
1706 dt.setTimeZone(QTimeZone::UTC);
1707 return dt;
1708}
1709
1710QByteArray QNetworkHeadersPrivate::toHttpDate(const QDateTime &dt)
1711{
1712 return QLocale::c().toString(dt.toUTC(), u"ddd, dd MMM yyyy hh:mm:ss 'GMT'").toLatin1();
1713}
1714
1716 const QHttpHeaders &headers)
1717{
1718 if (headers.isEmpty())
1719 return {};
1720
1722 QHash<QByteArray, qsizetype> nameToIndex;
1723 list.reserve(headers.size());
1724 nameToIndex.reserve(headers.size());
1725
1726 for (qsizetype i = 0; i < headers.size(); ++i) {
1727 const auto nameL1 = headers.nameAt(i);
1728 const auto value = headers.valueAt(i);
1729
1730 const bool isSetCookie = nameL1 == QHttpHeaders::wellKnownHeaderName(
1731 QHttpHeaders::WellKnownHeader::SetCookie);
1732
1733 const auto name = QByteArray(nameL1.data(), nameL1.size());
1734 if (auto it = nameToIndex.find(name); it != nameToIndex.end()) {
1735 list[it.value()].second += isSetCookie ? "\n" : ", ";
1736 list[it.value()].second += value;
1737 } else {
1738 nameToIndex[name] = list.size();
1739 list.emplaceBack(name, value.toByteArray());
1740 }
1741 }
1742
1743 return list;
1744}
1745
1746QHttpHeaders QNetworkHeadersPrivate::fromRawToHttp(const RawHeadersList &raw)
1747{
1748 if (raw.empty())
1749 return {};
1750
1751 QHttpHeaders headers;
1752 headers.reserve(raw.size());
1753
1754 for (const auto &[key, value] : raw) {
1755 const bool isSetCookie = key.compare(QHttpHeaders::wellKnownHeaderName(
1756 QHttpHeaders::WellKnownHeader::SetCookie),
1757 Qt::CaseInsensitive) == 0;
1758 if (isSetCookie) {
1759 for (auto header : QLatin1StringView(value).tokenize('\n'_L1))
1760 headers.append(key, header);
1761 } else {
1762 headers.append(key, value);
1763 }
1764 }
1765
1766 return headers;
1767}
1768
1769std::optional<qint64> QNetworkHeadersPrivate::toInt(QByteArrayView value)
1770{
1771 if (value.empty())
1772 return std::nullopt;
1773
1774 bool ok;
1775 qint64 res = value.toLongLong(&ok);
1776 if (ok)
1777 return res;
1778 return std::nullopt;
1779}
1780
1781std::optional<QNetworkHeadersPrivate::NetworkCookieList> QNetworkHeadersPrivate::toSetCookieList(
1782 const QList<QByteArray> &values)
1783{
1784 if (values.empty())
1785 return std::nullopt;
1786
1787 QList<QNetworkCookie> cookies;
1788 for (const auto &s : values)
1789 cookies += QNetworkCookie::parseCookies(s);
1790
1791 if (cookies.empty())
1792 return std::nullopt;
1793 return cookies;
1794}
1795
1796QByteArray QNetworkHeadersPrivate::fromCookieList(const QList<QNetworkCookie> &cookies)
1797{
1798 return makeCookieHeader(cookies, QNetworkCookie::NameAndValueOnly, "; ");
1799}
1800
1801std::optional<QNetworkHeadersPrivate::NetworkCookieList> QNetworkHeadersPrivate::toCookieList(
1802 const QList<QByteArray> &values)
1803{
1804 if (values.empty())
1805 return std::nullopt;
1806
1807 QList<QNetworkCookie> cookies;
1808 for (const auto &s : values)
1809 cookies += parseCookieHeader(s);
1810
1811 if (cookies.empty())
1812 return std::nullopt;
1813 return cookies;
1814}
1815
1816void QNetworkHeadersPrivate::invalidateHeaderCache()
1817{
1818 rawHeaderCache.headersList.clear();
1819 rawHeaderCache.isCached = false;
1820}
1821
1822void QNetworkHeadersPrivate::setCookedFromHttp(const QHttpHeaders &newHeaders)
1823{
1824 cookedHeaders.clear();
1825
1826 QMap<QNetworkRequest::KnownHeaders, QList<QByteArray>> multipleHeadersMap;
1827 for (int i = 0; i < newHeaders.size(); ++i) {
1828 const auto name = newHeaders.nameAt(i);
1829 const auto value = newHeaders.valueAt(i);
1830
1831 const int parsedKeyAsInt = parseHeaderName(name);
1832 if (parsedKeyAsInt == -1)
1833 continue;
1834
1835 const QNetworkRequest::KnownHeaders parsedKey
1836 = static_cast<QNetworkRequest::KnownHeaders>(parsedKeyAsInt);
1837
1838 auto &list = multipleHeadersMap[parsedKey];
1839 list.append(value.toByteArray());
1840 }
1841
1842 for (auto i = multipleHeadersMap.cbegin(), end = multipleHeadersMap.cend(); i != end; ++i)
1843 cookedHeaders.insert(i.key(), parseHeaderValue(i.key(), i.value()));
1844}
1845
1846QT_END_NAMESPACE
1847
1848#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[]