1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtNetwork module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qnetworkrequest.h"
41#include "qnetworkrequest_p.h"
42#include "qplatformdefs.h"
43#include "qnetworkcookie.h"
44#include "qsslconfiguration.h"
45#if QT_CONFIG(http) || defined(Q_CLANG_QDOC)
46#include "qhttp2configuration.h"
47#include "private/http2protocol_p.h"
48#endif
49#include "QtCore/qshareddata.h"
50#include "QtCore/qlocale.h"
51#include "QtCore/qdatetime.h"
52
53#include <ctype.h>
54#if QT_CONFIG(datestring)
55# include <stdio.h>
56#endif
57
58#include <algorithm>
59
60QT_BEGIN_NAMESPACE
61
62/*!
63 \class QNetworkRequest
64 \since 4.4
65 \ingroup network
66 \ingroup shared
67 \inmodule QtNetwork
68
69 \brief The QNetworkRequest class holds a request to be sent with QNetworkAccessManager.
70
71 QNetworkRequest is part of the Network Access API and is the class
72 holding the information necessary to send a request over the
73 network. It contains a URL and some ancillary information that can
74 be used to modify the request.
75
76 \sa QNetworkReply, QNetworkAccessManager
77*/
78
79/*!
80 \enum QNetworkRequest::KnownHeaders
81
82 List of known header types that QNetworkRequest parses. Each known
83 header is also represented in raw form with its full HTTP name.
84
85 \value ContentDispositionHeader Corresponds to the HTTP
86 Content-Disposition header and contains a string containing the
87 disposition type (for instance, attachment) and a parameter (for
88 instance, filename).
89
90 \value ContentTypeHeader Corresponds to the HTTP Content-Type
91 header and contains a string containing the media (MIME) type and
92 any auxiliary data (for instance, charset).
93
94 \value ContentLengthHeader Corresponds to the HTTP Content-Length
95 header and contains the length in bytes of the data transmitted.
96
97 \value LocationHeader Corresponds to the HTTP Location
98 header and contains a URL representing the actual location of the
99 data, including the destination URL in case of redirections.
100
101 \value LastModifiedHeader Corresponds to the HTTP Last-Modified
102 header and contains a QDateTime representing the last modification
103 date of the contents.
104
105 \value IfModifiedSinceHeader Corresponds to the HTTP If-Modified-Since
106 header and contains a QDateTime. It is usually added to a
107 QNetworkRequest. The server shall send a 304 (Not Modified) response
108 if the resource has not changed since this time.
109
110 \value ETagHeader Corresponds to the HTTP ETag
111 header and contains a QString representing the last modification
112 state of the contents.
113
114 \value IfMatchHeader Corresponds to the HTTP If-Match
115 header and contains a QStringList. It is usually added to a
116 QNetworkRequest. The server shall send a 412 (Precondition Failed)
117 response if the resource does not match.
118
119 \value IfNoneMatchHeader Corresponds to the HTTP If-None-Match
120 header and contains a QStringList. It is usually added to a
121 QNetworkRequest. The server shall send a 304 (Not Modified) response
122 if the resource does match.
123
124 \value CookieHeader Corresponds to the HTTP Cookie header
125 and contains a QList<QNetworkCookie> representing the cookies to
126 be sent back to the server.
127
128 \value SetCookieHeader Corresponds to the HTTP Set-Cookie
129 header and contains a QList<QNetworkCookie> representing the
130 cookies sent by the server to be stored locally.
131
132 \value UserAgentHeader The User-Agent header sent by HTTP clients.
133
134 \value ServerHeader The Server header received by HTTP clients.
135
136 \sa header(), setHeader(), rawHeader(), setRawHeader()
137*/
138
139/*!
140 \enum QNetworkRequest::Attribute
141 \since 4.7
142
143 Attribute codes for the QNetworkRequest and QNetworkReply.
144
145 Attributes are extra meta-data that are used to control the
146 behavior of the request and to pass further information from the
147 reply back to the application. Attributes are also extensible,
148 allowing custom implementations to pass custom values.
149
150 The following table explains what the default attribute codes are,
151 the QVariant types associated, the default value if said attribute
152 is missing and whether it's used in requests or replies.
153
154 \value HttpStatusCodeAttribute
155 Replies only, type: QMetaType::Int (no default)
156 Indicates the HTTP status code received from the HTTP server
157 (like 200, 304, 404, 401, etc.). If the connection was not
158 HTTP-based, this attribute will not be present.
159
160 \value HttpReasonPhraseAttribute
161 Replies only, type: QMetaType::QByteArray (no default)
162 Indicates the HTTP reason phrase as received from the HTTP
163 server (like "Ok", "Found", "Not Found", "Access Denied",
164 etc.) This is the human-readable representation of the status
165 code (see above). If the connection was not HTTP-based, this
166 attribute will not be present.
167
168 \value RedirectionTargetAttribute
169 Replies only, type: QMetaType::QUrl (no default)
170 If present, it indicates that the server is redirecting the
171 request to a different URL. The Network Access API does follow
172 redirections by default, but if
173 QNetworkRequest::ManualRedirectPolicy is enabled and
174 the redirect was not handled in redirected() then this
175 attribute will be present.
176 The returned URL might be relative. Use QUrl::resolved()
177 to create an absolute URL out of it.
178
179 \value ConnectionEncryptedAttribute
180 Replies only, type: QMetaType::Bool (default: false)
181 Indicates whether the data was obtained through an encrypted
182 (secure) connection.
183
184 \value CacheLoadControlAttribute
185 Requests only, type: QMetaType::Int (default: QNetworkRequest::PreferNetwork)
186 Controls how the cache should be accessed. The possible values
187 are those of QNetworkRequest::CacheLoadControl. Note that the
188 default QNetworkAccessManager implementation does not support
189 caching. However, this attribute may be used by certain
190 backends to modify their requests (for example, for caching proxies).
191
192 \value CacheSaveControlAttribute
193 Requests only, type: QMetaType::Bool (default: true)
194 Controls if the data obtained should be saved to cache for
195 future uses. If the value is false, the data obtained will not
196 be automatically cached. If true, data may be cached, provided
197 it is cacheable (what is cacheable depends on the protocol
198 being used).
199
200 \value SourceIsFromCacheAttribute
201 Replies only, type: QMetaType::Bool (default: false)
202 Indicates whether the data was obtained from cache
203 or not.
204
205 \value DoNotBufferUploadDataAttribute
206 Requests only, type: QMetaType::Bool (default: false)
207 Indicates whether the QNetworkAccessManager code is
208 allowed to buffer the upload data, e.g. when doing a HTTP POST.
209 When using this flag with sequential upload data, the ContentLengthHeader
210 header must be set.
211
212 \value HttpPipeliningAllowedAttribute
213 Requests only, type: QMetaType::Bool (default: false)
214 Indicates whether the QNetworkAccessManager code is
215 allowed to use HTTP pipelining with this request.
216
217 \value HttpPipeliningWasUsedAttribute
218 Replies only, type: QMetaType::Bool
219 Indicates whether the HTTP pipelining was used for receiving
220 this reply.
221
222 \value CustomVerbAttribute
223 Requests only, type: QMetaType::QByteArray
224 Holds the value for the custom HTTP verb to send (destined for usage
225 of other verbs than GET, POST, PUT and DELETE). This verb is set
226 when calling QNetworkAccessManager::sendCustomRequest().
227
228 \value CookieLoadControlAttribute
229 Requests only, type: QMetaType::Int (default: QNetworkRequest::Automatic)
230 Indicates whether to send 'Cookie' headers in the request.
231 This attribute is set to false by Qt WebKit when creating a cross-origin
232 XMLHttpRequest where withCredentials has not been set explicitly to true by the
233 Javascript that created the request.
234 See \l{http://www.w3.org/TR/XMLHttpRequest2/#credentials-flag}{here} for more information.
235 (This value was introduced in 4.7.)
236
237 \value CookieSaveControlAttribute
238 Requests only, type: QMetaType::Int (default: QNetworkRequest::Automatic)
239 Indicates whether to save 'Cookie' headers received from the server in reply
240 to the request.
241 This attribute is set to false by Qt WebKit when creating a cross-origin
242 XMLHttpRequest where withCredentials has not been set explicitly to true by the
243 Javascript that created the request.
244 See \l{http://www.w3.org/TR/XMLHttpRequest2/#credentials-flag} {here} for more information.
245 (This value was introduced in 4.7.)
246
247 \value AuthenticationReuseAttribute
248 Requests only, type: QMetaType::Int (default: QNetworkRequest::Automatic)
249 Indicates whether to use cached authorization credentials in the request,
250 if available. If this is set to QNetworkRequest::Manual and the authentication
251 mechanism is 'Basic' or 'Digest', Qt will not send an an 'Authorization' HTTP
252 header with any cached credentials it may have for the request's URL.
253 This attribute is set to QNetworkRequest::Manual by Qt WebKit when creating a cross-origin
254 XMLHttpRequest where withCredentials has not been set explicitly to true by the
255 Javascript that created the request.
256 See \l{http://www.w3.org/TR/XMLHttpRequest2/#credentials-flag} {here} for more information.
257 (This value was introduced in 4.7.)
258
259 \omitvalue MaximumDownloadBufferSizeAttribute
260
261 \omitvalue DownloadBufferAttribute
262
263 \omitvalue SynchronousRequestAttribute
264
265 \value BackgroundRequestAttribute
266 Type: QMetaType::Bool (default: false)
267 Indicates that this is a background transfer, rather than a user initiated
268 transfer. Depending on the platform, background transfers may be subject
269 to different policies.
270
271 \value Http2AllowedAttribute
272 Requests only, type: QMetaType::Bool (default: true)
273 Indicates whether the QNetworkAccessManager code is
274 allowed to use HTTP/2 with this request. This applies
275 to SSL requests or 'cleartext' HTTP/2.
276
277 \value Http2WasUsedAttribute
278 Replies only, type: QMetaType::Bool (default: false)
279 Indicates whether HTTP/2 was used for receiving this reply.
280 (This value was introduced in 5.9.)
281
282 \value EmitAllUploadProgressSignalsAttribute
283 Requests only, type: QMetaType::Bool (default: false)
284 Indicates whether all upload signals should be emitted.
285 By default, the uploadProgress signal is emitted only
286 in 100 millisecond intervals.
287 (This value was introduced in 5.5.)
288
289 \value OriginalContentLengthAttribute
290 Replies only, type QMetaType::Int
291 Holds the original content-length attribute before being invalidated and
292 removed from the header when the data is compressed and the request was
293 marked to be decompressed automatically.
294 (This value was introduced in 5.9.)
295
296 \value RedirectPolicyAttribute
297 Requests only, type: QMetaType::Int, should be one of the
298 QNetworkRequest::RedirectPolicy values
299 (default: NoLessSafeRedirectPolicy).
300 (This value was introduced in 5.9.)
301
302 \value Http2DirectAttribute
303 Requests only, type: QMetaType::Bool (default: false)
304 If set, this attribute will force QNetworkAccessManager to use
305 HTTP/2 protocol without initial HTTP/2 protocol negotiation.
306 Use of this attribute implies prior knowledge that a particular
307 server supports HTTP/2. The attribute works with SSL or 'cleartext'
308 HTTP/2. If a server turns out to not support HTTP/2, when HTTP/2 direct
309 was specified, QNetworkAccessManager gives up, without attempting to
310 fall back to HTTP/1.1. If both Http2AllowedAttribute and
311 Http2DirectAttribute are set, Http2DirectAttribute takes priority.
312 (This value was introduced in 5.11.)
313
314 \omitvalue ResourceTypeAttribute
315
316 \value AutoDeleteReplyOnFinishAttribute
317 Requests only, type: QMetaType::Bool (default: false)
318 If set, this attribute will make QNetworkAccessManager delete
319 the QNetworkReply after having emitted "finished".
320 (This value was introduced in 5.14.)
321
322 \value User
323 Special type. Additional information can be passed in
324 QVariants with types ranging from User to UserMax. The default
325 implementation of Network Access will ignore any request
326 attributes in this range and it will not produce any
327 attributes in this range in replies. The range is reserved for
328 extensions of QNetworkAccessManager.
329
330 \value UserMax
331 Special type. See User.
332*/
333
334/*!
335 \enum QNetworkRequest::CacheLoadControl
336
337 Controls the caching mechanism of QNetworkAccessManager.
338
339 \value AlwaysNetwork always load from network and do not
340 check if the cache has a valid entry (similar to the
341 "Reload" feature in browsers); in addition, force intermediate
342 caches to re-validate.
343
344 \value PreferNetwork default value; load from the network
345 if the cached entry is older than the network entry. This will never
346 return stale data from the cache, but revalidate resources that
347 have become stale.
348
349 \value PreferCache load from cache if available,
350 otherwise load from network. Note that this can return possibly
351 stale (but not expired) items from cache.
352
353 \value AlwaysCache only load from cache, indicating error
354 if the item was not cached (i.e., off-line mode)
355*/
356
357/*!
358 \enum QNetworkRequest::LoadControl
359 \since 4.7
360
361 Indicates if an aspect of the request's loading mechanism has been
362 manually overridden, e.g. by Qt WebKit.
363
364 \value Automatic default value: indicates default behaviour.
365
366 \value Manual indicates behaviour has been manually overridden.
367*/
368
369/*!
370 \enum QNetworkRequest::RedirectPolicy
371 \since 5.9
372
373 Indicates whether the Network Access API should automatically follow a
374 HTTP redirect response or not.
375
376 \value ManualRedirectPolicy Not following any redirects.
377
378 \value NoLessSafeRedirectPolicy Default value: Only "http"->"http",
379 "http" -> "https" or "https" -> "https" redirects
380 are allowed.
381
382 \value SameOriginRedirectPolicy Require the same protocol, host and port.
383 Note, http://example.com and http://example.com:80
384 will fail with this policy (implicit/explicit ports
385 are considered to be a mismatch).
386
387 \value UserVerifiedRedirectPolicy Client decides whether to follow each
388 redirect by handling the redirected()
389 signal, emitting redirectAllowed() on
390 the QNetworkReply object to allow
391 the redirect or aborting/finishing it to
392 reject the redirect. This can be used,
393 for example, to ask the user whether to
394 accept the redirect, or to decide
395 based on some app-specific configuration.
396*/
397
398/*!
399 \enum QNetworkRequest::TransferTimeoutConstant
400 \since 5.15
401
402 A constant that can be used for enabling transfer
403 timeouts with a preset value.
404
405 \value DefaultTransferTimeoutConstant The transfer timeout in milliseconds.
406 Used if setTimeout() is called
407 without an argument.
408 */
409
410class QNetworkRequestPrivate: public QSharedData, public QNetworkHeadersPrivate
411{
412public:
413 static const int maxRedirectCount = 50;
414 inline QNetworkRequestPrivate()
415 : priority(QNetworkRequest::NormalPriority)
416#ifndef QT_NO_SSL
417 , sslConfiguration(nullptr)
418#endif
419 , maxRedirectsAllowed(maxRedirectCount)
420 , transferTimeout(0)
421 { qRegisterMetaType<QNetworkRequest>(); }
422 ~QNetworkRequestPrivate()
423 {
424#ifndef QT_NO_SSL
425 delete sslConfiguration;
426#endif
427 }
428
429
430 QNetworkRequestPrivate(const QNetworkRequestPrivate &other)
431 : QSharedData(other), QNetworkHeadersPrivate(other)
432 {
433 url = other.url;
434 priority = other.priority;
435 maxRedirectsAllowed = other.maxRedirectsAllowed;
436#ifndef QT_NO_SSL
437 sslConfiguration = nullptr;
438 if (other.sslConfiguration)
439 sslConfiguration = new QSslConfiguration(*other.sslConfiguration);
440#endif
441 peerVerifyName = other.peerVerifyName;
442#if QT_CONFIG(http)
443 h2Configuration = other.h2Configuration;
444#endif
445 transferTimeout = other.transferTimeout;
446 }
447
448 inline bool operator==(const QNetworkRequestPrivate &other) const
449 {
450 return url == other.url &&
451 priority == other.priority &&
452 rawHeaders == other.rawHeaders &&
453 attributes == other.attributes &&
454 maxRedirectsAllowed == other.maxRedirectsAllowed &&
455 peerVerifyName == other.peerVerifyName
456#if QT_CONFIG(http)
457 && h2Configuration == other.h2Configuration
458#endif
459 && transferTimeout == other.transferTimeout
460 ;
461 // don't compare cookedHeaders
462 }
463
464 QUrl url;
465 QNetworkRequest::Priority priority;
466#ifndef QT_NO_SSL
467 mutable QSslConfiguration *sslConfiguration;
468#endif
469 int maxRedirectsAllowed;
470 QString peerVerifyName;
471#if QT_CONFIG(http)
472 QHttp2Configuration h2Configuration;
473#endif
474 int transferTimeout;
475};
476
477/*!
478 Constructs a QNetworkRequest object with no URL to be requested.
479 Use setUrl() to set one.
480
481 \sa url(), setUrl()
482*/
483QNetworkRequest::QNetworkRequest()
484 : d(new QNetworkRequestPrivate)
485{
486
487#if QT_CONFIG(http)
488 // Initial values proposed by RFC 7540 are quite draconian,
489 // so unless an application will set its own parameters, we
490 // make stream window size larger and increase (via WINDOW_UPDATE)
491 // the session window size. These are our 'defaults':
492 d->h2Configuration.setStreamReceiveWindowSize(Http2::qtDefaultStreamReceiveWindowSize);
493 d->h2Configuration.setSessionReceiveWindowSize(Http2::maxSessionReceiveWindowSize);
494 d->h2Configuration.setServerPushEnabled(false);
495#endif // QT_CONFIG(http)
496}
497
498/*!
499 Constructs a QNetworkRequest object with \a url as the URL to be
500 requested.
501
502 \sa url(), setUrl()
503*/
504QNetworkRequest::QNetworkRequest(const QUrl &url)
505 : QNetworkRequest()
506{
507 d->url = url;
508}
509
510/*!
511 Creates a copy of \a other.
512*/
513QNetworkRequest::QNetworkRequest(const QNetworkRequest &other)
514 : d(other.d)
515{
516}
517
518/*!
519 Disposes of the QNetworkRequest object.
520*/
521QNetworkRequest::~QNetworkRequest()
522{
523 // QSharedDataPointer auto deletes
524 d = nullptr;
525}
526
527/*!
528 Returns \c true if this object is the same as \a other (i.e., if they
529 have the same URL, same headers and same meta-data settings).
530
531 \sa operator!=()
532*/
533bool QNetworkRequest::operator==(const QNetworkRequest &other) const
534{
535 return d == other.d || *d == *other.d;
536}
537
538/*!
539 \fn bool QNetworkRequest::operator!=(const QNetworkRequest &other) const
540
541 Returns \c false if this object is not the same as \a other.
542
543 \sa operator==()
544*/
545
546/*!
547 Creates a copy of \a other
548*/
549QNetworkRequest &QNetworkRequest::operator=(const QNetworkRequest &other)
550{
551 d = other.d;
552 return *this;
553}
554
555/*!
556 \fn void QNetworkRequest::swap(QNetworkRequest &other)
557 \since 5.0
558
559 Swaps this network request with \a other. This function is very
560 fast and never fails.
561*/
562
563/*!
564 Returns the URL this network request is referring to.
565
566 \sa setUrl()
567*/
568QUrl QNetworkRequest::url() const
569{
570 return d->url;
571}
572
573/*!
574 Sets the URL this network request is referring to be \a url.
575
576 \sa url()
577*/
578void QNetworkRequest::setUrl(const QUrl &url)
579{
580 d->url = url;
581}
582
583/*!
584 Returns the value of the known network header \a header if it is
585 present in this request. If it is not present, returns QVariant()
586 (i.e., an invalid variant).
587
588 \sa KnownHeaders, rawHeader(), setHeader()
589*/
590QVariant QNetworkRequest::header(KnownHeaders header) const
591{
592 return d->cookedHeaders.value(header);
593}
594
595/*!
596 Sets the value of the known header \a header to be \a value,
597 overriding any previously set headers. This operation also sets
598 the equivalent raw HTTP header.
599
600 \sa KnownHeaders, setRawHeader(), header()
601*/
602void QNetworkRequest::setHeader(KnownHeaders header, const QVariant &value)
603{
604 d->setCookedHeader(header, value);
605}
606
607/*!
608 Returns \c true if the raw header \a headerName is present in this
609 network request.
610
611 \sa rawHeader(), setRawHeader()
612*/
613bool QNetworkRequest::hasRawHeader(const QByteArray &headerName) const
614{
615 return d->findRawHeader(headerName) != d->rawHeaders.constEnd();
616}
617
618/*!
619 Returns the raw form of header \a headerName. If no such header is
620 present, an empty QByteArray is returned, which may be
621 indistinguishable from a header that is present but has no content
622 (use hasRawHeader() to find out if the header exists or not).
623
624 Raw headers can be set with setRawHeader() or with setHeader().
625
626 \sa header(), setRawHeader()
627*/
628QByteArray QNetworkRequest::rawHeader(const QByteArray &headerName) const
629{
630 QNetworkHeadersPrivate::RawHeadersList::ConstIterator it =
631 d->findRawHeader(headerName);
632 if (it != d->rawHeaders.constEnd())
633 return it->second;
634 return QByteArray();
635}
636
637/*!
638 Returns a list of all raw headers that are set in this network
639 request. The list is in the order that the headers were set.
640
641 \sa hasRawHeader(), rawHeader()
642*/
643QList<QByteArray> QNetworkRequest::rawHeaderList() const
644{
645 return d->rawHeadersKeys();
646}
647
648/*!
649 Sets the header \a headerName to be of value \a headerValue. If \a
650 headerName corresponds to a known header (see
651 QNetworkRequest::KnownHeaders), the raw format will be parsed and
652 the corresponding "cooked" header will be set as well.
653
654 For example:
655 \snippet code/src_network_access_qnetworkrequest.cpp 0
656
657 will also set the known header LastModifiedHeader to be the
658 QDateTime object of the parsed date.
659
660 \note Setting the same header twice overrides the previous
661 setting. To accomplish the behaviour of multiple HTTP headers of
662 the same name, you should concatenate the two values, separating
663 them with a comma (",") and set one single raw header.
664
665 \sa KnownHeaders, setHeader(), hasRawHeader(), rawHeader()
666*/
667void QNetworkRequest::setRawHeader(const QByteArray &headerName, const QByteArray &headerValue)
668{
669 d->setRawHeader(headerName, headerValue);
670}
671
672/*!
673 Returns the attribute associated with the code \a code. If the
674 attribute has not been set, it returns \a defaultValue.
675
676 \note This function does not apply the defaults listed in
677 QNetworkRequest::Attribute.
678
679 \sa setAttribute(), QNetworkRequest::Attribute
680*/
681QVariant QNetworkRequest::attribute(Attribute code, const QVariant &defaultValue) const
682{
683 return d->attributes.value(code, defaultValue);
684}
685
686/*!
687 Sets the attribute associated with code \a code to be value \a
688 value. If the attribute is already set, the previous value is
689 discarded. In special, if \a value is an invalid QVariant, the
690 attribute is unset.
691
692 \sa attribute(), QNetworkRequest::Attribute
693*/
694void QNetworkRequest::setAttribute(Attribute code, const QVariant &value)
695{
696 if (value.isValid())
697 d->attributes.insert(code, value);
698 else
699 d->attributes.remove(code);
700}
701
702#ifndef QT_NO_SSL
703/*!
704 Returns this network request's SSL configuration. By default this is the same
705 as QSslConfiguration::defaultConfiguration().
706
707 \sa setSslConfiguration(), QSslConfiguration::defaultConfiguration()
708*/
709QSslConfiguration QNetworkRequest::sslConfiguration() const
710{
711 if (!d->sslConfiguration)
712 d->sslConfiguration = new QSslConfiguration(QSslConfiguration::defaultConfiguration());
713 return *d->sslConfiguration;
714}
715
716/*!
717 Sets this network request's SSL configuration to be \a config. The
718 settings that apply are the private key, the local certificate,
719 the SSL protocol (SSLv2, SSLv3, TLSv1.0 where applicable), the CA
720 certificates and the ciphers that the SSL backend is allowed to
721 use.
722
723 \sa sslConfiguration(), QSslConfiguration::defaultConfiguration()
724*/
725void QNetworkRequest::setSslConfiguration(const QSslConfiguration &config)
726{
727 if (!d->sslConfiguration)
728 d->sslConfiguration = new QSslConfiguration(config);
729 else
730 *d->sslConfiguration = config;
731}
732#endif
733
734/*!
735 \since 4.6
736
737 Allows setting a reference to the \a object initiating
738 the request.
739
740 For example Qt WebKit sets the originating object to the
741 QWebFrame that initiated the request.
742
743 \sa originatingObject()
744*/
745void QNetworkRequest::setOriginatingObject(QObject *object)
746{
747 d->originatingObject = object;
748}
749
750/*!
751 \since 4.6
752
753 Returns a reference to the object that initiated this
754 network request; returns \nullptr if not set or the object has
755 been destroyed.
756
757 \sa setOriginatingObject()
758*/
759QObject *QNetworkRequest::originatingObject() const
760{
761 return d->originatingObject.data();
762}
763
764/*!
765 \since 4.7
766
767 Return the priority of this request.
768
769 \sa setPriority()
770*/
771QNetworkRequest::Priority QNetworkRequest::priority() const
772{
773 return d->priority;
774}
775
776/*! \enum QNetworkRequest::Priority
777
778 \since 4.7
779
780 This enum lists the possible network request priorities.
781
782 \value HighPriority High priority
783 \value NormalPriority Normal priority
784 \value LowPriority Low priority
785 */
786
787/*!
788 \since 4.7
789
790 Set the priority of this request to \a priority.
791
792 \note The \a priority is only a hint to the network access
793 manager. It can use it or not. Currently it is used for HTTP to
794 decide which request should be sent first to a server.
795
796 \sa priority()
797*/
798void QNetworkRequest::setPriority(Priority priority)
799{
800 d->priority = priority;
801}
802
803/*!
804 \since 5.6
805
806 Returns the maximum number of redirects allowed to be followed for this
807 request.
808
809 \sa setMaximumRedirectsAllowed()
810*/
811int QNetworkRequest::maximumRedirectsAllowed() const
812{
813 return d->maxRedirectsAllowed;
814}
815
816/*!
817 \since 5.6
818
819 Sets the maximum number of redirects allowed to be followed for this
820 request to \a maxRedirectsAllowed.
821
822 \sa maximumRedirectsAllowed()
823*/
824void QNetworkRequest::setMaximumRedirectsAllowed(int maxRedirectsAllowed)
825{
826 d->maxRedirectsAllowed = maxRedirectsAllowed;
827}
828
829/*!
830 \since 5.13
831
832 Returns the host name set for the certificate validation, as set by
833 setPeerVerifyName. By default this returns a null string.
834
835 \sa setPeerVerifyName
836*/
837QString QNetworkRequest::peerVerifyName() const
838{
839 return d->peerVerifyName;
840}
841
842/*!
843 \since 5.13
844
845 Sets \a peerName as host name for the certificate validation, instead of the one used for the
846 TCP connection.
847
848 \sa peerVerifyName
849*/
850void QNetworkRequest::setPeerVerifyName(const QString &peerName)
851{
852 d->peerVerifyName = peerName;
853}
854
855#if QT_CONFIG(http) || defined(Q_CLANG_QDOC)
856/*!
857 \since 5.14
858
859 Returns the current parameters that QNetworkAccessManager is
860 using for this request and its underlying HTTP/2 connection.
861 This is either a configuration previously set by an application
862 or a default configuration.
863
864 The default values that QNetworkAccessManager is using are:
865
866 \list
867 \li Window size for connection-level flowcontrol is 2147483647 octets
868 \li Window size for stream-level flowcontrol is 21474836 octets
869 \li Max frame size is 16384
870 \endlist
871
872 By default, server push is disabled, Huffman compression and
873 string indexing are enabled.
874
875 \sa setHttp2Configuration
876*/
877QHttp2Configuration QNetworkRequest::http2Configuration() const
878{
879 return d->h2Configuration;
880}
881
882/*!
883 \since 5.14
884
885 Sets request's HTTP/2 parameters from \a configuration.
886
887 \note The configuration must be set prior to making a request.
888 \note HTTP/2 multiplexes several streams in a single HTTP/2
889 connection. This implies that QNetworkAccessManager will use
890 the configuration found in the first request from a series
891 of requests sent to the same host.
892
893 \sa http2Configuration, QNetworkAccessManager, QHttp2Configuration
894*/
895void QNetworkRequest::setHttp2Configuration(const QHttp2Configuration &configuration)
896{
897 d->h2Configuration = configuration;
898}
899#endif // QT_CONFIG(http) || defined(Q_CLANG_QDOC)
900#if QT_CONFIG(http) || defined(Q_CLANG_QDOC) || defined (Q_OS_WASM)
901/*!
902 \since 5.15
903
904 Returns the timeout used for transfers, in milliseconds.
905
906 This timeout is zero if setTransferTimeout hasn't been
907 called, which means that the timeout is not used.
908
909 \sa setTransferTimeout
910*/
911int QNetworkRequest::transferTimeout() const
912{
913 return d->transferTimeout;
914}
915
916/*!
917 \since 5.15
918
919 Sets \a timeout as the transfer timeout in milliseconds.
920
921 Transfers are aborted if no bytes are transferred before
922 the timeout expires. Zero means no timer is set. If no
923 argument is provided, the timeout is
924 QNetworkRequest::DefaultTransferTimeoutConstant. If this function
925 is not called, the timeout is disabled and has the
926 value zero.
927
928 \sa transferTimeout
929*/
930void QNetworkRequest::setTransferTimeout(int timeout)
931{
932 d->transferTimeout = timeout;
933}
934#endif // QT_CONFIG(http) || defined(Q_CLANG_QDOC) || defined (Q_OS_WASM)
935
936static QByteArray headerName(QNetworkRequest::KnownHeaders header)
937{
938 switch (header) {
939 case QNetworkRequest::ContentTypeHeader:
940 return "Content-Type";
941
942 case QNetworkRequest::ContentLengthHeader:
943 return "Content-Length";
944
945 case QNetworkRequest::LocationHeader:
946 return "Location";
947
948 case QNetworkRequest::LastModifiedHeader:
949 return "Last-Modified";
950
951 case QNetworkRequest::IfModifiedSinceHeader:
952 return "If-Modified-Since";
953
954 case QNetworkRequest::ETagHeader:
955 return "ETag";
956
957 case QNetworkRequest::IfMatchHeader:
958 return "If-Match";
959
960 case QNetworkRequest::IfNoneMatchHeader:
961 return "If-None-Match";
962
963 case QNetworkRequest::CookieHeader:
964 return "Cookie";
965
966 case QNetworkRequest::SetCookieHeader:
967 return "Set-Cookie";
968
969 case QNetworkRequest::ContentDispositionHeader:
970 return "Content-Disposition";
971
972 case QNetworkRequest::UserAgentHeader:
973 return "User-Agent";
974
975 case QNetworkRequest::ServerHeader:
976 return "Server";
977
978 // no default:
979 // if new values are added, this will generate a compiler warning
980 }
981
982 return QByteArray();
983}
984
985static QByteArray headerValue(QNetworkRequest::KnownHeaders header, const QVariant &value)
986{
987 switch (header) {
988 case QNetworkRequest::ContentTypeHeader:
989 case QNetworkRequest::ContentLengthHeader:
990 case QNetworkRequest::ContentDispositionHeader:
991 case QNetworkRequest::UserAgentHeader:
992 case QNetworkRequest::ServerHeader:
993 case QNetworkRequest::ETagHeader:
994 case QNetworkRequest::IfMatchHeader:
995 case QNetworkRequest::IfNoneMatchHeader:
996 return value.toByteArray();
997
998 case QNetworkRequest::LocationHeader:
999 switch (value.userType()) {
1000 case QMetaType::QUrl:
1001 return value.toUrl().toEncoded();
1002
1003 default:
1004 return value.toByteArray();
1005 }
1006
1007 case QNetworkRequest::LastModifiedHeader:
1008 case QNetworkRequest::IfModifiedSinceHeader:
1009 switch (value.userType()) {
1010 case QMetaType::QDate:
1011 case QMetaType::QDateTime:
1012 // generate RFC 1123/822 dates:
1013 return QNetworkHeadersPrivate::toHttpDate(value.toDateTime());
1014
1015 default:
1016 return value.toByteArray();
1017 }
1018
1019 case QNetworkRequest::CookieHeader: {
1020 QList<QNetworkCookie> cookies = qvariant_cast<QList<QNetworkCookie> >(value);
1021 if (cookies.isEmpty() && value.userType() == qMetaTypeId<QNetworkCookie>())
1022 cookies << qvariant_cast<QNetworkCookie>(value);
1023
1024 QByteArray result;
1025 bool first = true;
1026 for (const QNetworkCookie &cookie : qAsConst(cookies)) {
1027 if (!first)
1028 result += "; ";
1029 first = false;
1030 result += cookie.toRawForm(QNetworkCookie::NameAndValueOnly);
1031 }
1032 return result;
1033 }
1034
1035 case QNetworkRequest::SetCookieHeader: {
1036 QList<QNetworkCookie> cookies = qvariant_cast<QList<QNetworkCookie> >(value);
1037 if (cookies.isEmpty() && value.userType() == qMetaTypeId<QNetworkCookie>())
1038 cookies << qvariant_cast<QNetworkCookie>(value);
1039
1040 QByteArray result;
1041 bool first = true;
1042 for (const QNetworkCookie &cookie : qAsConst(cookies)) {
1043 if (!first)
1044 result += ", ";
1045 first = false;
1046 result += cookie.toRawForm(QNetworkCookie::Full);
1047 }
1048 return result;
1049 }
1050 }
1051
1052 return QByteArray();
1053}
1054
1055static int parseHeaderName(const QByteArray &headerName)
1056{
1057 if (headerName.isEmpty())
1058 return -1;
1059
1060 switch (tolower(headerName.at(0))) {
1061 case 'c':
1062 if (headerName.compare("content-type", Qt::CaseInsensitive) == 0)
1063 return QNetworkRequest::ContentTypeHeader;
1064 else if (headerName.compare("content-length", Qt::CaseInsensitive) == 0)
1065 return QNetworkRequest::ContentLengthHeader;
1066 else if (headerName.compare("cookie", Qt::CaseInsensitive) == 0)
1067 return QNetworkRequest::CookieHeader;
1068 else if (qstricmp(headerName.constData(), "content-disposition") == 0)
1069 return QNetworkRequest::ContentDispositionHeader;
1070 break;
1071
1072 case 'e':
1073 if (qstricmp(headerName.constData(), "etag") == 0)
1074 return QNetworkRequest::ETagHeader;
1075 break;
1076
1077 case 'i':
1078 if (qstricmp(headerName.constData(), "if-modified-since") == 0)
1079 return QNetworkRequest::IfModifiedSinceHeader;
1080 if (qstricmp(headerName.constData(), "if-match") == 0)
1081 return QNetworkRequest::IfMatchHeader;
1082 if (qstricmp(headerName.constData(), "if-none-match") == 0)
1083 return QNetworkRequest::IfNoneMatchHeader;
1084 break;
1085
1086 case 'l':
1087 if (headerName.compare("location", Qt::CaseInsensitive) == 0)
1088 return QNetworkRequest::LocationHeader;
1089 else if (headerName.compare("last-modified", Qt::CaseInsensitive) == 0)
1090 return QNetworkRequest::LastModifiedHeader;
1091 break;
1092
1093 case 's':
1094 if (headerName.compare("set-cookie", Qt::CaseInsensitive) == 0)
1095 return QNetworkRequest::SetCookieHeader;
1096 else if (headerName.compare("server", Qt::CaseInsensitive) == 0)
1097 return QNetworkRequest::ServerHeader;
1098 break;
1099
1100 case 'u':
1101 if (headerName.compare("user-agent", Qt::CaseInsensitive) == 0)
1102 return QNetworkRequest::UserAgentHeader;
1103 break;
1104 }
1105
1106 return -1; // nothing found
1107}
1108
1109static QVariant parseHttpDate(const QByteArray &raw)
1110{
1111 QDateTime dt = QNetworkHeadersPrivate::fromHttpDate(raw);
1112 if (dt.isValid())
1113 return dt;
1114 return QVariant(); // transform an invalid QDateTime into a null QVariant
1115}
1116
1117static QVariant parseCookieHeader(const QByteArray &raw)
1118{
1119 QList<QNetworkCookie> result;
1120 const QList<QByteArray> cookieList = raw.split(';');
1121 for (const QByteArray &cookie : cookieList) {
1122 QList<QNetworkCookie> parsed = QNetworkCookie::parseCookies(cookie.trimmed());
1123 if (parsed.count() != 1)
1124 return QVariant(); // invalid Cookie: header
1125
1126 result += parsed;
1127 }
1128
1129 return QVariant::fromValue(result);
1130}
1131
1132static QVariant parseETag(const QByteArray &raw)
1133{
1134 const QByteArray trimmed = raw.trimmed();
1135 if (!trimmed.startsWith('"') && !trimmed.startsWith(R"(W/")"))
1136 return QVariant();
1137
1138 if (!trimmed.endsWith('"'))
1139 return QVariant();
1140
1141 return QString::fromLatin1(trimmed);
1142}
1143
1144static QVariant parseIfMatch(const QByteArray &raw)
1145{
1146 const QByteArray trimmedRaw = raw.trimmed();
1147 if (trimmedRaw == "*")
1148 return QStringList(QStringLiteral("*"));
1149
1150 QStringList tags;
1151 const QList<QByteArray> split = trimmedRaw.split(',');
1152 for (const QByteArray &element : split) {
1153 const QByteArray trimmed = element.trimmed();
1154 if (!trimmed.startsWith('"'))
1155 continue;
1156
1157 if (!trimmed.endsWith('"'))
1158 continue;
1159
1160 tags += QString::fromLatin1(trimmed);
1161 }
1162 return tags;
1163}
1164
1165static QVariant parseIfNoneMatch(const QByteArray &raw)
1166{
1167 const QByteArray trimmedRaw = raw.trimmed();
1168 if (trimmedRaw == "*")
1169 return QStringList(QStringLiteral("*"));
1170
1171 QStringList tags;
1172 const QList<QByteArray> split = trimmedRaw.split(',');
1173 for (const QByteArray &element : split) {
1174 const QByteArray trimmed = element.trimmed();
1175 if (!trimmed.startsWith('"') && !trimmed.startsWith(R"(W/")"))
1176 continue;
1177
1178 if (!trimmed.endsWith('"'))
1179 continue;
1180
1181 tags += QString::fromLatin1(trimmed);
1182 }
1183 return tags;
1184}
1185
1186
1187static QVariant parseHeaderValue(QNetworkRequest::KnownHeaders header, const QByteArray &value)
1188{
1189 // header is always a valid value
1190 switch (header) {
1191 case QNetworkRequest::UserAgentHeader:
1192 case QNetworkRequest::ServerHeader:
1193 case QNetworkRequest::ContentTypeHeader:
1194 case QNetworkRequest::ContentDispositionHeader:
1195 // copy exactly, convert to QString
1196 return QString::fromLatin1(value);
1197
1198 case QNetworkRequest::ContentLengthHeader: {
1199 bool ok;
1200 qint64 result = value.trimmed().toLongLong(&ok);
1201 if (ok)
1202 return result;
1203 return QVariant();
1204 }
1205
1206 case QNetworkRequest::LocationHeader: {
1207 QUrl result = QUrl::fromEncoded(value, QUrl::StrictMode);
1208 if (result.isValid() && !result.scheme().isEmpty())
1209 return result;
1210 return QVariant();
1211 }
1212
1213 case QNetworkRequest::LastModifiedHeader:
1214 case QNetworkRequest::IfModifiedSinceHeader:
1215 return parseHttpDate(value);
1216
1217 case QNetworkRequest::ETagHeader:
1218 return parseETag(value);
1219
1220 case QNetworkRequest::IfMatchHeader:
1221 return parseIfMatch(value);
1222
1223 case QNetworkRequest::IfNoneMatchHeader:
1224 return parseIfNoneMatch(value);
1225
1226 case QNetworkRequest::CookieHeader:
1227 return parseCookieHeader(value);
1228
1229 case QNetworkRequest::SetCookieHeader:
1230 return QVariant::fromValue(QNetworkCookie::parseCookies(value));
1231
1232 default:
1233 Q_ASSERT(0);
1234 }
1235 return QVariant();
1236}
1237
1238QNetworkHeadersPrivate::RawHeadersList::ConstIterator
1239QNetworkHeadersPrivate::findRawHeader(const QByteArray &key) const
1240{
1241 RawHeadersList::ConstIterator it = rawHeaders.constBegin();
1242 RawHeadersList::ConstIterator end = rawHeaders.constEnd();
1243 for ( ; it != end; ++it)
1244 if (it->first.compare(key, Qt::CaseInsensitive) == 0)
1245 return it;
1246
1247 return end; // not found
1248}
1249
1250QNetworkHeadersPrivate::RawHeadersList QNetworkHeadersPrivate::allRawHeaders() const
1251{
1252 return rawHeaders;
1253}
1254
1255QList<QByteArray> QNetworkHeadersPrivate::rawHeadersKeys() const
1256{
1257 QList<QByteArray> result;
1258 result.reserve(rawHeaders.size());
1259 RawHeadersList::ConstIterator it = rawHeaders.constBegin(),
1260 end = rawHeaders.constEnd();
1261 for ( ; it != end; ++it)
1262 result << it->first;
1263
1264 return result;
1265}
1266
1267void QNetworkHeadersPrivate::setRawHeader(const QByteArray &key, const QByteArray &value)
1268{
1269 if (key.isEmpty())
1270 // refuse to accept an empty raw header
1271 return;
1272
1273 setRawHeaderInternal(key, value);
1274 parseAndSetHeader(key, value);
1275}
1276
1277/*!
1278 \internal
1279 Sets the internal raw headers list to match \a list. The cooked headers
1280 will also be updated.
1281
1282 If \a list contains duplicates, they will be stored, but only the first one
1283 is usually accessed.
1284*/
1285void QNetworkHeadersPrivate::setAllRawHeaders(const RawHeadersList &list)
1286{
1287 cookedHeaders.clear();
1288 rawHeaders = list;
1289
1290 RawHeadersList::ConstIterator it = rawHeaders.constBegin();
1291 RawHeadersList::ConstIterator end = rawHeaders.constEnd();
1292 for ( ; it != end; ++it)
1293 parseAndSetHeader(it->first, it->second);
1294}
1295
1296void QNetworkHeadersPrivate::setCookedHeader(QNetworkRequest::KnownHeaders header,
1297 const QVariant &value)
1298{
1299 QByteArray name = headerName(header);
1300 if (name.isEmpty()) {
1301 // headerName verifies that \a header is a known value
1302 qWarning("QNetworkRequest::setHeader: invalid header value KnownHeader(%d) received", header);
1303 return;
1304 }
1305
1306 if (value.isNull()) {
1307 setRawHeaderInternal(name, QByteArray());
1308 cookedHeaders.remove(header);
1309 } else {
1310 QByteArray rawValue = headerValue(header, value);
1311 if (rawValue.isEmpty()) {
1312 qWarning("QNetworkRequest::setHeader: QVariant of type %s cannot be used with header %s",
1313 value.typeName(), name.constData());
1314 return;
1315 }
1316
1317 setRawHeaderInternal(name, rawValue);
1318 cookedHeaders.insert(header, value);
1319 }
1320}
1321
1322void QNetworkHeadersPrivate::setRawHeaderInternal(const QByteArray &key, const QByteArray &value)
1323{
1324 auto firstEqualsKey = [&key](const RawHeaderPair &header) {
1325 return header.first.compare(key, Qt::CaseInsensitive) == 0;
1326 };
1327 rawHeaders.erase(std::remove_if(rawHeaders.begin(), rawHeaders.end(),
1328 firstEqualsKey),
1329 rawHeaders.end());
1330
1331 if (value.isNull())
1332 return; // only wanted to erase key
1333
1334 RawHeaderPair pair;
1335 pair.first = key;
1336 pair.second = value;
1337 rawHeaders.append(pair);
1338}
1339
1340void QNetworkHeadersPrivate::parseAndSetHeader(const QByteArray &key, const QByteArray &value)
1341{
1342 // is it a known header?
1343 const int parsedKeyAsInt = parseHeaderName(key);
1344 if (parsedKeyAsInt != -1) {
1345 const QNetworkRequest::KnownHeaders parsedKey
1346 = static_cast<QNetworkRequest::KnownHeaders>(parsedKeyAsInt);
1347 if (value.isNull()) {
1348 cookedHeaders.remove(parsedKey);
1349 } else if (parsedKey == QNetworkRequest::ContentLengthHeader
1350 && cookedHeaders.contains(QNetworkRequest::ContentLengthHeader)) {
1351 // Only set the cooked header "Content-Length" once.
1352 // See bug QTBUG-15311
1353 } else {
1354 cookedHeaders.insert(parsedKey, parseHeaderValue(parsedKey, value));
1355 }
1356
1357 }
1358}
1359
1360// Fast month string to int conversion. This code
1361// assumes that the Month name is correct and that
1362// the string is at least three chars long.
1363static int name_to_month(const char* month_str)
1364{
1365 switch (month_str[0]) {
1366 case 'J':
1367 switch (month_str[1]) {
1368 case 'a':
1369 return 1;
1370 case 'u':
1371 switch (month_str[2] ) {
1372 case 'n':
1373 return 6;
1374 case 'l':
1375 return 7;
1376 }
1377 }
1378 break;
1379 case 'F':
1380 return 2;
1381 case 'M':
1382 switch (month_str[2] ) {
1383 case 'r':
1384 return 3;
1385 case 'y':
1386 return 5;
1387 }
1388 break;
1389 case 'A':
1390 switch (month_str[1]) {
1391 case 'p':
1392 return 4;
1393 case 'u':
1394 return 8;
1395 }
1396 break;
1397 case 'O':
1398 return 10;
1399 case 'S':
1400 return 9;
1401 case 'N':
1402 return 11;
1403 case 'D':
1404 return 12;
1405 }
1406
1407 return 0;
1408}
1409
1410QDateTime QNetworkHeadersPrivate::fromHttpDate(const QByteArray &value)
1411{
1412 // HTTP dates have three possible formats:
1413 // RFC 1123/822 - ddd, dd MMM yyyy hh:mm:ss "GMT"
1414 // RFC 850 - dddd, dd-MMM-yy hh:mm:ss "GMT"
1415 // ANSI C's asctime - ddd MMM d hh:mm:ss yyyy
1416 // We only handle them exactly. If they deviate, we bail out.
1417
1418 int pos = value.indexOf(',');
1419 QDateTime dt;
1420#if QT_CONFIG(datestring)
1421 if (pos == -1) {
1422 // no comma -> asctime(3) format
1423 dt = QDateTime::fromString(QString::fromLatin1(value), Qt::TextDate);
1424 } else {
1425 // Use sscanf over QLocal/QDateTimeParser for speed reasons. See the
1426 // Qt WebKit performance benchmarks to get an idea.
1427 if (pos == 3) {
1428 char month_name[4];
1429 int day, year, hour, minute, second;
1430#ifdef Q_CC_MSVC
1431 // Use secure version to avoid compiler warning
1432 if (sscanf_s(value.constData(), "%*3s, %d %3s %d %d:%d:%d 'GMT'", &day, month_name, 4, &year, &hour, &minute, &second) == 6)
1433#else
1434 // The POSIX secure mode is %ms (which allocates memory), too bleeding edge for now
1435 // In any case this is already safe as field width is specified.
1436 if (sscanf(value.constData(), "%*3s, %d %3s %d %d:%d:%d 'GMT'", &day, month_name, &year, &hour, &minute, &second) == 6)
1437#endif
1438 dt = QDateTime(QDate(year, name_to_month(month_name), day), QTime(hour, minute, second));
1439 } else {
1440 QLocale c = QLocale::c();
1441 // eat the weekday, the comma and the space following it
1442 QString sansWeekday = QString::fromLatin1(value.constData() + pos + 2);
1443 // must be RFC 850 date
1444 dt = c.toDateTime(sansWeekday, QLatin1String("dd-MMM-yy hh:mm:ss 'GMT'"));
1445 }
1446 }
1447#endif // datestring
1448
1449 if (dt.isValid())
1450 dt.setTimeSpec(Qt::UTC);
1451 return dt;
1452}
1453
1454QByteArray QNetworkHeadersPrivate::toHttpDate(const QDateTime &dt)
1455{
1456 return QLocale::c().toString(dt, u"ddd, dd MMM yyyy hh:mm:ss 'GMT'")
1457 .toLatin1();
1458}
1459
1460QT_END_NAMESPACE
1461