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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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