1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#include <QtNetwork/private/qtnetworkglobal_p.h>
6
7#include "qnetworkaccessmanager.h"
8#include "qnetworkaccessmanager_p.h"
9#include "qnetworkrequest.h"
10#include "qnetworkreply.h"
11#include "qnetworkreply_p.h"
12#include "qnetworkcookie.h"
13#include "qnetworkcookiejar.h"
14#include "qabstractnetworkcache.h"
15#include "qhstspolicy.h"
16#include "qhsts_p.h"
17
18#if QT_CONFIG(settings)
19#include "qhstsstore_p.h"
20#endif // QT_CONFIG(settings)
21
22#include "qnetworkaccessfilebackend_p.h"
23#include "qnetworkaccessdebugpipebackend_p.h"
24#include "qnetworkaccesscachebackend_p.h"
25#include "qnetworkreplydataimpl_p.h"
26#include "qnetworkreplyfileimpl_p.h"
27
28#include "qnetworkaccessbackend_p.h"
29#include "qnetworkreplyimpl_p.h"
30
31#include "QtCore/qbuffer.h"
32#include "QtCore/qlist.h"
33#include "QtCore/qurl.h"
34#include "QtNetwork/private/qauthenticator_p.h"
35#include "QtNetwork/qsslconfiguration.h"
36
37#if QT_CONFIG(http)
38#include "QtNetwork/private/http2protocol_p.h"
39#include "qhttpmultipart.h"
40#include "qhttpmultipart_p.h"
41#include "qnetworkreplyhttpimpl_p.h"
42#endif
43
44#include "qthread.h"
45
46#include <QHostInfo>
47
48#include "QtCore/qapplicationstatic.h"
49#include "QtCore/qloggingcategory.h"
50#include <QtCore/private/qfactoryloader_p.h>
51
52#if defined(Q_OS_MACOS)
53#include <QtCore/private/qcore_mac_p.h>
54
55#include <CoreServices/CoreServices.h>
56#include <SystemConfiguration/SystemConfiguration.h>
57#include <Security/Security.h>
58#endif
59#ifdef Q_OS_WASM
60#include "qnetworkreplywasmimpl_p.h"
61#include "qhttpmultipart.h"
62#include "qhttpmultipart_p.h"
63#endif
64
65#include "qnetconmonitor_p.h"
66
67#include <mutex>
68#include <utility>
69
70QT_BEGIN_NAMESPACE
71
72using namespace Qt::StringLiterals;
73using namespace std::chrono_literals;
74
75#if defined(Q_OS_MACOS)
76Q_STATIC_LOGGING_CATEGORY(lcQnam, "qt.network.access.manager")
77#endif
78
79Q_APPLICATION_STATIC(QNetworkAccessFileBackendFactory, fileBackend)
80
81#if QT_CONFIG(private_tests)
82Q_GLOBAL_STATIC(QNetworkAccessDebugPipeBackendFactory, debugpipeBackend)
83#endif
84
85Q_APPLICATION_STATIC(QFactoryLoader, qnabfLoader, QNetworkAccessBackendFactory_iid, "/networkaccess"_L1)
86
87#if defined(Q_OS_MACOS)
88bool getProxyAuth(const QString& proxyHostname, const QString &scheme, QString& username, QString& password)
89{
90 CFStringRef protocolType = nullptr;
91 if (scheme.compare("ftp"_L1, Qt::CaseInsensitive) == 0) {
92 protocolType = kSecAttrProtocolFTPProxy;
93 } else if (scheme.compare("http"_L1, Qt::CaseInsensitive) == 0
94 || scheme.compare("preconnect-http"_L1, Qt::CaseInsensitive) == 0) {
95 protocolType = kSecAttrProtocolHTTPProxy;
96 } else if (scheme.compare("https"_L1,Qt::CaseInsensitive)==0
97 || scheme.compare("preconnect-https"_L1, Qt::CaseInsensitive) == 0) {
98 protocolType = kSecAttrProtocolHTTPSProxy;
99 } else {
100 qCWarning(lcQnam) << "Cannot query user name and password for a proxy, unnknown protocol:"
101 << scheme;
102 return false;
103 }
104
105 QCFType<CFMutableDictionaryRef> query(CFDictionaryCreateMutable(kCFAllocatorDefault,
106 0, nullptr, nullptr));
107 Q_ASSERT(query);
108
109 CFDictionaryAddValue(query, kSecClass, kSecClassInternetPassword);
110 CFDictionaryAddValue(query, kSecAttrProtocol, protocolType);
111
112 QCFType<CFStringRef> serverName; // Note the scope.
113 if (proxyHostname.size()) {
114 serverName = proxyHostname.toCFString();
115 CFDictionaryAddValue(query, kSecAttrServer, serverName);
116 }
117
118 // This is to get the user name in the result:
119 CFDictionaryAddValue(query, kSecReturnAttributes, kCFBooleanTrue);
120 // This one to get the password:
121 CFDictionaryAddValue(query, kSecReturnData, kCFBooleanTrue);
122
123 // The default for kSecMatchLimit key is 1 (the first match only), which is fine,
124 // so don't set this value explicitly.
125
126 QCFType<CFTypeRef> replyData;
127 if (SecItemCopyMatching(query, &replyData) != errSecSuccess) {
128 qCWarning(lcQnam, "Failed to extract user name and password from the keychain.");
129 return false;
130 }
131
132 if (!replyData || CFDictionaryGetTypeID() != CFGetTypeID(replyData)) {
133 qCWarning(lcQnam, "Query returned data in unexpected format.");
134 return false;
135 }
136
137 CFDictionaryRef accountData = replyData.as<CFDictionaryRef>();
138 const void *value = CFDictionaryGetValue(accountData, kSecAttrAccount);
139 if (!value || CFGetTypeID(value) != CFStringGetTypeID()) {
140 qCWarning(lcQnam, "Cannot find user name or its format is unknown.");
141 return false;
142 }
143 username = QString::fromCFString(static_cast<CFStringRef>(value));
144
145 value = CFDictionaryGetValue(accountData, kSecValueData);
146 if (!value || CFGetTypeID(value) != CFDataGetTypeID()) {
147 qCWarning(lcQnam, "Cannot find password or its format is unknown.");
148 return false;
149 }
150 const CFDataRef passData = static_cast<const CFDataRef>(value);
151 password = QString::fromLocal8Bit(reinterpret_cast<const char *>(CFDataGetBytePtr(passData)),
152 qsizetype(CFDataGetLength(passData)));
153 return true;
154}
155#endif // Q_OS_MACOS
156
157
158
159static void ensureInitialized()
160{
161#if QT_CONFIG(private_tests)
162 (void) debugpipeBackend();
163#endif
164
165 // leave this one last since it will query the special QAbstractFileEngines
166 (void) fileBackend();
167}
168
169/*!
170 \class QNetworkAccessManager
171 \brief The QNetworkAccessManager class allows the application to
172 send network requests and receive replies.
173 \since 4.4
174
175 \ingroup network
176 \inmodule QtNetwork
177 \reentrant
178
179 The Network Access API is constructed around one QNetworkAccessManager
180 object, which holds the common configuration and settings for the requests
181 it sends. It contains the proxy and cache configuration, as well as the
182 signals related to such issues, and reply signals that can be used to
183 monitor the progress of a network operation. One QNetworkAccessManager
184 instance should be enough for the whole Qt application. Since
185 QNetworkAccessManager is based on QObject, it can only be used from the
186 thread it belongs to.
187
188 Once a QNetworkAccessManager object has been created, the application can
189 use it to send requests over the network. A group of standard functions
190 is supplied that take a request and optional data, and each returns a
191 QNetworkReply object. The returned object is used to obtain any data
192 returned in response to the corresponding request.
193
194 A simple download off the network could be accomplished with:
195 \snippet code/src_network_access_qnetworkaccessmanager.cpp 0
196
197 QNetworkAccessManager has an asynchronous API.
198 When the \tt replyFinished slot above is called, the parameter it
199 takes is the QNetworkReply object containing the downloaded data
200 as well as meta-data (headers, etc.).
201
202 \note After the request has finished, it is the responsibility of the user
203 to delete the QNetworkReply object at an appropriate time. Do not directly
204 delete it inside the slot connected to finished(). You can use the
205 deleteLater() function.
206
207 \note QNetworkAccessManager queues the requests it receives. The number
208 of requests executed in parallel is dependent on the protocol.
209 Currently, for the HTTP protocol on desktop platforms, 6 requests are
210 executed in parallel for one host/port combination.
211
212 \note QNetworkAccessManager doesn't handle RFC 2616 Section 8.2.2 properly,
213 in that it doesn't react to incoming data until it's done writing. For
214 example, the upload of a large file won't stop even if the server returns
215 a status code that instructs the client to not continue.
216
217 A more involved example, assuming the manager is already existent,
218 can be:
219 \snippet code/src_network_access_qnetworkaccessmanager.cpp 1
220
221 \sa QNetworkRequest, QNetworkReply, QNetworkProxy
222*/
223
224/*!
225 \enum QNetworkAccessManager::Operation
226
227 Indicates the operation this reply is processing.
228
229 \value HeadOperation retrieve headers operation (created
230 with head())
231
232 \value GetOperation retrieve headers and download contents
233 (created with get())
234
235 \value PutOperation upload contents operation (created
236 with put())
237
238 \value PostOperation send the contents of an HTML form for
239 processing via HTTP POST (created with post())
240
241 \value DeleteOperation delete contents operation (created with
242 deleteResource())
243
244 \value CustomOperation custom operation (created with
245 sendCustomRequest()) \since 4.7
246
247 \omitvalue UnknownOperation
248
249 \sa QNetworkReply::operation()
250*/
251
252/*!
253 \fn void QNetworkAccessManager::networkSessionConnected()
254
255 \since 4.7
256 \deprecated
257
258 \internal
259
260 This signal is emitted when the status of the network session changes into a usable (Connected)
261 state. It is used to signal to QNetworkReplys to start or migrate their network operation once
262 the network session has been opened or finished roaming.
263*/
264
265/*!
266 \fn void QNetworkAccessManager::proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator)
267
268 This signal is emitted whenever a proxy requests authentication
269 and QNetworkAccessManager cannot find a valid, cached
270 credential. The slot connected to this signal should fill in the
271 credentials for the proxy \a proxy in the \a authenticator object.
272
273 QNetworkAccessManager will cache the credentials internally. The
274 next time the proxy requests authentication, QNetworkAccessManager
275 will automatically send the same credential without emitting the
276 proxyAuthenticationRequired signal again.
277
278 If the proxy rejects the credentials, QNetworkAccessManager will
279 emit the signal again.
280
281 \sa proxy(), setProxy(), authenticationRequired()
282*/
283
284/*!
285 \fn void QNetworkAccessManager::authenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator)
286
287 This signal is emitted whenever a final server requests
288 authentication before it delivers the requested contents. The slot
289 connected to this signal should fill the credentials for the
290 contents (which can be determined by inspecting the \a reply
291 object) in the \a authenticator object.
292
293 QNetworkAccessManager will cache the credentials internally and
294 will send the same values if the server requires authentication
295 again, without emitting the authenticationRequired() signal. If it
296 rejects the credentials, this signal will be emitted again.
297
298 \note To have the request not send credentials you must not call
299 setUser() or setPassword() on the \a authenticator object. This
300 will result in the \l finished() signal being emitted with a
301 \l QNetworkReply with error \l {QNetworkReply::} {AuthenticationRequiredError}.
302
303 \note It is not possible to use a QueuedConnection to connect to
304 this signal, as the connection will fail if the authenticator has
305 not been filled in with new information when the signal returns.
306
307 \sa proxyAuthenticationRequired(), QAuthenticator::setUser(), QAuthenticator::setPassword()
308*/
309
310/*!
311 \fn void QNetworkAccessManager::finished(QNetworkReply *reply)
312
313 This signal is emitted whenever a pending network reply is
314 finished. The \a reply parameter will contain a pointer to the
315 reply that has just finished. This signal is emitted in tandem
316 with the QNetworkReply::finished() signal.
317
318 See QNetworkReply::finished() for information on the status that
319 the object will be in.
320
321 \note Do not delete the \a reply object in the slot connected to this
322 signal. Use deleteLater().
323
324 \sa QNetworkReply::finished(), QNetworkReply::error()
325*/
326
327/*!
328 \fn void QNetworkAccessManager::encrypted(QNetworkReply *reply)
329 \since 5.1
330
331 This signal is emitted when an SSL/TLS session has successfully
332 completed the initial handshake. At this point, no user data
333 has been transmitted. The signal can be used to perform
334 additional checks on the certificate chain, for example to
335 notify users when the certificate for a website has changed. The
336 \a reply parameter specifies which network reply is responsible.
337 If the reply does not match the expected criteria then it should
338 be aborted by calling QNetworkReply::abort() by a slot connected
339 to this signal. The SSL configuration in use can be inspected
340 using the QNetworkReply::sslConfiguration() method.
341
342 Internally, QNetworkAccessManager may open multiple connections
343 to a server, in order to allow it process requests in parallel.
344 These connections may be reused, which means that the encrypted()
345 signal would not be emitted. This means that you are only
346 guaranteed to receive this signal for the first connection to a
347 site in the lifespan of the QNetworkAccessManager.
348
349 \sa QSslSocket::encrypted()
350 \sa QNetworkReply::encrypted()
351*/
352
353/*!
354 \fn void QNetworkAccessManager::sslErrors(QNetworkReply *reply, const QList<QSslError> &errors)
355
356 This signal is emitted if the SSL/TLS session encountered errors
357 during the set up, including certificate verification errors. The
358 \a errors parameter contains the list of errors and \a reply is
359 the QNetworkReply that is encountering these errors.
360
361 To indicate that the errors are not fatal and that the connection
362 should proceed, the QNetworkReply::ignoreSslErrors() function should be called
363 from the slot connected to this signal. If it is not called, the
364 SSL session will be torn down before any data is exchanged
365 (including the URL).
366
367 This signal can be used to display an error message to the user
368 indicating that security may be compromised and display the
369 SSL settings (see sslConfiguration() to obtain it). If the user
370 decides to proceed after analyzing the remote certificate, the
371 slot should call ignoreSslErrors().
372
373 \sa QSslSocket::sslErrors(), QNetworkReply::sslErrors(),
374 QNetworkReply::sslConfiguration(), QNetworkReply::ignoreSslErrors()
375*/
376
377/*!
378 \fn void QNetworkAccessManager::preSharedKeyAuthenticationRequired(QNetworkReply *reply, QSslPreSharedKeyAuthenticator *authenticator)
379 \since 5.5
380
381 This signal is emitted if the SSL/TLS handshake negotiates a PSK
382 ciphersuite, and therefore a PSK authentication is then required.
383 The \a reply object is the QNetworkReply that is negotiating
384 such ciphersuites.
385
386 When using PSK, the client must send to the server a valid identity and a
387 valid pre shared key, in order for the SSL handshake to continue.
388 Applications can provide this information in a slot connected to this
389 signal, by filling in the passed \a authenticator object according to their
390 needs.
391
392 \note Ignoring this signal, or failing to provide the required credentials,
393 will cause the handshake to fail, and therefore the connection to be aborted.
394
395 \note The \a authenticator object is owned by the reply and must not be
396 deleted by the application.
397
398 \sa QSslPreSharedKeyAuthenticator
399*/
400
401/*!
402 Constructs a QNetworkAccessManager object that is the center of
403 the Network Access API and sets \a parent as the parent object.
404*/
405QNetworkAccessManager::QNetworkAccessManager(QObject *parent)
406 : QObject(*new QNetworkAccessManagerPrivate, parent)
407{
408 ensureInitialized();
409 d_func()->ensureBackendPluginsLoaded();
410
411 qRegisterMetaType<QNetworkReply::NetworkError>();
412#ifndef QT_NO_NETWORKPROXY
413 qRegisterMetaType<QNetworkProxy>();
414#endif
415#ifndef QT_NO_SSL
416 qRegisterMetaType<QList<QSslError> >();
417 qRegisterMetaType<QSslConfiguration>();
418 qRegisterMetaType<QSslPreSharedKeyAuthenticator *>();
419#endif
420 qRegisterMetaType<QList<std::pair<QByteArray, QByteArray>>>();
421#if QT_CONFIG(http)
422 qRegisterMetaType<QHttpNetworkRequest>();
423#endif
424 qRegisterMetaType<QNetworkReply::NetworkError>();
425 qRegisterMetaType<QSharedPointer<char> >();
426}
427
428/*!
429 Destroys the QNetworkAccessManager object and frees up any
430 resources. Note that QNetworkReply objects that are returned from
431 this class have this object set as their parents, which means that
432 they will be deleted along with it if you don't call
433 QObject::setParent() on them.
434*/
435QNetworkAccessManager::~QNetworkAccessManager()
436{
437#ifndef QT_NO_NETWORKPROXY
438 delete d_func()->proxyFactory;
439#endif
440
441 // Delete the QNetworkReply children first.
442 // Else a QAbstractNetworkCache might get deleted in ~QObject
443 // before a QNetworkReply that accesses the QAbstractNetworkCache
444 // object in its destructor.
445 qDeleteAll(c: findChildren<QNetworkReply *>());
446 // The other children will be deleted in this ~QObject
447 // FIXME instead of this "hack" make the QNetworkReplyImpl
448 // properly watch the cache deletion, e.g. via a QWeakPointer.
449}
450
451#ifndef QT_NO_NETWORKPROXY
452/*!
453 Returns the QNetworkProxy that the requests sent using this
454 QNetworkAccessManager object will use. The default value for the
455 proxy is QNetworkProxy::DefaultProxy.
456
457 \sa setProxy(), setProxyFactory(), proxyAuthenticationRequired()
458*/
459QNetworkProxy QNetworkAccessManager::proxy() const
460{
461 return d_func()->proxy;
462}
463
464/*!
465 Sets the proxy to be used in future requests to be \a proxy. This
466 does not affect requests that have already been sent. The
467 proxyAuthenticationRequired() signal will be emitted if the proxy
468 requests authentication.
469
470 A proxy set with this function will be used for all requests
471 issued by QNetworkAccessManager. In some cases, it might be
472 necessary to select different proxies depending on the type of
473 request being sent or the destination host. If that's the case,
474 you should consider using setProxyFactory().
475
476 \sa proxy(), proxyAuthenticationRequired()
477*/
478void QNetworkAccessManager::setProxy(const QNetworkProxy &proxy)
479{
480 Q_D(QNetworkAccessManager);
481 delete d->proxyFactory;
482 d->proxy = proxy;
483 d->proxyFactory = nullptr;
484}
485
486/*!
487 \fn QNetworkProxyFactory *QNetworkAccessManager::proxyFactory() const
488 \since 4.5
489
490 Returns the proxy factory that this QNetworkAccessManager object
491 is using to determine the proxies to be used for requests.
492
493 Note that the pointer returned by this function is managed by
494 QNetworkAccessManager and could be deleted at any time.
495
496 \sa setProxyFactory(), proxy()
497*/
498QNetworkProxyFactory *QNetworkAccessManager::proxyFactory() const
499{
500 return d_func()->proxyFactory;
501}
502
503/*!
504 \since 4.5
505
506 Sets the proxy factory for this class to be \a factory. A proxy
507 factory is used to determine a more specific list of proxies to be
508 used for a given request, instead of trying to use the same proxy
509 value for all requests.
510
511 All queries sent by QNetworkAccessManager will have type
512 QNetworkProxyQuery::UrlRequest.
513
514 For example, a proxy factory could apply the following rules:
515 \list
516 \li if the target address is in the local network (for example,
517 if the hostname contains no dots or if it's an IP address in
518 the organization's range), return QNetworkProxy::NoProxy
519 \li if the request is FTP, return an FTP proxy
520 \li if the request is HTTP or HTTPS, then return an HTTP proxy
521 \li otherwise, return a SOCKSv5 proxy server
522 \endlist
523
524 The lifetime of the object \a factory will be managed by
525 QNetworkAccessManager. It will delete the object when necessary.
526
527 \note If a specific proxy is set with setProxy(), the factory will not
528 be used.
529
530 \sa proxyFactory(), setProxy(), QNetworkProxyQuery
531*/
532void QNetworkAccessManager::setProxyFactory(QNetworkProxyFactory *factory)
533{
534 Q_D(QNetworkAccessManager);
535 delete d->proxyFactory;
536 d->proxyFactory = factory;
537 d->proxy = QNetworkProxy();
538}
539#endif
540
541/*!
542 \since 4.5
543
544 Returns the cache that is used to store data obtained from the network.
545
546 \sa setCache()
547*/
548QAbstractNetworkCache *QNetworkAccessManager::cache() const
549{
550 Q_D(const QNetworkAccessManager);
551 return d->networkCache;
552}
553
554/*!
555 \since 4.5
556
557 Sets the manager's network cache to be the \a cache specified. The cache
558 is used for all requests dispatched by the manager.
559
560 Use this function to set the network cache object to a class that implements
561 additional features, like saving the cookies to permanent storage.
562
563 \note QNetworkAccessManager takes ownership of the \a cache object.
564
565 QNetworkAccessManager by default does not have a set cache.
566 Qt provides a simple disk cache, QNetworkDiskCache, which can be used.
567
568 \sa cache(), QNetworkRequest::CacheLoadControl
569*/
570void QNetworkAccessManager::setCache(QAbstractNetworkCache *cache)
571{
572 Q_D(QNetworkAccessManager);
573 if (d->networkCache != cache) {
574 delete d->networkCache;
575 d->networkCache = cache;
576 if (d->networkCache)
577 d->networkCache->setParent(this);
578 }
579}
580
581/*!
582 Returns the QNetworkCookieJar that is used to store cookies
583 obtained from the network as well as cookies that are about to be
584 sent.
585
586 \sa setCookieJar()
587*/
588QNetworkCookieJar *QNetworkAccessManager::cookieJar() const
589{
590 Q_D(const QNetworkAccessManager);
591 if (!d->cookieJar)
592 d->createCookieJar();
593 return d->cookieJar;
594}
595
596/*!
597 Sets the manager's cookie jar to be the \a cookieJar specified.
598 The cookie jar is used by all requests dispatched by the manager.
599
600 Use this function to set the cookie jar object to a class that
601 implements additional features, like saving the cookies to permanent
602 storage.
603
604 \note QNetworkAccessManager takes ownership of the \a cookieJar object.
605
606 If \a cookieJar is in the same thread as this QNetworkAccessManager,
607 it will set the parent of the \a cookieJar
608 so that the cookie jar is deleted when this
609 object is deleted as well. If you want to share cookie jars
610 between different QNetworkAccessManager objects, you may want to
611 set the cookie jar's parent to 0 after calling this function.
612
613 QNetworkAccessManager by default does not implement any cookie
614 policy of its own: it accepts all cookies sent by the server, as
615 long as they are well formed and meet the minimum security
616 requirements (cookie domain matches the request's and cookie path
617 matches the request's). In order to implement your own security
618 policy, override the QNetworkCookieJar::cookiesForUrl() and
619 QNetworkCookieJar::setCookiesFromUrl() virtual functions. Those
620 functions are called by QNetworkAccessManager when it detects a
621 new cookie.
622
623 \sa cookieJar(), QNetworkCookieJar::cookiesForUrl(), QNetworkCookieJar::setCookiesFromUrl()
624*/
625void QNetworkAccessManager::setCookieJar(QNetworkCookieJar *cookieJar)
626{
627 Q_D(QNetworkAccessManager);
628 d->cookieJarCreated = true;
629 if (d->cookieJar != cookieJar) {
630 if (d->cookieJar && d->cookieJar->parent() == this)
631 delete d->cookieJar;
632 d->cookieJar = cookieJar;
633 if (cookieJar && thread() == cookieJar->thread())
634 d->cookieJar->setParent(this);
635 }
636}
637
638/*!
639 \since 5.9
640
641 If \a enabled is \c true, QNetworkAccessManager follows the HTTP Strict Transport
642 Security policy (HSTS, RFC6797). When processing a request, QNetworkAccessManager
643 automatically replaces the "http" scheme with "https" and uses a secure transport
644 for HSTS hosts. If it's set explicitly, port 80 is replaced by port 443.
645
646 When HSTS is enabled, for each HTTP response containing HSTS header and
647 received over a secure transport, QNetworkAccessManager will update its HSTS
648 cache, either remembering a host with a valid policy or removing a host with
649 an expired or disabled HSTS policy.
650
651 \sa isStrictTransportSecurityEnabled()
652*/
653void QNetworkAccessManager::setStrictTransportSecurityEnabled(bool enabled)
654{
655 Q_D(QNetworkAccessManager);
656 d->stsEnabled = enabled;
657}
658
659/*!
660 \since 5.9
661
662 Returns true if HTTP Strict Transport Security (HSTS) was enabled. By default
663 HSTS is disabled.
664
665 \sa setStrictTransportSecurityEnabled()
666*/
667bool QNetworkAccessManager::isStrictTransportSecurityEnabled() const
668{
669 Q_D(const QNetworkAccessManager);
670 return d->stsEnabled;
671}
672
673/*!
674 \since 5.10
675
676 If \a enabled is \c true, the internal HSTS cache will use a persistent store
677 to read and write HSTS policies. \a storeDir defines where this store will be
678 located. The default location is defined by QStandardPaths::CacheLocation.
679 If there is no writable QStandartPaths::CacheLocation and \a storeDir is an
680 empty string, the store will be located in the program's working directory.
681
682 \note If HSTS cache already contains HSTS policies by the time persistent
683 store is enabled, these policies will be preserved in the store. In case both
684 cache and store contain the same known hosts, policies from cache are considered
685 to be more up-to-date (and thus will overwrite the previous values in the store).
686 If this behavior is undesired, enable HSTS store before enabling Strict Transport
687 Security. By default, the persistent store of HSTS policies is disabled.
688
689 \sa isStrictTransportSecurityStoreEnabled(), setStrictTransportSecurityEnabled(),
690 QStandardPaths::standardLocations()
691*/
692
693void QNetworkAccessManager::enableStrictTransportSecurityStore(bool enabled, const QString &storeDir)
694{
695#if QT_CONFIG(settings)
696 Q_D(QNetworkAccessManager);
697 d->stsStore.reset(other: enabled ? new QHstsStore(storeDir) : nullptr);
698 d->stsCache.setStore(d->stsStore.data());
699#else
700 Q_UNUSED(enabled);
701 Q_UNUSED(storeDir);
702 qWarning("HSTS permanent store requires the feature 'settings' enabled");
703#endif // QT_CONFIG(settings)
704}
705
706/*!
707 \since 5.10
708
709 Returns true if HSTS cache uses a permanent store to load and store HSTS
710 policies.
711
712 \sa enableStrictTransportSecurityStore()
713*/
714
715bool QNetworkAccessManager::isStrictTransportSecurityStoreEnabled() const
716{
717#if QT_CONFIG(settings)
718 Q_D(const QNetworkAccessManager);
719 return bool(d->stsStore.data());
720#else
721 return false;
722#endif // QT_CONFIG(settings)
723}
724
725/*!
726 \since 5.9
727
728 Adds HTTP Strict Transport Security policies into HSTS cache.
729 \a knownHosts contains the known hosts that have QHstsPolicy
730 information.
731
732 \note An expired policy will remove a known host from the cache, if previously
733 present.
734
735 \note While processing HTTP responses, QNetworkAccessManager can also update
736 the HSTS cache, removing or updating exitsting policies or introducing new
737 \a knownHosts. The current implementation thus is server-driven, client code
738 can provide QNetworkAccessManager with previously known or discovered
739 policies, but this information can be overridden by "Strict-Transport-Security"
740 response headers.
741
742 \sa strictTransportSecurityHosts(), enableStrictTransportSecurityStore(), QHstsPolicy
743*/
744
745void QNetworkAccessManager::addStrictTransportSecurityHosts(const QList<QHstsPolicy> &knownHosts)
746{
747 Q_D(QNetworkAccessManager);
748 d->stsCache.updateFromPolicies(hosts: knownHosts);
749}
750
751/*!
752 \since 5.9
753
754 Returns the list of HTTP Strict Transport Security policies. This list can
755 differ from what was initially set via addStrictTransportSecurityHosts() if
756 HSTS cache was updated from a "Strict-Transport-Security" response header.
757
758 \sa addStrictTransportSecurityHosts(), QHstsPolicy
759*/
760QList<QHstsPolicy> QNetworkAccessManager::strictTransportSecurityHosts() const
761{
762 Q_D(const QNetworkAccessManager);
763 return d->stsCache.policies();
764}
765
766/*!
767 Posts a request to obtain the network headers for \a request
768 and returns a new QNetworkReply object which will contain such headers.
769
770 The function is named after the HTTP request associated (HEAD).
771*/
772QNetworkReply *QNetworkAccessManager::head(const QNetworkRequest &request)
773{
774 return d_func()->postProcess(reply: createRequest(op: QNetworkAccessManager::HeadOperation, request));
775}
776
777/*!
778 Posts a request to obtain the contents of the target \a request
779 and returns a new QNetworkReply object opened for reading which emits the
780 \l{QIODevice::readyRead()}{readyRead()} signal whenever new data
781 arrives.
782
783 The contents as well as associated headers will be downloaded.
784
785 \sa post(), put(), deleteResource(), sendCustomRequest()
786*/
787QNetworkReply *QNetworkAccessManager::get(const QNetworkRequest &request)
788{
789 return d_func()->postProcess(reply: createRequest(op: QNetworkAccessManager::GetOperation, request));
790}
791
792/*!
793 \since 6.7
794
795 \overload
796
797 \note A GET request with a message body is not cached.
798
799 \note If the request is redirected, the message body will be kept only if the status code is
800 308.
801*/
802
803QNetworkReply *QNetworkAccessManager::get(const QNetworkRequest &request, QIODevice *data)
804{
805 QNetworkRequest newRequest(request);
806 return d_func()->postProcess(
807 reply: createRequest(op: QNetworkAccessManager::GetOperation, request: newRequest, outgoingData: data));
808}
809
810/*!
811 \since 6.7
812
813 \overload
814
815 \note A GET request with a message body is not cached.
816
817 \note If the request is redirected, the message body will be kept only if the status code is
818 308.
819*/
820
821QNetworkReply *QNetworkAccessManager::get(const QNetworkRequest &request, const QByteArray &data)
822{
823 QBuffer *buffer = new QBuffer;
824 buffer->setData(data);
825 buffer->open(openMode: QIODevice::ReadOnly);
826
827 QNetworkReply *reply = get(request, data: buffer);
828 buffer->setParent(reply);
829 return reply;
830}
831
832/*!
833 Sends an HTTP POST request to the destination specified by \a request
834 and returns a new QNetworkReply object opened for reading that will
835 contain the reply sent by the server. The contents of the \a data
836 device will be uploaded to the server.
837
838 \a data must be open for reading and must remain valid until the
839 finished() signal is emitted for this reply.
840
841 \note Sending a POST request on protocols other than HTTP and
842 HTTPS is undefined and will probably fail.
843
844 \sa get(), put(), deleteResource(), sendCustomRequest()
845*/
846QNetworkReply *QNetworkAccessManager::post(const QNetworkRequest &request, QIODevice *data)
847{
848 return d_func()->postProcess(reply: createRequest(op: QNetworkAccessManager::PostOperation, request, outgoingData: data));
849}
850
851/*!
852 \overload
853
854 Sends the contents of the \a data byte array to the destination
855 specified by \a request.
856*/
857QNetworkReply *QNetworkAccessManager::post(const QNetworkRequest &request, const QByteArray &data)
858{
859 QBuffer *buffer = new QBuffer;
860 buffer->setData(data);
861 buffer->open(openMode: QIODevice::ReadOnly);
862
863 QNetworkReply *reply = post(request, data: buffer);
864 buffer->setParent(reply);
865 return reply;
866}
867
868/*!
869 \fn QNetworkReply *QNetworkAccessManager::post(const QNetworkRequest &request, std::nullptr_t nptr)
870
871 \since 6.8
872
873 \overload
874
875 Sends the POST request specified by \a request without a body and returns
876 a new QNetworkReply object.
877*/
878
879#if QT_CONFIG(http) || defined(Q_OS_WASM)
880/*!
881 \since 4.8
882
883 \overload
884
885 Sends the contents of the \a multiPart message to the destination
886 specified by \a request.
887
888 This can be used for sending MIME multipart messages over HTTP.
889
890 \sa QHttpMultiPart, QHttpPart, put()
891*/
892QNetworkReply *QNetworkAccessManager::post(const QNetworkRequest &request, QHttpMultiPart *multiPart)
893{
894 QNetworkRequest newRequest = d_func()->prepareMultipart(request, multiPart);
895 QIODevice *device = multiPart->d_func()->device;
896 QNetworkReply *reply = post(request: newRequest, data: device);
897 return reply;
898}
899
900/*!
901 \since 4.8
902
903 \overload
904
905 Sends the contents of the \a multiPart message to the destination
906 specified by \a request.
907
908 This can be used for sending MIME multipart messages over HTTP.
909
910 \sa QHttpMultiPart, QHttpPart, post()
911*/
912QNetworkReply *QNetworkAccessManager::put(const QNetworkRequest &request, QHttpMultiPart *multiPart)
913{
914 QNetworkRequest newRequest = d_func()->prepareMultipart(request, multiPart);
915 QIODevice *device = multiPart->d_func()->device;
916 QNetworkReply *reply = put(request: newRequest, data: device);
917 return reply;
918}
919#endif // QT_CONFIG(http)
920
921/*!
922 Uploads the contents of \a data to the destination \a request and
923 returns a new QNetworkReply object that will be open for reply.
924
925 \a data must be opened for reading when this function is called
926 and must remain valid until the finished() signal is emitted for
927 this reply.
928
929 Whether anything will be available for reading from the returned
930 object is protocol dependent. For HTTP, the server may send a
931 small HTML page indicating the upload was successful (or not).
932 Other protocols will probably have content in their replies.
933
934 \note For HTTP, this request will send a PUT request, which most servers
935 do not allow. Form upload mechanisms, including that of uploading
936 files through HTML forms, use the POST mechanism.
937
938 \sa get(), post(), deleteResource(), sendCustomRequest()
939*/
940QNetworkReply *QNetworkAccessManager::put(const QNetworkRequest &request, QIODevice *data)
941{
942 return d_func()->postProcess(reply: createRequest(op: QNetworkAccessManager::PutOperation, request, outgoingData: data));
943}
944
945/*!
946 \overload
947
948 Sends the contents of the \a data byte array to the destination
949 specified by \a request.
950*/
951QNetworkReply *QNetworkAccessManager::put(const QNetworkRequest &request, const QByteArray &data)
952{
953 QBuffer *buffer = new QBuffer;
954 buffer->setData(data);
955 buffer->open(openMode: QIODevice::ReadOnly);
956
957 QNetworkReply *reply = put(request, data: buffer);
958 buffer->setParent(reply);
959 return reply;
960}
961
962/*!
963 \since 6.8
964
965 \overload
966
967 \fn QNetworkReply *QNetworkAccessManager::put(const QNetworkRequest &request, std::nullptr_t nptr)
968
969 Sends the PUT request specified by \a request without a body and returns
970 a new QNetworkReply object.
971*/
972
973/*!
974 \since 4.6
975
976 Sends a request to delete the resource identified by the URL of \a request.
977
978 \note This feature is currently available for HTTP only, performing an
979 HTTP DELETE request.
980
981 \sa get(), post(), put(), sendCustomRequest()
982*/
983QNetworkReply *QNetworkAccessManager::deleteResource(const QNetworkRequest &request)
984{
985 return d_func()->postProcess(reply: createRequest(op: QNetworkAccessManager::DeleteOperation, request));
986}
987
988#ifndef QT_NO_SSL
989/*!
990 \since 5.2
991
992 Initiates a connection to the host given by \a hostName at port \a port, using
993 \a sslConfiguration. This function is useful to complete the TCP and SSL handshake
994 to a host before the HTTPS request is made, resulting in a lower network latency.
995
996 \note Preconnecting a HTTP/2 connection can be done by calling setAllowedNextProtocols()
997 on \a sslConfiguration with QSslConfiguration::ALPNProtocolHTTP2 contained in
998 the list of allowed protocols. When using HTTP/2, one single connection per host is
999 enough, i.e. calling this method multiple times per host will not result in faster
1000 network transactions.
1001
1002 \note This function has no possibility to report errors.
1003
1004 \sa connectToHost(), get(), post(), put(), deleteResource()
1005*/
1006
1007void QNetworkAccessManager::connectToHostEncrypted(const QString &hostName, quint16 port,
1008 const QSslConfiguration &sslConfiguration)
1009{
1010 connectToHostEncrypted(hostName, port, sslConfiguration, peerName: QString());
1011}
1012
1013/*!
1014 \since 5.13
1015 \overload
1016
1017 Initiates a connection to the host given by \a hostName at port \a port, using
1018 \a sslConfiguration with \a peerName set to be the hostName used for certificate
1019 validation. This function is useful to complete the TCP and SSL handshake
1020 to a host before the HTTPS request is made, resulting in a lower network latency.
1021
1022 \note Preconnecting a HTTP/2 connection can be done by calling setAllowedNextProtocols()
1023 on \a sslConfiguration with QSslConfiguration::ALPNProtocolHTTP2 contained in
1024 the list of allowed protocols. When using HTTP/2, one single connection per host is
1025 enough, i.e. calling this method multiple times per host will not result in faster
1026 network transactions.
1027
1028 \note This function has no possibility to report errors.
1029
1030 \sa connectToHost(), get(), post(), put(), deleteResource()
1031*/
1032
1033void QNetworkAccessManager::connectToHostEncrypted(const QString &hostName, quint16 port,
1034 const QSslConfiguration &sslConfiguration,
1035 const QString &peerName)
1036{
1037 QUrl url;
1038 url.setHost(host: hostName);
1039 url.setPort(port);
1040 url.setScheme("preconnect-https"_L1);
1041 QNetworkRequest request(url);
1042 if (sslConfiguration != QSslConfiguration::defaultConfiguration())
1043 request.setSslConfiguration(sslConfiguration);
1044
1045 // There is no way to enable HTTP2 via a request after having established the connection,
1046 // so we need to check the ssl configuration whether HTTP2 is allowed here.
1047 if (!sslConfiguration.allowedNextProtocols().contains(t: QSslConfiguration::ALPNProtocolHTTP2))
1048 request.setAttribute(code: QNetworkRequest::Http2AllowedAttribute, value: false);
1049
1050 request.setPeerVerifyName(peerName);
1051 get(request);
1052}
1053#endif
1054
1055/*!
1056 \since 5.2
1057
1058 Initiates a connection to the host given by \a hostName at port \a port.
1059 This function is useful to complete the TCP handshake
1060 to a host before the HTTP request is made, resulting in a lower network latency.
1061
1062 \note This function has no possibility to report errors.
1063
1064 \sa connectToHostEncrypted(), get(), post(), put(), deleteResource()
1065*/
1066void QNetworkAccessManager::connectToHost(const QString &hostName, quint16 port)
1067{
1068 QUrl url;
1069 url.setHost(host: hostName);
1070 url.setPort(port);
1071 url.setScheme("preconnect-http"_L1);
1072 QNetworkRequest request(url);
1073 get(request);
1074}
1075
1076/*!
1077 \since 5.9
1078
1079 Sets the manager's redirect policy to be the \a policy specified. This policy
1080 will affect all subsequent requests created by the manager.
1081
1082 Use this function to enable or disable HTTP redirects on the manager's level.
1083
1084 \note When creating a request QNetworkRequest::RedirectAttributePolicy has
1085 the highest priority, next by priority the manager's policy.
1086
1087 The default value is QNetworkRequest::NoLessSafeRedirectPolicy.
1088 Clients relying on manual redirect handling are encouraged to set
1089 this policy explicitly in their code.
1090
1091 \sa redirectPolicy(), QNetworkRequest::RedirectPolicy
1092*/
1093void QNetworkAccessManager::setRedirectPolicy(QNetworkRequest::RedirectPolicy policy)
1094{
1095 Q_D(QNetworkAccessManager);
1096 d->redirectPolicy = policy;
1097}
1098
1099/*!
1100 \since 5.9
1101
1102 Returns the redirect policy that is used when creating new requests.
1103
1104 \sa setRedirectPolicy(), QNetworkRequest::RedirectPolicy
1105*/
1106QNetworkRequest::RedirectPolicy QNetworkAccessManager::redirectPolicy() const
1107{
1108 Q_D(const QNetworkAccessManager);
1109 return d->redirectPolicy;
1110}
1111
1112/*!
1113 \since 4.7
1114
1115 Sends a custom request to the server identified by the URL of \a request.
1116
1117 It is the user's responsibility to send a \a verb to the server that is valid
1118 according to the HTTP specification.
1119
1120 This method provides means to send verbs other than the common ones provided
1121 via get() or post() etc., for instance sending an HTTP OPTIONS command.
1122
1123 If \a data is not empty, the contents of the \a data
1124 device will be uploaded to the server; in that case, data must be open for
1125 reading and must remain valid until the finished() signal is emitted for this reply.
1126
1127 \note This feature is currently available for HTTP(S) only.
1128
1129 \sa get(), post(), put(), deleteResource()
1130*/
1131QNetworkReply *QNetworkAccessManager::sendCustomRequest(const QNetworkRequest &request, const QByteArray &verb, QIODevice *data)
1132{
1133 QNetworkRequest newRequest(request);
1134 newRequest.setAttribute(code: QNetworkRequest::CustomVerbAttribute, value: verb);
1135 return d_func()->postProcess(reply: createRequest(op: QNetworkAccessManager::CustomOperation, request: newRequest, outgoingData: data));
1136}
1137
1138/*!
1139 \since 5.8
1140
1141 \overload
1142
1143 Sends the contents of the \a data byte array to the destination
1144 specified by \a request.
1145*/
1146QNetworkReply *QNetworkAccessManager::sendCustomRequest(const QNetworkRequest &request, const QByteArray &verb, const QByteArray &data)
1147{
1148 QBuffer *buffer = new QBuffer;
1149 buffer->setData(data);
1150 buffer->open(openMode: QIODevice::ReadOnly);
1151
1152 QNetworkReply *reply = sendCustomRequest(request, verb, data: buffer);
1153 buffer->setParent(reply);
1154 return reply;
1155}
1156
1157#if QT_CONFIG(http) || defined(Q_OS_WASM)
1158/*!
1159 \since 5.8
1160
1161 \overload
1162
1163 Sends a custom request to the server identified by the URL of \a request.
1164
1165 Sends the contents of the \a multiPart message to the destination
1166 specified by \a request.
1167
1168 This can be used for sending MIME multipart messages for custom verbs.
1169
1170 \sa QHttpMultiPart, QHttpPart, put()
1171*/
1172QNetworkReply *QNetworkAccessManager::sendCustomRequest(const QNetworkRequest &request, const QByteArray &verb, QHttpMultiPart *multiPart)
1173{
1174 QNetworkRequest newRequest = d_func()->prepareMultipart(request, multiPart);
1175 QIODevice *device = multiPart->d_func()->device;
1176 QNetworkReply *reply = sendCustomRequest(request: newRequest, verb, data: device);
1177 return reply;
1178}
1179#endif // QT_CONFIG(http)
1180
1181/*!
1182 Returns a new QNetworkReply object to handle the operation \a op
1183 and request \a originalReq. The device \a outgoingData is always 0
1184 for Get and Head requests, but is the value passed to post() and
1185 put() in those operations (the QByteArray variants will pass a QBuffer
1186 object).
1187
1188 The default implementation calls QNetworkCookieJar::cookiesForUrl()
1189 on the cookie jar set with setCookieJar() to obtain the cookies to
1190 be sent to the remote server.
1191
1192 The returned object must be in an open state.
1193*/
1194QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Operation op,
1195 const QNetworkRequest &originalReq,
1196 QIODevice *outgoingData)
1197{
1198 Q_D(QNetworkAccessManager);
1199
1200 QNetworkRequest req(originalReq);
1201 if (redirectPolicy() != QNetworkRequest::NoLessSafeRedirectPolicy
1202 && req.attribute(code: QNetworkRequest::RedirectPolicyAttribute).isNull()) {
1203 req.setAttribute(code: QNetworkRequest::RedirectPolicyAttribute, value: redirectPolicy());
1204 }
1205
1206#if QT_CONFIG(http) || defined (Q_OS_WASM)
1207 if (req.transferTimeoutAsDuration() == 0ms)
1208 req.setTransferTimeout(transferTimeoutAsDuration());
1209#endif
1210
1211 if (autoDeleteReplies()
1212 && req.attribute(code: QNetworkRequest::AutoDeleteReplyOnFinishAttribute).isNull()) {
1213 req.setAttribute(code: QNetworkRequest::AutoDeleteReplyOnFinishAttribute, value: true);
1214 }
1215
1216 bool isLocalFile = req.url().isLocalFile();
1217 QString scheme = req.url().scheme();
1218
1219 // Remap local+http to unix+http to make further processing easier
1220 if (scheme == "local+http"_L1) {
1221 scheme = u"unix+http"_s;
1222 QUrl url = req.url();
1223 url.setScheme(scheme);
1224 req.setUrl(url);
1225 }
1226
1227 // fast path for GET on file:// URLs
1228 // The QNetworkAccessFileBackend will right now only be used for PUT
1229 if (op == QNetworkAccessManager::GetOperation
1230 || op == QNetworkAccessManager::HeadOperation) {
1231 if (isLocalFile
1232#ifdef Q_OS_ANDROID
1233 || scheme == "assets"_L1
1234#endif
1235 || scheme == "qrc"_L1) {
1236 return new QNetworkReplyFileImpl(this, req, op);
1237 }
1238
1239 if (scheme == "data"_L1)
1240 return new QNetworkReplyDataImpl(this, req, op);
1241
1242 // A request with QNetworkRequest::AlwaysCache does not need any bearer management
1243 QNetworkRequest::CacheLoadControl mode =
1244 static_cast<QNetworkRequest::CacheLoadControl>(
1245 req.attribute(code: QNetworkRequest::CacheLoadControlAttribute,
1246 defaultValue: QNetworkRequest::PreferNetwork).toInt());
1247 if (mode == QNetworkRequest::AlwaysCache) {
1248 // FIXME Implement a QNetworkReplyCacheImpl instead, see QTBUG-15106
1249 QNetworkReplyImpl *reply = new QNetworkReplyImpl(this);
1250 QNetworkReplyImplPrivate *priv = reply->d_func();
1251 priv->manager = this;
1252 priv->backend = new QNetworkAccessCacheBackend();
1253 priv->backend->setManagerPrivate(this->d_func());
1254 priv->backend->setParent(reply);
1255 priv->backend->setReplyPrivate(priv);
1256 priv->setup(op, request: req, outgoingData);
1257 return reply;
1258 }
1259 }
1260 QNetworkRequest request = req;
1261 auto h = request.headers();
1262#ifndef Q_OS_WASM // Content-length header is not allowed to be set by user in wasm
1263 if (!h.contains(name: QHttpHeaders::WellKnownHeader::ContentLength) &&
1264 outgoingData && !outgoingData->isSequential()) {
1265 // request has no Content-Length
1266 // but the data that is outgoing is random-access
1267 h.append(name: QHttpHeaders::WellKnownHeader::ContentLength,
1268 value: QByteArray::number(outgoingData->size()));
1269 }
1270#endif
1271 if (static_cast<QNetworkRequest::LoadControl>
1272 (request.attribute(code: QNetworkRequest::CookieLoadControlAttribute,
1273 defaultValue: QNetworkRequest::Automatic).toInt()) == QNetworkRequest::Automatic) {
1274 if (d->cookieJar) {
1275 QList<QNetworkCookie> cookies = d->cookieJar->cookiesForUrl(url: request.url());
1276 if (!cookies.isEmpty())
1277 h.replaceOrAppend(name: QHttpHeaders::WellKnownHeader::Cookie,
1278 newValue: QNetworkHeadersPrivate::fromCookieList(cookies));
1279 }
1280 }
1281 request.setHeaders(std::move(h));
1282#ifdef Q_OS_WASM
1283 Q_UNUSED(isLocalFile);
1284 // Support http, https, and relative urls
1285 if (scheme == "http"_L1 || scheme == "https"_L1 || scheme.isEmpty()) {
1286 QNetworkReplyWasmImpl *reply = new QNetworkReplyWasmImpl(this);
1287 QNetworkReplyWasmImplPrivate *priv = reply->d_func();
1288 priv->manager = this;
1289 priv->setup(op, request, outgoingData);
1290 return reply;
1291 }
1292#endif
1293
1294#if QT_CONFIG(http)
1295 constexpr char16_t httpSchemes[][17] = {
1296 u"http",
1297 u"preconnect-http",
1298#ifndef QT_NO_SSL
1299 u"https",
1300 u"preconnect-https",
1301#endif
1302 u"unix+http",
1303 };
1304 // Since Qt 5 we use the new QNetworkReplyHttpImpl
1305 if (std::find(first: std::begin(arr: httpSchemes), last: std::end(arr: httpSchemes), val: scheme) != std::end(arr: httpSchemes)) {
1306
1307#ifndef QT_NO_SSL
1308 const bool isLocalSocket = scheme.startsWith(s: "unix"_L1);
1309 if (!isLocalSocket && isStrictTransportSecurityEnabled()
1310 && d->stsCache.isKnownHost(url: request.url())) {
1311 QUrl stsUrl(request.url());
1312 // RFC6797, 8.3:
1313 // The UA MUST replace the URI scheme with "https" [RFC2818],
1314 // and if the URI contains an explicit port component of "80",
1315 // then the UA MUST convert the port component to be "443", or
1316 // if the URI contains an explicit port component that is not
1317 // equal to "80", the port component value MUST be preserved;
1318 // otherwise,
1319 // if the URI does not contain an explicit port component, the UA
1320 // MUST NOT add one.
1321 if (stsUrl.port() == 80)
1322 stsUrl.setPort(443);
1323 stsUrl.setScheme("https"_L1);
1324 request.setUrl(stsUrl);
1325 }
1326#endif
1327 QNetworkReplyHttpImpl *reply = new QNetworkReplyHttpImpl(this, request, op, outgoingData);
1328 return reply;
1329 }
1330#endif // QT_CONFIG(http)
1331
1332 // first step: create the reply
1333 QNetworkReplyImpl *reply = new QNetworkReplyImpl(this);
1334 QNetworkReplyImplPrivate *priv = reply->d_func();
1335 priv->manager = this;
1336
1337 // second step: fetch cached credentials
1338 // This is not done for the time being, we should use signal emissions to request
1339 // the credentials from cache.
1340
1341 // third step: find a backend
1342 priv->backend = d->findBackend(op, request);
1343
1344 if (priv->backend) {
1345 priv->backend->setParent(reply);
1346 priv->backend->setReplyPrivate(priv);
1347 }
1348
1349#ifndef QT_NO_SSL
1350 reply->setSslConfiguration(request.sslConfiguration());
1351#endif
1352
1353 // fourth step: setup the reply
1354 priv->setup(op, request, outgoingData);
1355
1356 return reply;
1357}
1358
1359/*!
1360 \since 5.2
1361
1362 Lists all the URL schemes supported by the access manager.
1363
1364 Reimplement this method to provide your own supported schemes
1365 in a QNetworkAccessManager subclass. It is for instance necessary
1366 when your subclass provides support for new protocols.
1367*/
1368QStringList QNetworkAccessManager::supportedSchemes() const
1369{
1370 QStringList schemes;
1371 QNetworkAccessManager *self = const_cast<QNetworkAccessManager *>(this); // We know we call a const slot
1372 QMetaObject::invokeMethod(obj: self, member: "supportedSchemesImplementation", c: Qt::DirectConnection,
1373 Q_RETURN_ARG(QStringList, schemes));
1374 schemes.removeDuplicates();
1375 return schemes;
1376}
1377
1378/*!
1379 \since 5.2
1380 \deprecated
1381
1382 Lists all the URL schemes supported by the access manager.
1383
1384 You should not call this function directly; use
1385 QNetworkAccessManager::supportedSchemes() instead.
1386
1387 Because of binary compatibility constraints, the supportedSchemes()
1388 method (introduced in Qt 5.2) was not virtual in Qt 5, but now it
1389 is. Override the supportedSchemes method rather than this one.
1390
1391 \sa supportedSchemes()
1392*/
1393QStringList QNetworkAccessManager::supportedSchemesImplementation() const
1394{
1395 Q_D(const QNetworkAccessManager);
1396
1397 QStringList schemes = d->backendSupportedSchemes();
1398 // Those ones don't exist in backends
1399#if QT_CONFIG(http)
1400 schemes << QStringLiteral("http");
1401 schemes << QStringLiteral("unix+http");
1402 schemes << QStringLiteral("local+http");
1403#ifndef QT_NO_SSL
1404 if (QSslSocket::supportsSsl())
1405 schemes << QStringLiteral("https");
1406#endif
1407#endif
1408 schemes << QStringLiteral("data");
1409 return schemes;
1410}
1411
1412/*!
1413 \since 5.0
1414
1415 Flushes the internal cache of authentication data and network connections.
1416
1417 This function is useful for doing auto tests.
1418
1419 \sa clearConnectionCache()
1420*/
1421void QNetworkAccessManager::clearAccessCache()
1422{
1423 QNetworkAccessManagerPrivate::clearAuthenticationCache(manager: this);
1424 QNetworkAccessManagerPrivate::clearConnectionCache(manager: this);
1425}
1426
1427/*!
1428 \since 5.9
1429
1430 Flushes the internal cache of network connections.
1431 In contrast to clearAccessCache() the authentication data
1432 is preserved.
1433
1434 \sa clearAccessCache()
1435*/
1436void QNetworkAccessManager::clearConnectionCache()
1437{
1438 QNetworkAccessManagerPrivate::clearConnectionCache(manager: this);
1439}
1440
1441
1442/*!
1443 \since 5.14
1444
1445 Returns the true if QNetworkAccessManager is currently configured
1446 to automatically delete QNetworkReplies, false otherwise.
1447
1448 \sa setAutoDeleteReplies,
1449 QNetworkRequest::AutoDeleteReplyOnFinishAttribute
1450*/
1451bool QNetworkAccessManager::autoDeleteReplies() const
1452{
1453 return d_func()->autoDeleteReplies;
1454}
1455
1456/*!
1457 \since 5.14
1458
1459 Enables or disables automatic deletion of \l {QNetworkReply} {QNetworkReplies}.
1460
1461 Setting \a shouldAutoDelete to true is the same as setting the
1462 QNetworkRequest::AutoDeleteReplyOnFinishAttribute attribute to
1463 true on all \e{future} \l {QNetworkRequest} {QNetworkRequests}
1464 passed to this instance of QNetworkAccessManager unless the
1465 attribute was already explicitly set on the QNetworkRequest.
1466
1467 \sa autoDeleteReplies,
1468 QNetworkRequest::AutoDeleteReplyOnFinishAttribute
1469*/
1470void QNetworkAccessManager::setAutoDeleteReplies(bool shouldAutoDelete)
1471{
1472 d_func()->autoDeleteReplies = shouldAutoDelete;
1473}
1474
1475/*!
1476 \fn int QNetworkAccessManager::transferTimeout() const
1477 \since 5.15
1478
1479 Returns the timeout used for transfers, in milliseconds.
1480
1481 \sa setTransferTimeout()
1482*/
1483
1484/*!
1485 \fn void QNetworkAccessManager::setTransferTimeout(int timeout)
1486 \since 5.15
1487
1488 Sets \a timeout as the transfer timeout in milliseconds.
1489
1490 \sa setTransferTimeout(std::chrono::milliseconds),
1491 transferTimeout(), transferTimeoutAsDuration()
1492*/
1493
1494/*!
1495 \since 6.7
1496
1497 Returns the timeout duration after which the transfer is aborted if no
1498 data is exchanged.
1499
1500 The default duration is zero, which means that the timeout is not used.
1501
1502 \sa setTransferTimeout(std::chrono::milliseconds)
1503 */
1504std::chrono::milliseconds QNetworkAccessManager::transferTimeoutAsDuration() const
1505{
1506 return d_func()->transferTimeout;
1507}
1508
1509/*!
1510 \since 6.7
1511
1512 Sets the timeout \a duration to abort the transfer if no data is exchanged.
1513
1514 Transfers are aborted if no bytes are transferred before
1515 the timeout expires. Zero means no timer is set. If no
1516 argument is provided, the timeout is
1517 QNetworkRequest::DefaultTransferTimeout. If this function
1518 is not called, the timeout is disabled and has the
1519 value zero. The request-specific non-zero timeouts set for
1520 the requests that are executed override this value. This means
1521 that if QNetworkAccessManager has an enabled timeout, it needs
1522 to be disabled to execute a request without a timeout.
1523
1524 \sa transferTimeoutAsDuration()
1525 */
1526void QNetworkAccessManager::setTransferTimeout(std::chrono::milliseconds duration)
1527{
1528 d_func()->transferTimeout = duration;
1529}
1530
1531void QNetworkAccessManagerPrivate::_q_replyFinished(QNetworkReply *reply)
1532{
1533 Q_Q(QNetworkAccessManager);
1534
1535 emit q->finished(reply);
1536 if (reply->request().attribute(code: QNetworkRequest::AutoDeleteReplyOnFinishAttribute, defaultValue: false).toBool())
1537 QMetaObject::invokeMethod(object: reply, function: [reply] { reply->deleteLater(); }, type: Qt::QueuedConnection);
1538}
1539
1540void QNetworkAccessManagerPrivate::_q_replyEncrypted(QNetworkReply *reply)
1541{
1542#ifndef QT_NO_SSL
1543 Q_Q(QNetworkAccessManager);
1544 emit q->encrypted(reply);
1545#else
1546 Q_UNUSED(reply);
1547#endif
1548}
1549
1550void QNetworkAccessManagerPrivate::_q_replySslErrors(const QList<QSslError> &errors)
1551{
1552#ifndef QT_NO_SSL
1553 Q_Q(QNetworkAccessManager);
1554 QNetworkReply *reply = qobject_cast<QNetworkReply *>(object: q->sender());
1555 if (reply)
1556 emit q->sslErrors(reply, errors);
1557#else
1558 Q_UNUSED(errors);
1559#endif
1560}
1561
1562#ifndef QT_NO_SSL
1563void QNetworkAccessManagerPrivate::_q_replyPreSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator *authenticator)
1564{
1565 Q_Q(QNetworkAccessManager);
1566 QNetworkReply *reply = qobject_cast<QNetworkReply *>(object: q->sender());
1567 if (reply)
1568 emit q->preSharedKeyAuthenticationRequired(reply, authenticator);
1569}
1570#endif
1571
1572QNetworkReply *QNetworkAccessManagerPrivate::postProcess(QNetworkReply *reply)
1573{
1574 Q_Q(QNetworkAccessManager);
1575 QNetworkReplyPrivate::setManager(reply, manager: q);
1576 q->connect(sender: reply, signal: &QNetworkReply::finished, context: reply,
1577 slot: [this, reply]() { _q_replyFinished(reply); });
1578#ifndef QT_NO_SSL
1579 /* In case we're compiled without SSL support, we don't have this signal and we need to
1580 * avoid getting a connection error. */
1581 q->connect(sender: reply, signal: &QNetworkReply::encrypted, context: reply,
1582 slot: [this, reply]() { _q_replyEncrypted(reply); });
1583 q->connect(asender: reply, SIGNAL(sslErrors(QList<QSslError>)), SLOT(_q_replySslErrors(QList<QSslError>)));
1584 q->connect(asender: reply, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)), SLOT(_q_replyPreSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)));
1585#endif
1586
1587 return reply;
1588}
1589
1590void QNetworkAccessManagerPrivate::createCookieJar() const
1591{
1592 if (!cookieJarCreated) {
1593 // keep the ugly hack in here
1594 QNetworkAccessManagerPrivate *that = const_cast<QNetworkAccessManagerPrivate *>(this);
1595 that->cookieJar = new QNetworkCookieJar(that->q_func());
1596 that->cookieJarCreated = true;
1597 }
1598}
1599
1600void QNetworkAccessManagerPrivate::authenticationRequired(QAuthenticator *authenticator,
1601 QNetworkReply *reply,
1602 bool synchronous,
1603 QUrl &url,
1604 QUrl *urlForLastAuthentication,
1605 bool allowAuthenticationReuse)
1606{
1607 Q_Q(QNetworkAccessManager);
1608
1609 // don't try the cache for the same URL twice in a row
1610 // being called twice for the same URL means the authentication failed
1611 // also called when last URL is empty, e.g. on first call
1612 if (allowAuthenticationReuse && (urlForLastAuthentication->isEmpty()
1613 || url != *urlForLastAuthentication)) {
1614 // if credentials are included in the url, then use them, unless they were already used
1615 if (!url.userName().isEmpty() && !url.password().isEmpty()
1616 && (url.userName() != authenticator->user()
1617 || url.password() != authenticator->password())) {
1618 authenticator->setUser(url.userName(options: QUrl::FullyDecoded));
1619 authenticator->setPassword(url.password(QUrl::FullyDecoded));
1620 *urlForLastAuthentication = url;
1621 authenticationManager->cacheCredentials(url, auth: authenticator);
1622 return;
1623 }
1624
1625 QNetworkAuthenticationCredential cred = authenticationManager->fetchCachedCredentials(url, auth: authenticator);
1626 if (!cred.isNull()
1627 && (cred.user != authenticator->user() || cred.password != authenticator->password())) {
1628 authenticator->setUser(cred.user);
1629 authenticator->setPassword(cred.password);
1630 *urlForLastAuthentication = url;
1631 return;
1632 }
1633 }
1634
1635 // if we emit a signal here in synchronous mode, the user might spin
1636 // an event loop, which might recurse and lead to problems
1637 if (synchronous)
1638 return;
1639
1640 *urlForLastAuthentication = url;
1641 emit q->authenticationRequired(reply, authenticator);
1642 if (allowAuthenticationReuse)
1643 authenticationManager->cacheCredentials(url, auth: authenticator);
1644}
1645
1646#ifndef QT_NO_NETWORKPROXY
1647void QNetworkAccessManagerPrivate::proxyAuthenticationRequired(const QUrl &url,
1648 const QNetworkProxy &proxy,
1649 bool synchronous,
1650 QAuthenticator *authenticator,
1651 QNetworkProxy *lastProxyAuthentication)
1652{
1653 Q_Q(QNetworkAccessManager);
1654 QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(auth&: *authenticator);
1655 if (proxy != *lastProxyAuthentication && (!priv || !priv->hasFailed)) {
1656 QNetworkAuthenticationCredential cred = authenticationManager->fetchCachedProxyCredentials(proxy);
1657 if (!cred.isNull()) {
1658 authenticator->setUser(cred.user);
1659 authenticator->setPassword(cred.password);
1660 return;
1661 }
1662 }
1663
1664#if defined(Q_OS_MACOS)
1665 //now we try to get the username and password from keychain
1666 //if not successful signal will be emitted
1667 QString username;
1668 QString password;
1669 if (getProxyAuth(proxy.hostName(), url.scheme(), username, password)) {
1670 // only cache the system credentials if they are correct (or if they have changed)
1671 // to not run into an endless loop in case they are wrong
1672 QNetworkAuthenticationCredential cred = authenticationManager->fetchCachedProxyCredentials(proxy);
1673 if (!priv->hasFailed || cred.user != username || cred.password != password) {
1674 authenticator->setUser(username);
1675 authenticator->setPassword(password);
1676 authenticationManager->cacheProxyCredentials(proxy, authenticator);
1677 return;
1678 }
1679 }
1680#else
1681 Q_UNUSED(url);
1682#endif
1683
1684 // if we emit a signal here in synchronous mode, the user might spin
1685 // an event loop, which might recurse and lead to problems
1686 if (synchronous)
1687 return;
1688
1689 *lastProxyAuthentication = proxy;
1690 emit q->proxyAuthenticationRequired(proxy, authenticator);
1691 authenticationManager->cacheProxyCredentials(proxy, auth: authenticator);
1692}
1693
1694QList<QNetworkProxy> QNetworkAccessManagerPrivate::queryProxy(const QNetworkProxyQuery &query)
1695{
1696 QList<QNetworkProxy> proxies;
1697 if (proxyFactory) {
1698 proxies = proxyFactory->queryProxy(query);
1699 if (proxies.isEmpty()) {
1700 qWarning(msg: "QNetworkAccessManager: factory %p has returned an empty result set",
1701 proxyFactory);
1702 proxies << QNetworkProxy::NoProxy;
1703 }
1704 } else if (proxy.type() == QNetworkProxy::DefaultProxy) {
1705 // no proxy set, query the application
1706 return QNetworkProxyFactory::proxyForQuery(query);
1707 } else {
1708 proxies << proxy;
1709 }
1710
1711 return proxies;
1712}
1713#endif
1714
1715void QNetworkAccessManagerPrivate::clearAuthenticationCache(QNetworkAccessManager *manager)
1716{
1717 manager->d_func()->authenticationManager->clearCache();
1718}
1719
1720void QNetworkAccessManagerPrivate::clearConnectionCache(QNetworkAccessManager *manager)
1721{
1722 manager->d_func()->destroyThread();
1723}
1724
1725QNetworkAccessManagerPrivate::~QNetworkAccessManagerPrivate()
1726{
1727 destroyThread();
1728}
1729
1730QThread * QNetworkAccessManagerPrivate::createThread()
1731{
1732 if (!thread) {
1733 thread = new QThread;
1734 thread->setObjectName(QStringLiteral("QNetworkAccessManager thread"));
1735 thread->start();
1736 }
1737 Q_ASSERT(thread);
1738 return thread;
1739}
1740
1741void QNetworkAccessManagerPrivate::destroyThread()
1742{
1743 if (thread) {
1744 thread->quit();
1745 thread->wait(deadline: QDeadlineTimer(5000));
1746 if (thread->isFinished())
1747 delete thread;
1748 else
1749 QObject::connect(sender: thread, SIGNAL(finished()), receiver: thread, SLOT(deleteLater()));
1750 thread = nullptr;
1751 }
1752}
1753
1754
1755#if QT_CONFIG(http) || defined(Q_OS_WASM)
1756
1757QNetworkRequest QNetworkAccessManagerPrivate::prepareMultipart(const QNetworkRequest &request, QHttpMultiPart *multiPart)
1758{
1759 // copy the request, we probably need to add some headers
1760 QNetworkRequest newRequest(request);
1761 auto h = newRequest.headers();
1762
1763 // add Content-Type header if not there already
1764 if (!h.contains(name: QHttpHeaders::WellKnownHeader::ContentType)) {
1765 QByteArray contentType;
1766 contentType.reserve(asize: 34 + multiPart->d_func()->boundary.size());
1767 contentType += "multipart/";
1768 switch (multiPart->d_func()->contentType) {
1769 case QHttpMultiPart::RelatedType:
1770 contentType += "related";
1771 break;
1772 case QHttpMultiPart::FormDataType:
1773 contentType += "form-data";
1774 break;
1775 case QHttpMultiPart::AlternativeType:
1776 contentType += "alternative";
1777 break;
1778 default:
1779 contentType += "mixed";
1780 break;
1781 }
1782 // putting the boundary into quotes, recommended in RFC 2046 section 5.1.1
1783 contentType += "; boundary=\"" + multiPart->d_func()->boundary + '"';
1784 h.append(name: QHttpHeaders::WellKnownHeader::ContentType, value: contentType);
1785 }
1786
1787 // add MIME-Version header if not there already (we must include the header
1788 // if the message conforms to RFC 2045, see section 4 of that RFC)
1789 if (!h.contains(name: QHttpHeaders::WellKnownHeader::MIMEVersion))
1790 h.append(name: QHttpHeaders::WellKnownHeader::MIMEVersion, value: "1.0"_ba);
1791
1792 newRequest.setHeaders(std::move(h));
1793
1794 QIODevice *device = multiPart->d_func()->device;
1795 if (!device->isReadable()) {
1796 if (!device->isOpen()) {
1797 if (!device->open(mode: QIODevice::ReadOnly))
1798 qWarning(msg: "could not open device for reading");
1799 } else {
1800 qWarning(msg: "device is not readable");
1801 }
1802 }
1803
1804 return newRequest;
1805}
1806#endif // QT_CONFIG(http)
1807
1808/*!
1809 \internal
1810 Go through the instances so the factories will be created and
1811 register themselves to QNetworkAccessBackendFactoryData
1812*/
1813void QNetworkAccessManagerPrivate::ensureBackendPluginsLoaded()
1814{
1815 Q_CONSTINIT static QBasicMutex mutex;
1816 std::unique_lock locker(mutex);
1817 if (!qnabfLoader())
1818 return;
1819#if QT_CONFIG(library)
1820 qnabfLoader->update();
1821#endif
1822 int index = 0;
1823 while (qnabfLoader->instance(index))
1824 ++index;
1825}
1826
1827QT_END_NAMESPACE
1828
1829#include "moc_qnetworkaccessmanager.cpp"
1830

source code of qtbase/src/network/access/qnetworkaccessmanager.cpp