| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the QtNetwork module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qnetworkaccessmanager.h" |
| #include "qnetworkaccessmanager_p.h" |
| #include "qnetworkrequest.h" |
| #include "qnetworkreply.h" |
| #include "qnetworkreply_p.h" |
| #include "qnetworkcookie.h" |
| #include "qnetworkcookiejar.h" |
| #include "qabstractnetworkcache.h" |
| #include "qhstspolicy.h" |
| #include "qhsts_p.h" |
| |
| #if QT_CONFIG(settings) |
| #include "qhstsstore_p.h" |
| #endif // QT_CONFIG(settings) |
| |
| #include "QtNetwork/qnetworksession.h" |
| #include "QtNetwork/private/qsharednetworksession_p.h" |
| |
| #if QT_CONFIG(ftp) |
| #include "qnetworkaccessftpbackend_p.h" |
| #endif |
| #include "qnetworkaccessfilebackend_p.h" |
| #include "qnetworkaccessdebugpipebackend_p.h" |
| #include "qnetworkaccesscachebackend_p.h" |
| #include "qnetworkreplydataimpl_p.h" |
| #include "qnetworkreplyfileimpl_p.h" |
| |
| #include "QtCore/qbuffer.h" |
| #include "QtCore/qurl.h" |
| #include "QtCore/qvector.h" |
| #include "QtNetwork/private/qauthenticator_p.h" |
| #include "QtNetwork/qsslconfiguration.h" |
| #include "QtNetwork/qnetworkconfigmanager.h" |
| #include "QtNetwork/private/http2protocol_p.h" |
| |
| #if QT_CONFIG(http) |
| #include "qhttpmultipart.h" |
| #include "qhttpmultipart_p.h" |
| #include "qnetworkreplyhttpimpl_p.h" |
| #endif |
| |
| #include "qthread.h" |
| |
| #include <QHostInfo> |
| |
| #if defined(Q_OS_MACOS) |
| #include <CoreServices/CoreServices.h> |
| #include <SystemConfiguration/SystemConfiguration.h> |
| #include <Security/SecKeychain.h> |
| #endif |
| #ifdef Q_OS_WASM |
| #include "qnetworkreplywasmimpl_p.h" |
| #endif |
| |
| #include "qnetconmonitor_p.h" |
| |
| QT_BEGIN_NAMESPACE |
| |
| Q_GLOBAL_STATIC(QNetworkAccessFileBackendFactory, fileBackend) |
| #if QT_CONFIG(ftp) |
| Q_GLOBAL_STATIC(QNetworkAccessFtpBackendFactory, ftpBackend) |
| #endif // QT_CONFIG(ftp) |
| |
| #ifdef QT_BUILD_INTERNAL |
| Q_GLOBAL_STATIC(QNetworkAccessDebugPipeBackendFactory, debugpipeBackend) |
| #endif |
| |
| #if defined(Q_OS_MACX) |
| bool getProxyAuth(const QString& proxyHostname, const QString &scheme, QString& username, QString& password) |
| { |
| OSStatus err; |
| SecKeychainItemRef itemRef; |
| bool retValue = false; |
| SecProtocolType protocolType = kSecProtocolTypeAny; |
| if (scheme.compare(QLatin1String("ftp"),Qt::CaseInsensitive)==0) { |
| protocolType = kSecProtocolTypeFTPProxy; |
| } else if (scheme.compare(QLatin1String("http"),Qt::CaseInsensitive)==0 |
| || scheme.compare(QLatin1String("preconnect-http"),Qt::CaseInsensitive)==0) { |
| protocolType = kSecProtocolTypeHTTPProxy; |
| } else if (scheme.compare(QLatin1String("https"),Qt::CaseInsensitive)==0 |
| || scheme.compare(QLatin1String("preconnect-https"),Qt::CaseInsensitive)==0) { |
| protocolType = kSecProtocolTypeHTTPSProxy; |
| } |
| QByteArray proxyHostnameUtf8(proxyHostname.toUtf8()); |
| err = SecKeychainFindInternetPassword(NULL, |
| proxyHostnameUtf8.length(), proxyHostnameUtf8.constData(), |
| 0,NULL, |
| 0, NULL, |
| 0, NULL, |
| 0, |
| protocolType, |
| kSecAuthenticationTypeAny, |
| 0, NULL, |
| &itemRef); |
| if (err == noErr) { |
| |
| SecKeychainAttribute attr; |
| SecKeychainAttributeList attrList; |
| UInt32 length; |
| void *outData; |
| |
| attr.tag = kSecAccountItemAttr; |
| attr.length = 0; |
| attr.data = NULL; |
| |
| attrList.count = 1; |
| attrList.attr = &attr; |
| |
| if (SecKeychainItemCopyContent(itemRef, NULL, &attrList, &length, &outData) == noErr) { |
| username = QString::fromUtf8((const char*)attr.data, attr.length); |
| password = QString::fromUtf8((const char*)outData, length); |
| SecKeychainItemFreeContent(&attrList,outData); |
| retValue = true; |
| } |
| CFRelease(itemRef); |
| } |
| return retValue; |
| } |
| #endif |
| |
| |
| |
| static void ensureInitialized() |
| { |
| #if QT_CONFIG(ftp) |
| (void) ftpBackend(); |
| #endif |
| |
| #ifdef QT_BUILD_INTERNAL |
| (void) debugpipeBackend(); |
| #endif |
| |
| // leave this one last since it will query the special QAbstractFileEngines |
| (void) fileBackend(); |
| } |
| |
| /*! |
| \class QNetworkAccessManager |
| \brief The QNetworkAccessManager class allows the application to |
| send network requests and receive replies. |
| \since 4.4 |
| |
| \ingroup network |
| \inmodule QtNetwork |
| \reentrant |
| |
| The Network Access API is constructed around one QNetworkAccessManager |
| object, which holds the common configuration and settings for the requests |
| it sends. It contains the proxy and cache configuration, as well as the |
| signals related to such issues, and reply signals that can be used to |
| monitor the progress of a network operation. One QNetworkAccessManager |
| instance should be enough for the whole Qt application. Since |
| QNetworkAccessManager is based on QObject, it can only be used from the |
| thread it belongs to. |
| |
| Once a QNetworkAccessManager object has been created, the application can |
| use it to send requests over the network. A group of standard functions |
| are supplied that take a request and optional data, and each return a |
| QNetworkReply object. The returned object is used to obtain any data |
| returned in response to the corresponding request. |
| |
| A simple download off the network could be accomplished with: |
| \snippet code/src_network_access_qnetworkaccessmanager.cpp 0 |
| |
| QNetworkAccessManager has an asynchronous API. |
| When the \tt replyFinished slot above is called, the parameter it |
| takes is the QNetworkReply object containing the downloaded data |
| as well as meta-data (headers, etc.). |
| |
| \note After the request has finished, it is the responsibility of the user |
| to delete the QNetworkReply object at an appropriate time. Do not directly |
| delete it inside the slot connected to finished(). You can use the |
| deleteLater() function. |
| |
| \note QNetworkAccessManager queues the requests it receives. The number |
| of requests executed in parallel is dependent on the protocol. |
| Currently, for the HTTP protocol on desktop platforms, 6 requests are |
| executed in parallel for one host/port combination. |
| |
| A more involved example, assuming the manager is already existent, |
| can be: |
| \snippet code/src_network_access_qnetworkaccessmanager.cpp 1 |
| |
| \section1 Network and Roaming Support |
| |
| With the addition of the \l {Bearer Management} API to Qt 4.7 |
| QNetworkAccessManager gained the ability to manage network connections. |
| QNetworkAccessManager can start the network interface if the device is |
| offline and terminates the interface if the current process is the last |
| one to use the uplink. Note that some platforms utilize grace periods from |
| when the last application stops using a uplink until the system actually |
| terminates the connectivity link. Roaming is equally transparent. Any |
| queued/pending network requests are automatically transferred to the new |
| access point. |
| |
| Clients wanting to utilize this feature should not require any changes. In fact |
| it is likely that existing platform specific connection code can simply be |
| removed from the application. |
| |
| \note The network and roaming support in QNetworkAccessManager is conditional |
| upon the platform supporting connection management. The |
| \l QNetworkConfigurationManager::NetworkSessionRequired can be used to |
| detect whether QNetworkAccessManager utilizes this feature. |
| |
| \sa QNetworkRequest, QNetworkReply, QNetworkProxy |
| */ |
| |
| /*! |
| \enum QNetworkAccessManager::Operation |
| |
| Indicates the operation this reply is processing. |
| |
| \value HeadOperation retrieve headers operation (created |
| with head()) |
| |
| \value GetOperation retrieve headers and download contents |
| (created with get()) |
| |
| \value PutOperation upload contents operation (created |
| with put()) |
| |
| \value PostOperation send the contents of an HTML form for |
| processing via HTTP POST (created with post()) |
| |
| \value DeleteOperation delete contents operation (created with |
| deleteResource()) |
| |
| \value CustomOperation custom operation (created with |
| sendCustomRequest()) \since 4.7 |
| |
| \omitvalue UnknownOperation |
| |
| \sa QNetworkReply::operation() |
| */ |
| |
| /*! |
| \enum QNetworkAccessManager::NetworkAccessibility |
| |
| Indicates whether the network is accessible via this network access manager. |
| |
| \value UnknownAccessibility The network accessibility cannot be determined. |
| \value NotAccessible The network is not currently accessible, either because there |
| is currently no network coverage or network access has been |
| explicitly disabled by a call to setNetworkAccessible(). |
| \value Accessible The network is accessible. |
| |
| \sa networkAccessible |
| */ |
| |
| /*! |
| \property QNetworkAccessManager::networkAccessible |
| \brief whether the network is currently accessible via this network access manager. |
| |
| \since 4.7 |
| |
| If the network is \l {NotAccessible}{not accessible} the network access manager will not |
| process any new network requests, all such requests will fail with an error. Requests with |
| URLs with the file:// scheme will still be processed. |
| |
| By default the value of this property reflects the physical state of the device. Applications |
| may override it to disable all network requests via this network access manager by calling |
| |
| \snippet code/src_network_access_qnetworkaccessmanager.cpp 4 |
| |
| Network requests can be re-enabled again, and this property will resume to |
| reflect the actual device state by calling |
| |
| \snippet code/src_network_access_qnetworkaccessmanager.cpp 5 |
| |
| \note Calling setNetworkAccessible() does not change the network state. |
| */ |
| |
| /*! |
| \fn void QNetworkAccessManager::networkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility accessible) |
| |
| This signal is emitted when the value of the \l networkAccessible property changes. |
| \a accessible is the new network accessibility. |
| */ |
| |
| /*! |
| \fn void QNetworkAccessManager::networkSessionConnected() |
| |
| \since 4.7 |
| |
| \internal |
| |
| This signal is emitted when the status of the network session changes into a usable (Connected) |
| state. It is used to signal to QNetworkReplys to start or migrate their network operation once |
| the network session has been opened or finished roaming. |
| */ |
| |
| /*! |
| \fn void QNetworkAccessManager::proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator) |
| |
| This signal is emitted whenever a proxy requests authentication |
| and QNetworkAccessManager cannot find a valid, cached |
| credential. The slot connected to this signal should fill in the |
| credentials for the proxy \a proxy in the \a authenticator object. |
| |
| QNetworkAccessManager will cache the credentials internally. The |
| next time the proxy requests authentication, QNetworkAccessManager |
| will automatically send the same credential without emitting the |
| proxyAuthenticationRequired signal again. |
| |
| If the proxy rejects the credentials, QNetworkAccessManager will |
| emit the signal again. |
| |
| \sa proxy(), setProxy(), authenticationRequired() |
| */ |
| |
| /*! |
| \fn void QNetworkAccessManager::authenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator) |
| |
| This signal is emitted whenever a final server requests |
| authentication before it delivers the requested contents. The slot |
| connected to this signal should fill the credentials for the |
| contents (which can be determined by inspecting the \a reply |
| object) in the \a authenticator object. |
| |
| QNetworkAccessManager will cache the credentials internally and |
| will send the same values if the server requires authentication |
| again, without emitting the authenticationRequired() signal. If it |
| rejects the credentials, this signal will be emitted again. |
| |
| \note To have the request not send credentials you must not call |
| setUser() or setPassword() on the \a authenticator object. This |
| will result in the \l finished() signal being emitted with a |
| \l QNetworkReply with error \l {QNetworkReply::} {AuthenticationRequiredError}. |
| |
| \note It is not possible to use a QueuedConnection to connect to |
| this signal, as the connection will fail if the authenticator has |
| not been filled in with new information when the signal returns. |
| |
| \sa proxyAuthenticationRequired(), QAuthenticator::setUser(), QAuthenticator::setPassword() |
| */ |
| |
| /*! |
| \fn void QNetworkAccessManager::finished(QNetworkReply *reply) |
| |
| This signal is emitted whenever a pending network reply is |
| finished. The \a reply parameter will contain a pointer to the |
| reply that has just finished. This signal is emitted in tandem |
| with the QNetworkReply::finished() signal. |
| |
| See QNetworkReply::finished() for information on the status that |
| the object will be in. |
| |
| \note Do not delete the \a reply object in the slot connected to this |
| signal. Use deleteLater(). |
| |
| \sa QNetworkReply::finished(), QNetworkReply::error() |
| */ |
| |
| /*! |
| \fn void QNetworkAccessManager::encrypted(QNetworkReply *reply) |
| \since 5.1 |
| |
| This signal is emitted when an SSL/TLS session has successfully |
| completed the initial handshake. At this point, no user data |
| has been transmitted. The signal can be used to perform |
| additional checks on the certificate chain, for example to |
| notify users when the certificate for a website has changed. The |
| \a reply parameter specifies which network reply is responsible. |
| If the reply does not match the expected criteria then it should |
| be aborted by calling QNetworkReply::abort() by a slot connected |
| to this signal. The SSL configuration in use can be inspected |
| using the QNetworkReply::sslConfiguration() method. |
| |
| Internally, QNetworkAccessManager may open multiple connections |
| to a server, in order to allow it process requests in parallel. |
| These connections may be reused, which means that the encrypted() |
| signal would not be emitted. This means that you are only |
| guaranteed to receive this signal for the first connection to a |
| site in the lifespan of the QNetworkAccessManager. |
| |
| \sa QSslSocket::encrypted() |
| \sa QNetworkReply::encrypted() |
| */ |
| |
| /*! |
| \fn void QNetworkAccessManager::sslErrors(QNetworkReply *reply, const QList<QSslError> &errors) |
| |
| This signal is emitted if the SSL/TLS session encountered errors |
| during the set up, including certificate verification errors. The |
| \a errors parameter contains the list of errors and \a reply is |
| the QNetworkReply that is encountering these errors. |
| |
| To indicate that the errors are not fatal and that the connection |
| should proceed, the QNetworkReply::ignoreSslErrors() function should be called |
| from the slot connected to this signal. If it is not called, the |
| SSL session will be torn down before any data is exchanged |
| (including the URL). |
| |
| This signal can be used to display an error message to the user |
| indicating that security may be compromised and display the |
| SSL settings (see sslConfiguration() to obtain it). If the user |
| decides to proceed after analyzing the remote certificate, the |
| slot should call ignoreSslErrors(). |
| |
| \sa QSslSocket::sslErrors(), QNetworkReply::sslErrors(), |
| QNetworkReply::sslConfiguration(), QNetworkReply::ignoreSslErrors() |
| */ |
| |
| /*! |
| \fn void QNetworkAccessManager::preSharedKeyAuthenticationRequired(QNetworkReply *reply, QSslPreSharedKeyAuthenticator *authenticator) |
| \since 5.5 |
| |
| This signal is emitted if the SSL/TLS handshake negotiates a PSK |
| ciphersuite, and therefore a PSK authentication is then required. |
| The \a reply object is the QNetworkReply that is negotiating |
| such ciphersuites. |
| |
| When using PSK, the client must send to the server a valid identity and a |
| valid pre shared key, in order for the SSL handshake to continue. |
| Applications can provide this information in a slot connected to this |
| signal, by filling in the passed \a authenticator object according to their |
| needs. |
| |
| \note Ignoring this signal, or failing to provide the required credentials, |
| will cause the handshake to fail, and therefore the connection to be aborted. |
| |
| \note The \a authenticator object is owned by the reply and must not be |
| deleted by the application. |
| |
| \sa QSslPreSharedKeyAuthenticator |
| */ |
| |
| /*! |
| Constructs a QNetworkAccessManager object that is the center of |
| the Network Access API and sets \a parent as the parent object. |
| */ |
| QNetworkAccessManager::QNetworkAccessManager(QObject *parent) |
| : QObject(*new QNetworkAccessManagerPrivate, parent) |
| { |
| ensureInitialized(); |
| |
| qRegisterMetaType<QNetworkReply::NetworkError>(); |
| #ifndef QT_NO_NETWORKPROXY |
| qRegisterMetaType<QNetworkProxy>(); |
| #endif |
| #ifndef QT_NO_SSL |
| qRegisterMetaType<QList<QSslError> >(); |
| qRegisterMetaType<QSslConfiguration>(); |
| qRegisterMetaType<QSslPreSharedKeyAuthenticator *>(); |
| #endif |
| qRegisterMetaType<QList<QPair<QByteArray,QByteArray> > >(); |
| #if QT_CONFIG(http) |
| qRegisterMetaType<QHttpNetworkRequest>(); |
| #endif |
| qRegisterMetaType<QNetworkReply::NetworkError>(); |
| qRegisterMetaType<QSharedPointer<char> >(); |
| |
| Q_D(QNetworkAccessManager); |
| |
| if (QNetworkStatusMonitor::isEnabled()) { |
| connect(&d->statusMonitor, SIGNAL(onlineStateChanged(bool)), |
| SLOT(_q_onlineStateChanged(bool))); |
| #ifdef QT_NO_BEARERMANAGEMENT |
| d->networkAccessible = d->statusMonitor.isNetworkAccessible(); |
| #else |
| d->networkAccessible = d->statusMonitor.isNetworkAccessible() ? Accessible : NotAccessible; |
| } else { |
| // if a session is required, we track online state through |
| // the QNetworkSession's signals if a request is already made. |
| // we need to track current accessibility state by default |
| // |
| connect(&d->networkConfigurationManager, SIGNAL(onlineStateChanged(bool)), |
| SLOT(_q_onlineStateChanged(bool))); |
| connect(&d->networkConfigurationManager, SIGNAL(configurationChanged(QNetworkConfiguration)), |
| SLOT(_q_configurationChanged(QNetworkConfiguration))); |
| #endif // QT_NO_BEARERMANAGEMENT |
| } |
| } |
| |
| /*! |
| Destroys the QNetworkAccessManager object and frees up any |
| resources. Note that QNetworkReply objects that are returned from |
| this class have this object set as their parents, which means that |
| they will be deleted along with it if you don't call |
| QObject::setParent() on them. |
| */ |
| QNetworkAccessManager::~QNetworkAccessManager() |
| { |
| #ifndef QT_NO_NETWORKPROXY |
| delete d_func()->proxyFactory; |
| #endif |
| |
| // Delete the QNetworkReply children first. |
| // Else a QAbstractNetworkCache might get deleted in ~QObject |
| // before a QNetworkReply that accesses the QAbstractNetworkCache |
| // object in its destructor. |
| qDeleteAll(findChildren<QNetworkReply *>()); |
| // The other children will be deleted in this ~QObject |
| // FIXME instead of this "hack" make the QNetworkReplyImpl |
| // properly watch the cache deletion, e.g. via a QWeakPointer. |
| } |
| |
| #ifndef QT_NO_NETWORKPROXY |
| /*! |
| Returns the QNetworkProxy that the requests sent using this |
| QNetworkAccessManager object will use. The default value for the |
| proxy is QNetworkProxy::DefaultProxy. |
| |
| \sa setProxy(), setProxyFactory(), proxyAuthenticationRequired() |
| */ |
| QNetworkProxy QNetworkAccessManager::proxy() const |
| { |
| return d_func()->proxy; |
| } |
| |
| /*! |
| Sets the proxy to be used in future requests to be \a proxy. This |
| does not affect requests that have already been sent. The |
| proxyAuthenticationRequired() signal will be emitted if the proxy |
| requests authentication. |
| |
| A proxy set with this function will be used for all requests |
| issued by QNetworkAccessManager. In some cases, it might be |
| necessary to select different proxies depending on the type of |
| request being sent or the destination host. If that's the case, |
| you should consider using setProxyFactory(). |
| |
| \sa proxy(), proxyAuthenticationRequired() |
| */ |
| void QNetworkAccessManager::setProxy(const QNetworkProxy &proxy) |
| { |
| Q_D(QNetworkAccessManager); |
| delete d->proxyFactory; |
| d->proxy = proxy; |
| d->proxyFactory = nullptr; |
| } |
| |
| /*! |
| \fn QNetworkProxyFactory *QNetworkAccessManager::proxyFactory() const |
| \since 4.5 |
| |
| Returns the proxy factory that this QNetworkAccessManager object |
| is using to determine the proxies to be used for requests. |
| |
| Note that the pointer returned by this function is managed by |
| QNetworkAccessManager and could be deleted at any time. |
| |
| \sa setProxyFactory(), proxy() |
| */ |
| QNetworkProxyFactory *QNetworkAccessManager::proxyFactory() const |
| { |
| return d_func()->proxyFactory; |
| } |
| |
| /*! |
| \since 4.5 |
| |
| Sets the proxy factory for this class to be \a factory. A proxy |
| factory is used to determine a more specific list of proxies to be |
| used for a given request, instead of trying to use the same proxy |
| value for all requests. |
| |
| All queries sent by QNetworkAccessManager will have type |
| QNetworkProxyQuery::UrlRequest. |
| |
| For example, a proxy factory could apply the following rules: |
| \list |
| \li if the target address is in the local network (for example, |
| if the hostname contains no dots or if it's an IP address in |
| the organization's range), return QNetworkProxy::NoProxy |
| \li if the request is FTP, return an FTP proxy |
| \li if the request is HTTP or HTTPS, then return an HTTP proxy |
| \li otherwise, return a SOCKSv5 proxy server |
| \endlist |
| |
| The lifetime of the object \a factory will be managed by |
| QNetworkAccessManager. It will delete the object when necessary. |
| |
| \note If a specific proxy is set with setProxy(), the factory will not |
| be used. |
| |
| \sa proxyFactory(), setProxy(), QNetworkProxyQuery |
| */ |
| void QNetworkAccessManager::setProxyFactory(QNetworkProxyFactory *factory) |
| { |
| Q_D(QNetworkAccessManager); |
| delete d->proxyFactory; |
| d->proxyFactory = factory; |
| d->proxy = QNetworkProxy(); |
| } |
| #endif |
| |
| /*! |
| \since 4.5 |
| |
| Returns the cache that is used to store data obtained from the network. |
| |
| \sa setCache() |
| */ |
| QAbstractNetworkCache *QNetworkAccessManager::cache() const |
| { |
| Q_D(const QNetworkAccessManager); |
| return d->networkCache; |
| } |
| |
| /*! |
| \since 4.5 |
| |
| Sets the manager's network cache to be the \a cache specified. The cache |
| is used for all requests dispatched by the manager. |
| |
| Use this function to set the network cache object to a class that implements |
| additional features, like saving the cookies to permanent storage. |
| |
| \note QNetworkAccessManager takes ownership of the \a cache object. |
| |
| QNetworkAccessManager by default does not have a set cache. |
| Qt provides a simple disk cache, QNetworkDiskCache, which can be used. |
| |
| \sa cache(), QNetworkRequest::CacheLoadControl |
| */ |
| void QNetworkAccessManager::setCache(QAbstractNetworkCache *cache) |
| { |
| Q_D(QNetworkAccessManager); |
| if (d->networkCache != cache) { |
| delete d->networkCache; |
| d->networkCache = cache; |
| if (d->networkCache) |
| d->networkCache->setParent(this); |
| } |
| } |
| |
| /*! |
| Returns the QNetworkCookieJar that is used to store cookies |
| obtained from the network as well as cookies that are about to be |
| sent. |
| |
| \sa setCookieJar() |
| */ |
| QNetworkCookieJar *QNetworkAccessManager::cookieJar() const |
| { |
| Q_D(const QNetworkAccessManager); |
| if (!d->cookieJar) |
| d->createCookieJar(); |
| return d->cookieJar; |
| } |
| |
| /*! |
| Sets the manager's cookie jar to be the \a cookieJar specified. |
| The cookie jar is used by all requests dispatched by the manager. |
| |
| Use this function to set the cookie jar object to a class that |
| implements additional features, like saving the cookies to permanent |
| storage. |
| |
| \note QNetworkAccessManager takes ownership of the \a cookieJar object. |
| |
| If \a cookieJar is in the same thread as this QNetworkAccessManager, |
| it will set the parent of the \a cookieJar |
| so that the cookie jar is deleted when this |
| object is deleted as well. If you want to share cookie jars |
| between different QNetworkAccessManager objects, you may want to |
| set the cookie jar's parent to 0 after calling this function. |
| |
| QNetworkAccessManager by default does not implement any cookie |
| policy of its own: it accepts all cookies sent by the server, as |
| long as they are well formed and meet the minimum security |
| requirements (cookie domain matches the request's and cookie path |
| matches the request's). In order to implement your own security |
| policy, override the QNetworkCookieJar::cookiesForUrl() and |
| QNetworkCookieJar::setCookiesFromUrl() virtual functions. Those |
| functions are called by QNetworkAccessManager when it detects a |
| new cookie. |
| |
| \sa cookieJar(), QNetworkCookieJar::cookiesForUrl(), QNetworkCookieJar::setCookiesFromUrl() |
| */ |
| void QNetworkAccessManager::setCookieJar(QNetworkCookieJar *cookieJar) |
| { |
| Q_D(QNetworkAccessManager); |
| d->cookieJarCreated = true; |
| if (d->cookieJar != cookieJar) { |
| if (d->cookieJar && d->cookieJar->parent() == this) |
| delete d->cookieJar; |
| d->cookieJar = cookieJar; |
| if (cookieJar && thread() == cookieJar->thread()) |
| d->cookieJar->setParent(this); |
| } |
| } |
| |
| /*! |
| \since 5.9 |
| |
| If \a enabled is \c true, QNetworkAccessManager follows the HTTP Strict Transport |
| Security policy (HSTS, RFC6797). When processing a request, QNetworkAccessManager |
| automatically replaces the "http" scheme with "https" and uses a secure transport |
| for HSTS hosts. If it's set explicitly, port 80 is replaced by port 443. |
| |
| When HSTS is enabled, for each HTTP response containing HSTS header and |
| received over a secure transport, QNetworkAccessManager will update its HSTS |
| cache, either remembering a host with a valid policy or removing a host with |
| an expired or disabled HSTS policy. |
| |
| \sa isStrictTransportSecurityEnabled() |
| */ |
| void QNetworkAccessManager::setStrictTransportSecurityEnabled(bool enabled) |
| { |
| Q_D(QNetworkAccessManager); |
| d->stsEnabled = enabled; |
| } |
| |
| /*! |
| \since 5.9 |
| |
| Returns true if HTTP Strict Transport Security (HSTS) was enabled. By default |
| HSTS is disabled. |
| |
| \sa setStrictTransportSecurityEnabled() |
| */ |
| bool QNetworkAccessManager::isStrictTransportSecurityEnabled() const |
| { |
| Q_D(const QNetworkAccessManager); |
| return d->stsEnabled; |
| } |
| |
| /*! |
| \since 5.10 |
| |
| If \a enabled is \c true, the internal HSTS cache will use a persistent store |
| to read and write HSTS policies. \a storeDir defines where this store will be |
| located. The default location is defined by QStandardPaths::CacheLocation. |
| If there is no writable QStandartPaths::CacheLocation and \a storeDir is an |
| empty string, the store will be located in the program's working directory. |
| |
| \note If HSTS cache already contains HSTS policies by the time persistent |
| store is enabled, these policies will be preserved in the store. In case both |
| cache and store contain the same known hosts, policies from cache are considered |
| to be more up-to-date (and thus will overwrite the previous values in the store). |
| If this behavior is undesired, enable HSTS store before enabling Strict Tranport |
| Security. By default, the persistent store of HSTS policies is disabled. |
| |
| \sa isStrictTransportSecurityStoreEnabled(), setStrictTransportSecurityEnabled(), |
| QStandardPaths::standardLocations() |
| */ |
| |
| void QNetworkAccessManager::enableStrictTransportSecurityStore(bool enabled, const QString &storeDir) |
| { |
| #if QT_CONFIG(settings) |
| Q_D(QNetworkAccessManager); |
| d->stsStore.reset(enabled ? new QHstsStore(storeDir) : nullptr); |
| d->stsCache.setStore(d->stsStore.data()); |
| #else |
| Q_UNUSED(enabled) Q_UNUSED(storeDir) |
| qWarning("HSTS permanent store requires the feature 'settings' enabled"); |
| #endif // QT_CONFIG(settings) |
| } |
| |
| /*! |
| \since 5.10 |
| |
| Returns true if HSTS cache uses a permanent store to load and store HSTS |
| policies. |
| |
| \sa enableStrictTransportSecurityStore() |
| */ |
| |
| bool QNetworkAccessManager::isStrictTransportSecurityStoreEnabled() const |
| { |
| #if QT_CONFIG(settings) |
| Q_D(const QNetworkAccessManager); |
| return bool(d->stsStore.data()); |
| #else |
| return false; |
| #endif // QT_CONFIG(settings) |
| } |
| |
| /*! |
| \since 5.9 |
| |
| Adds HTTP Strict Transport Security policies into HSTS cache. |
| \a knownHosts contains the known hosts that have QHstsPolicy |
| information. |
| |
| \note An expired policy will remove a known host from the cache, if previously |
| present. |
| |
| \note While processing HTTP responses, QNetworkAccessManager can also update |
| the HSTS cache, removing or updating exitsting policies or introducing new |
| \a knownHosts. The current implementation thus is server-driven, client code |
| can provide QNetworkAccessManager with previously known or discovered |
| policies, but this information can be overridden by "Strict-Transport-Security" |
| response headers. |
| |
| \sa addStrictTransportSecurityHosts(), enableStrictTransportSecurityStore(), QHstsPolicy |
| */ |
| |
| void QNetworkAccessManager::addStrictTransportSecurityHosts(const QVector<QHstsPolicy> &knownHosts) |
| { |
| Q_D(QNetworkAccessManager); |
| d->stsCache.updateFromPolicies(knownHosts); |
| } |
| |
| /*! |
| \since 5.9 |
| |
| Returns the list of HTTP Strict Transport Security policies. This list can |
| differ from what was initially set via addStrictTransportSecurityHosts() if |
| HSTS cache was updated from a "Strict-Transport-Security" response header. |
| |
| \sa addStrictTransportSecurityHosts(), QHstsPolicy |
| */ |
| QVector<QHstsPolicy> QNetworkAccessManager::strictTransportSecurityHosts() const |
| { |
| Q_D(const QNetworkAccessManager); |
| return d->stsCache.policies(); |
| } |
| |
| /*! |
| Posts a request to obtain the network headers for \a request |
| and returns a new QNetworkReply object which will contain such headers. |
| |
| The function is named after the HTTP request associated (HEAD). |
| */ |
| QNetworkReply *QNetworkAccessManager::head(const QNetworkRequest &request) |
| { |
| return d_func()->postProcess(createRequest(QNetworkAccessManager::HeadOperation, request)); |
| } |
| |
| /*! |
| Posts a request to obtain the contents of the target \a request |
| and returns a new QNetworkReply object opened for reading which emits the |
| \l{QIODevice::readyRead()}{readyRead()} signal whenever new data |
| arrives. |
| |
| The contents as well as associated headers will be downloaded. |
| |
| \sa post(), put(), deleteResource(), sendCustomRequest() |
| */ |
| QNetworkReply *QNetworkAccessManager::get(const QNetworkRequest &request) |
| { |
| return d_func()->postProcess(createRequest(QNetworkAccessManager::GetOperation, request)); |
| } |
| |
| /*! |
| Sends an HTTP POST request to the destination specified by \a request |
| and returns a new QNetworkReply object opened for reading that will |
| contain the reply sent by the server. The contents of the \a data |
| device will be uploaded to the server. |
| |
| \a data must be open for reading and must remain valid until the |
| finished() signal is emitted for this reply. |
| |
| \note Sending a POST request on protocols other than HTTP and |
| HTTPS is undefined and will probably fail. |
| |
| \sa get(), put(), deleteResource(), sendCustomRequest() |
| */ |
| QNetworkReply *QNetworkAccessManager::post(const QNetworkRequest &request, QIODevice *data) |
| { |
| return d_func()->postProcess(createRequest(QNetworkAccessManager::PostOperation, request, data)); |
| } |
| |
| /*! |
| \overload |
| |
| Sends the contents of the \a data byte array to the destination |
| specified by \a request. |
| */ |
| QNetworkReply *QNetworkAccessManager::post(const QNetworkRequest &request, const QByteArray &data) |
| { |
| QBuffer *buffer = new QBuffer; |
| buffer->setData(data); |
| buffer->open(QIODevice::ReadOnly); |
| |
| QNetworkReply *reply = post(request, buffer); |
| buffer->setParent(reply); |
| return reply; |
| } |
| |
| #if QT_CONFIG(http) |
| /*! |
| \since 4.8 |
| |
| \overload |
| |
| Sends the contents of the \a multiPart message to the destination |
| specified by \a request. |
| |
| This can be used for sending MIME multipart messages over HTTP. |
| |
| \sa QHttpMultiPart, QHttpPart, put() |
| */ |
| QNetworkReply *QNetworkAccessManager::post(const QNetworkRequest &request, QHttpMultiPart *multiPart) |
| { |
| QNetworkRequest newRequest = d_func()->prepareMultipart(request, multiPart); |
| QIODevice *device = multiPart->d_func()->device; |
| QNetworkReply *reply = post(newRequest, device); |
| return reply; |
| } |
| |
| /*! |
| \since 4.8 |
| |
| \overload |
| |
| Sends the contents of the \a multiPart message to the destination |
| specified by \a request. |
| |
| This can be used for sending MIME multipart messages over HTTP. |
| |
| \sa QHttpMultiPart, QHttpPart, post() |
| */ |
| QNetworkReply *QNetworkAccessManager::put(const QNetworkRequest &request, QHttpMultiPart *multiPart) |
| { |
| QNetworkRequest newRequest = d_func()->prepareMultipart(request, multiPart); |
| QIODevice *device = multiPart->d_func()->device; |
| QNetworkReply *reply = put(newRequest, device); |
| return reply; |
| } |
| #endif // QT_CONFIG(http) |
| |
| /*! |
| Uploads the contents of \a data to the destination \a request and |
| returns a new QNetworkReply object that will be open for reply. |
| |
| \a data must be opened for reading when this function is called |
| and must remain valid until the finished() signal is emitted for |
| this reply. |
| |
| Whether anything will be available for reading from the returned |
| object is protocol dependent. For HTTP, the server may send a |
| small HTML page indicating the upload was successful (or not). |
| Other protocols will probably have content in their replies. |
| |
| \note For HTTP, this request will send a PUT request, which most servers |
| do not allow. Form upload mechanisms, including that of uploading |
| files through HTML forms, use the POST mechanism. |
| |
| \sa get(), post(), deleteResource(), sendCustomRequest() |
| */ |
| QNetworkReply *QNetworkAccessManager::put(const QNetworkRequest &request, QIODevice *data) |
| { |
| return d_func()->postProcess(createRequest(QNetworkAccessManager::PutOperation, request, data)); |
| } |
| |
| /*! |
| \overload |
| |
| Sends the contents of the \a data byte array to the destination |
| specified by \a request. |
| */ |
| QNetworkReply *QNetworkAccessManager::put(const QNetworkRequest &request, const QByteArray &data) |
| { |
| QBuffer *buffer = new QBuffer; |
| buffer->setData(data); |
| buffer->open(QIODevice::ReadOnly); |
| |
| QNetworkReply *reply = put(request, buffer); |
| buffer->setParent(reply); |
| return reply; |
| } |
| |
| /*! |
| \since 4.6 |
| |
| Sends a request to delete the resource identified by the URL of \a request. |
| |
| \note This feature is currently available for HTTP only, performing an |
| HTTP DELETE request. |
| |
| \sa get(), post(), put(), sendCustomRequest() |
| */ |
| QNetworkReply *QNetworkAccessManager::deleteResource(const QNetworkRequest &request) |
| { |
| return d_func()->postProcess(createRequest(QNetworkAccessManager::DeleteOperation, request)); |
| } |
| |
| #ifndef QT_NO_BEARERMANAGEMENT |
| |
| /*! |
| \since 4.7 |
| |
| Sets the network configuration that will be used when creating the |
| \l {QNetworkSession}{network session} to \a config. |
| |
| The network configuration is used to create and open a network session before any request that |
| requires network access is process. If no network configuration is explicitly set via this |
| function the network configuration returned by |
| QNetworkConfigurationManager::defaultConfiguration() will be used. |
| |
| To restore the default network configuration set the network configuration to the value |
| returned from QNetworkConfigurationManager::defaultConfiguration(). |
| |
| Setting a network configuration means that the QNetworkAccessManager instance will only |
| be using the specified one. In particular, if the default network configuration changes |
| (upon e.g. Wifi being available), this new configuration needs to be enabled |
| manually if desired. |
| |
| \snippet code/src_network_access_qnetworkaccessmanager.cpp 2 |
| |
| If an invalid network configuration is set, a network session will not be created. In this |
| case network requests will be processed regardless, but may fail. For example: |
| |
| \snippet code/src_network_access_qnetworkaccessmanager.cpp 3 |
| |
| \sa configuration(), QNetworkSession |
| */ |
| void QNetworkAccessManager::setConfiguration(const QNetworkConfiguration &config) |
| { |
| Q_D(QNetworkAccessManager); |
| |
| d->networkConfiguration = config; |
| d->customNetworkConfiguration = true; |
| d->createSession(config); |
| } |
| |
| /*! |
| \since 4.7 |
| |
| Returns the network configuration that will be used to create the |
| \l {QNetworkSession}{network session} which will be used when processing network requests. |
| |
| \sa setConfiguration(), activeConfiguration() |
| */ |
| QNetworkConfiguration QNetworkAccessManager::configuration() const |
| { |
| Q_D(const QNetworkAccessManager); |
| |
| QSharedPointer<QNetworkSession> session(d->getNetworkSession()); |
| if (session && !d->statusMonitor.isEnabled()) { |
| return session->configuration(); |
| } else { |
| return d->networkConfigurationManager.defaultConfiguration(); |
| } |
| } |
| |
| /*! |
| \since 4.7 |
| |
| Returns the current active network configuration. |
| |
| If the network configuration returned by configuration() is of type |
| QNetworkConfiguration::ServiceNetwork this function will return the current active child |
| network configuration of that configuration. Otherwise returns the same network configuration |
| as configuration(). |
| |
| Use this function to return the actual network configuration currently in use by the network |
| session. |
| |
| \sa configuration() |
| */ |
| QNetworkConfiguration QNetworkAccessManager::activeConfiguration() const |
| { |
| Q_D(const QNetworkAccessManager); |
| |
| QSharedPointer<QNetworkSession> networkSession(d->getNetworkSession()); |
| if (networkSession && !d->statusMonitor.isEnabled()) { |
| return d->networkConfigurationManager.configurationFromIdentifier( |
| networkSession->sessionProperty(QLatin1String("ActiveConfiguration")).toString()); |
| } else { |
| return d->networkConfigurationManager.defaultConfiguration(); |
| } |
| } |
| |
| /*! |
| \since 4.7 |
| |
| Overrides the reported network accessibility. If \a accessible is NotAccessible the reported |
| network accessiblity will always be NotAccessible. Otherwise the reported network |
| accessibility will reflect the actual device state. |
| */ |
| void QNetworkAccessManager::setNetworkAccessible(QNetworkAccessManager::NetworkAccessibility accessible) |
| { |
| Q_D(QNetworkAccessManager); |
| |
| d->defaultAccessControl = accessible == NotAccessible ? false : true; |
| |
| if (d->networkAccessible != accessible) { |
| NetworkAccessibility previous = networkAccessible(); |
| d->networkAccessible = accessible; |
| NetworkAccessibility current = networkAccessible(); |
| if (previous != current) |
| emit networkAccessibleChanged(current); |
| } |
| } |
| |
| /*! |
| \since 4.7 |
| |
| Returns the current network accessibility. |
| */ |
| QNetworkAccessManager::NetworkAccessibility QNetworkAccessManager::networkAccessible() const |
| { |
| Q_D(const QNetworkAccessManager); |
| |
| if (d->statusMonitor.isEnabled()) { |
| if (!d->statusMonitor.isMonitoring()) |
| d->statusMonitor.start(); |
| return d->networkAccessible; |
| } |
| |
| if (d->customNetworkConfiguration && d->networkConfiguration.state().testFlag(QNetworkConfiguration::Undefined)) |
| return UnknownAccessibility; |
| |
| if (d->networkSessionRequired) { |
| QSharedPointer<QNetworkSession> networkSession(d->getNetworkSession()); |
| if (networkSession) { |
| // d->online holds online/offline state of this network session. |
| if (d->online) |
| return d->networkAccessible; |
| else |
| return NotAccessible; |
| } else { |
| if (d->defaultAccessControl) { |
| if (d->online) |
| return d->networkAccessible; |
| else |
| return NotAccessible; |
| } |
| return (d->networkAccessible); |
| } |
| } else { |
| if (d->online) |
| return d->networkAccessible; |
| else |
| return NotAccessible; |
| } |
| } |
| |
| /*! |
| \internal |
| |
| Returns the network session currently in use. |
| This can be changed at any time, ownership remains with the QNetworkAccessManager |
| */ |
| const QWeakPointer<const QNetworkSession> QNetworkAccessManagerPrivate::getNetworkSession(const QNetworkAccessManager *q) |
| { |
| return q->d_func()->networkSessionWeakRef; |
| } |
| |
| QSharedPointer<QNetworkSession> QNetworkAccessManagerPrivate::getNetworkSession() const |
| { |
| if (networkSessionStrongRef) |
| return networkSessionStrongRef; |
| return networkSessionWeakRef.toStrongRef(); |
| } |
| |
| #endif // QT_NO_BEARERMANAGEMENT |
| |
| #ifndef QT_NO_SSL |
| /*! |
| \since 5.2 |
| |
| Initiates a connection to the host given by \a hostName at port \a port, using |
| \a sslConfiguration. This function is useful to complete the TCP and SSL handshake |
| to a host before the HTTPS request is made, resulting in a lower network latency. |
| |
| \note Preconnecting a SPDY connection can be done by calling setAllowedNextProtocols() |
| on \a sslConfiguration with QSslConfiguration::NextProtocolSpdy3_0 contained in |
| the list of allowed protocols. When using SPDY, one single connection per host is |
| enough, i.e. calling this method multiple times per host will not result in faster |
| network transactions. |
| |
| \note This function has no possibility to report errors. |
| |
| \sa connectToHost(), get(), post(), put(), deleteResource() |
| */ |
| |
| void QNetworkAccessManager::connectToHostEncrypted(const QString &hostName, quint16 port, |
| const QSslConfiguration &sslConfiguration) |
| { |
| connectToHostEncrypted(hostName, port, sslConfiguration, QString()); |
| } |
| |
| /*! |
| \since 5.13 |
| \overload |
| |
| Initiates a connection to the host given by \a hostName at port \a port, using |
| \a sslConfiguration with \a peerName set to be the hostName used for certificate |
| validation. This function is useful to complete the TCP and SSL handshake |
| to a host before the HTTPS request is made, resulting in a lower network latency. |
| |
| \note Preconnecting a SPDY connection can be done by calling setAllowedNextProtocols() |
| on \a sslConfiguration with QSslConfiguration::NextProtocolSpdy3_0 contained in |
| the list of allowed protocols. When using SPDY, one single connection per host is |
| enough, i.e. calling this method multiple times per host will not result in faster |
| network transactions. |
| |
| \note This function has no possibility to report errors. |
| |
| \sa connectToHost(), get(), post(), put(), deleteResource() |
| */ |
| |
| void QNetworkAccessManager::connectToHostEncrypted(const QString &hostName, quint16 port, |
| const QSslConfiguration &sslConfiguration, |
| const QString &peerName) |
| { |
| QUrl url; |
| url.setHost(hostName); |
| url.setPort(port); |
| url.setScheme(QLatin1String("preconnect-https")); |
| QNetworkRequest request(url); |
| if (sslConfiguration != QSslConfiguration::defaultConfiguration()) |
| request.setSslConfiguration(sslConfiguration); |
| |
| // There is no way to enable SPDY/HTTP2 via a request, so we need to check |
| // the ssl configuration whether SPDY/HTTP2 is allowed here. |
| if (sslConfiguration.allowedNextProtocols().contains(QSslConfiguration::ALPNProtocolHTTP2)) |
| request.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, true); |
| else if (sslConfiguration.allowedNextProtocols().contains(QSslConfiguration::NextProtocolSpdy3_0)) |
| request.setAttribute(QNetworkRequest::SpdyAllowedAttribute, true); |
| |
| request.setPeerVerifyName(peerName); |
| get(request); |
| } |
| #endif |
| |
| /*! |
| \since 5.2 |
| |
| Initiates a connection to the host given by \a hostName at port \a port. |
| This function is useful to complete the TCP handshake |
| to a host before the HTTP request is made, resulting in a lower network latency. |
| |
| \note This function has no possibility to report errors. |
| |
| \sa connectToHostEncrypted(), get(), post(), put(), deleteResource() |
| */ |
| void QNetworkAccessManager::connectToHost(const QString &hostName, quint16 port) |
| { |
| QUrl url; |
| url.setHost(hostName); |
| url.setPort(port); |
| url.setScheme(QLatin1String("preconnect-http")); |
| QNetworkRequest request(url); |
| get(request); |
| } |
| |
| /*! |
| \since 5.9 |
| |
| Sets the manager's redirect policy to be the \a policy specified. This policy |
| will affect all subsequent requests created by the manager. |
| |
| Use this function to enable or disable HTTP redirects on the manager's level. |
| |
| \note When creating a request QNetworkRequest::RedirectAttributePolicy has |
| the highest priority, next by priority is QNetworkRequest::FollowRedirectsAttribute. |
| Finally, the manager's policy has the lowest priority. |
| |
| For backwards compatibility the default value is QNetworkRequest::ManualRedirectPolicy. |
| This may change in the future and some type of auto-redirect policy will become |
| the default; clients relying on manual redirect handling are encouraged to set |
| this policy explicitly in their code. |
| |
| \sa redirectPolicy(), QNetworkRequest::RedirectPolicy, |
| QNetworkRequest::FollowRedirectsAttribute |
| */ |
| void QNetworkAccessManager::setRedirectPolicy(QNetworkRequest::RedirectPolicy policy) |
| { |
| Q_D(QNetworkAccessManager); |
| d->redirectPolicy = policy; |
| } |
| |
| /*! |
| \since 5.9 |
| |
| Returns the redirect policy that is used when creating new requests. |
| |
| \sa setRedirectPolicy(), QNetworkRequest::RedirectPolicy |
| */ |
| QNetworkRequest::RedirectPolicy QNetworkAccessManager::redirectPolicy() const |
| { |
| Q_D(const QNetworkAccessManager); |
| return d->redirectPolicy; |
| } |
| |
| /*! |
| \since 4.7 |
| |
| Sends a custom request to the server identified by the URL of \a request. |
| |
| It is the user's responsibility to send a \a verb to the server that is valid |
| according to the HTTP specification. |
| |
| This method provides means to send verbs other than the common ones provided |
| via get() or post() etc., for instance sending an HTTP OPTIONS command. |
| |
| If \a data is not empty, the contents of the \a data |
| device will be uploaded to the server; in that case, data must be open for |
| reading and must remain valid until the finished() signal is emitted for this reply. |
| |
| \note This feature is currently available for HTTP(S) only. |
| |
| \sa get(), post(), put(), deleteResource() |
| */ |
| QNetworkReply *QNetworkAccessManager::sendCustomRequest(const QNetworkRequest &request, const QByteArray &verb, QIODevice *data) |
| { |
| QNetworkRequest newRequest(request); |
| newRequest.setAttribute(QNetworkRequest::CustomVerbAttribute, verb); |
| return d_func()->postProcess(createRequest(QNetworkAccessManager::CustomOperation, newRequest, data)); |
| } |
| |
| /*! |
| \since 5.8 |
| |
| \overload |
| |
| Sends the contents of the \a data byte array to the destination |
| specified by \a request. |
| */ |
| QNetworkReply *QNetworkAccessManager::sendCustomRequest(const QNetworkRequest &request, const QByteArray &verb, const QByteArray &data) |
| { |
| QBuffer *buffer = new QBuffer; |
| buffer->setData(data); |
| buffer->open(QIODevice::ReadOnly); |
| |
| QNetworkReply *reply = sendCustomRequest(request, verb, buffer); |
| buffer->setParent(reply); |
| return reply; |
| } |
| |
| #if QT_CONFIG(http) |
| /*! |
| \since 5.8 |
| |
| \overload |
| |
| Sends a custom request to the server identified by the URL of \a request. |
| |
| Sends the contents of the \a multiPart message to the destination |
| specified by \a request. |
| |
| This can be used for sending MIME multipart messages for custom verbs. |
| |
| \sa QHttpMultiPart, QHttpPart, put() |
| */ |
| QNetworkReply *QNetworkAccessManager::sendCustomRequest(const QNetworkRequest &request, const QByteArray &verb, QHttpMultiPart *multiPart) |
| { |
| QNetworkRequest newRequest = d_func()->prepareMultipart(request, multiPart); |
| QIODevice *device = multiPart->d_func()->device; |
| QNetworkReply *reply = sendCustomRequest(newRequest, verb, device); |
| return reply; |
| } |
| #endif // QT_CONFIG(http) |
| |
| /*! |
| Returns a new QNetworkReply object to handle the operation \a op |
| and request \a originalReq. The device \a outgoingData is always 0 |
| for Get and Head requests, but is the value passed to post() and |
| put() in those operations (the QByteArray variants will pass a QBuffer |
| object). |
| |
| The default implementation calls QNetworkCookieJar::cookiesForUrl() |
| on the cookie jar set with setCookieJar() to obtain the cookies to |
| be sent to the remote server. |
| |
| The returned object must be in an open state. |
| */ |
| QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Operation op, |
| const QNetworkRequest &originalReq, |
| QIODevice *outgoingData) |
| { |
| Q_D(QNetworkAccessManager); |
| |
| QNetworkRequest req(originalReq); |
| if (redirectPolicy() != QNetworkRequest::ManualRedirectPolicy |
| && req.attribute(QNetworkRequest::RedirectPolicyAttribute).isNull() |
| && req.attribute(QNetworkRequest::FollowRedirectsAttribute).isNull()) { |
| req.setAttribute(QNetworkRequest::RedirectPolicyAttribute, redirectPolicy()); |
| } |
| |
| if (autoDeleteReplies() |
| && req.attribute(QNetworkRequest::AutoDeleteReplyOnFinishAttribute).isNull()) { |
| req.setAttribute(QNetworkRequest::AutoDeleteReplyOnFinishAttribute, true); |
| } |
| |
| bool isLocalFile = req.url().isLocalFile(); |
| QString scheme = req.url().scheme(); |
| |
| #ifdef Q_OS_WASM |
| // Support http, https, and relateive urls |
| if (scheme == QLatin1String("http") || scheme == QLatin1String("https") || scheme.isEmpty()) { |
| QNetworkReplyWasmImpl *reply = new QNetworkReplyWasmImpl(this); |
| QNetworkReplyWasmImplPrivate *priv = reply->d_func(); |
| priv->manager = this; |
| priv->setup(op, req, outgoingData); |
| return reply; |
| } |
| #endif |
| |
| // fast path for GET on file:// URLs |
| // The QNetworkAccessFileBackend will right now only be used for PUT |
| if (op == QNetworkAccessManager::GetOperation |
| || op == QNetworkAccessManager::HeadOperation) { |
| if (isLocalFile |
| #ifdef Q_OS_ANDROID |
| || scheme == QLatin1String("assets") |
| #endif |
| || scheme == QLatin1String("qrc")) { |
| return new QNetworkReplyFileImpl(this, req, op); |
| } |
| |
| if (scheme == QLatin1String("data")) |
| return new QNetworkReplyDataImpl(this, req, op); |
| |
| // A request with QNetworkRequest::AlwaysCache does not need any bearer management |
| QNetworkRequest::CacheLoadControl mode = |
| static_cast<QNetworkRequest::CacheLoadControl>( |
| req.attribute(QNetworkRequest::CacheLoadControlAttribute, |
| QNetworkRequest::PreferNetwork).toInt()); |
| if (mode == QNetworkRequest::AlwaysCache) { |
| // FIXME Implement a QNetworkReplyCacheImpl instead, see QTBUG-15106 |
| QNetworkReplyImpl *reply = new QNetworkReplyImpl(this); |
| QNetworkReplyImplPrivate *priv = reply->d_func(); |
| priv->manager = this; |
| priv->backend = new QNetworkAccessCacheBackend(); |
| priv->backend->manager = this->d_func(); |
| priv->backend->setParent(reply); |
| priv->backend->reply = priv; |
| priv->setup(op, req, outgoingData); |
| return reply; |
| } |
| } |
| |
| if (d->statusMonitor.isEnabled()) { |
| // See the code in ctor - QNetworkStatusMonitor allows us to |
| // immediately set 'networkAccessible' even before we start |
| // the monitor. |
| #ifdef QT_NO_BEARERMANAGEMENT |
| if (d->networkAccessible |
| #else |
| if (d->networkAccessible == NotAccessible |
| #endif // QT_NO_BEARERMANAGEMENT |
| && !isLocalFile) { |
| QHostAddress dest; |
| QString host = req.url().host().toLower(); |
| if (!(dest.setAddress(host) && dest.isLoopback()) |
| && host != QLatin1String("localhost") |
| && host != QHostInfo::localHostName().toLower()) { |
| return new QDisabledNetworkReply(this, req, op); |
| } |
| } |
| |
| if (!d->statusMonitor.isMonitoring() && !d->statusMonitor.start()) |
| qWarning(lcNetMon, "failed to start network status monitoring"); |
| } else { |
| #ifndef QT_NO_BEARERMANAGEMENT |
| // Return a disabled network reply if network access is disabled. |
| // Except if the scheme is empty or file:// or if the host resolves to a loopback address. |
| if (d->networkAccessible == NotAccessible && !isLocalFile) { |
| QHostAddress dest; |
| QString host = req.url().host().toLower(); |
| if (!(dest.setAddress(host) && dest.isLoopback()) && host != QLatin1String("localhost") |
| && host != QHostInfo::localHostName().toLower()) { |
| return new QDisabledNetworkReply(this, req, op); |
| } |
| } |
| |
| if (!d->networkSessionStrongRef && (d->initializeSession || !d->networkConfiguration.identifier().isEmpty())) { |
| if (!d->networkConfiguration.identifier().isEmpty()) { |
| if ((d->networkConfiguration.state() & QNetworkConfiguration::Defined) |
| && d->networkConfiguration != d->networkConfigurationManager.defaultConfiguration()) |
| d->createSession(d->networkConfigurationManager.defaultConfiguration()); |
| else |
| d->createSession(d->networkConfiguration); |
| |
| } else { |
| if (d->networkSessionRequired) |
| d->createSession(d->networkConfigurationManager.defaultConfiguration()); |
| else |
| d->initializeSession = false; |
| } |
| } |
| #endif |
| } |
| |
| QNetworkRequest request = req; |
| if (!request.header(QNetworkRequest::ContentLengthHeader).isValid() && |
| outgoingData && !outgoingData->isSequential()) { |
| // request has no Content-Length |
| // but the data that is outgoing is random-access |
| request.setHeader(QNetworkRequest::ContentLengthHeader, outgoingData->size()); |
| } |
| |
| if (static_cast<QNetworkRequest::LoadControl> |
| (request.attribute(QNetworkRequest::CookieLoadControlAttribute, |
| QNetworkRequest::Automatic).toInt()) == QNetworkRequest::Automatic) { |
| if (d->cookieJar) { |
| QList<QNetworkCookie> cookies = d->cookieJar->cookiesForUrl(request.url()); |
| if (!cookies.isEmpty()) |
| request.setHeader(QNetworkRequest::CookieHeader, QVariant::fromValue(cookies)); |
| } |
| } |
| |
| #if QT_CONFIG(http) |
| // Since Qt 5 we use the new QNetworkReplyHttpImpl |
| if (scheme == QLatin1String("http") || scheme == QLatin1String("preconnect-http") |
| #ifndef QT_NO_SSL |
| || scheme == QLatin1String("https") || scheme == QLatin1String("preconnect-https") |
| #endif |
| ) { |
| #ifndef QT_NO_SSL |
| if (isStrictTransportSecurityEnabled() && d->stsCache.isKnownHost(request.url())) { |
| QUrl stsUrl(request.url()); |
| // RFC6797, 8.3: |
| // The UA MUST replace the URI scheme with "https" [RFC2818], |
| // and if the URI contains an explicit port component of "80", |
| // then the UA MUST convert the port component to be "443", or |
| // if the URI contains an explicit port component that is not |
| // equal to "80", the port component value MUST be preserved; |
| // otherwise, |
| // if the URI does not contain an explicit port component, the UA |
| // MUST NOT add one. |
| if (stsUrl.port() == 80) |
| stsUrl.setPort(443); |
| stsUrl.setScheme(QLatin1String("https")); |
| request.setUrl(stsUrl); |
| } |
| #endif |
| QNetworkReplyHttpImpl *reply = new QNetworkReplyHttpImpl(this, request, op, outgoingData); |
| #ifndef QT_NO_BEARERMANAGEMENT |
| if (!d->statusMonitor.isEnabled()) { |
| connect(this, SIGNAL(networkSessionConnected()), |
| reply, SLOT(_q_networkSessionConnected())); |
| } |
| #endif |
| return reply; |
| } |
| #endif // QT_CONFIG(http) |
| |
| // first step: create the reply |
| QNetworkReplyImpl *reply = new QNetworkReplyImpl(this); |
| #ifndef QT_NO_BEARERMANAGEMENT |
| // NETMONTODO: network reply impl must be augmented to use the same monitoring |
| // capabilities as http network reply impl does. Once it does: uncomment the condition below |
| if (!isLocalFile /*&& !d->statusMonitor.isEnabled()*/) { |
| connect(this, SIGNAL(networkSessionConnected()), |
| reply, SLOT(_q_networkSessionConnected())); |
| } |
| #endif |
| QNetworkReplyImplPrivate *priv = reply->d_func(); |
| priv->manager = this; |
| |
| // second step: fetch cached credentials |
| // This is not done for the time being, we should use signal emissions to request |
| // the credentials from cache. |
| |
| // third step: find a backend |
| priv->backend = d->findBackend(op, request); |
| |
| if (priv->backend) { |
| priv->backend->setParent(reply); |
| priv->backend->reply = priv; |
| } |
| |
| #ifndef QT_NO_SSL |
| reply->setSslConfiguration(request.sslConfiguration()); |
| #endif |
| |
| // fourth step: setup the reply |
| priv->setup(op, request, outgoingData); |
| |
| return reply; |
| } |
| |
| /*! |
| \since 5.2 |
| |
| Lists all the URL schemes supported by the access manager. |
| |
| \sa supportedSchemesImplementation() |
| */ |
| QStringList QNetworkAccessManager::supportedSchemes() const |
| { |
| QStringList schemes; |
| QNetworkAccessManager *self = const_cast<QNetworkAccessManager *>(this); // We know we call a const slot |
| QMetaObject::invokeMethod(self, "supportedSchemesImplementation", Qt::DirectConnection, |
| Q_RETURN_ARG(QStringList, schemes)); |
| schemes.removeDuplicates(); |
| return schemes; |
| } |
| |
| /*! |
| \since 5.2 |
| |
| Lists all the URL schemes supported by the access manager. |
| |
| You should not call this function directly; use |
| QNetworkAccessManager::supportedSchemes() instead. |
| |
| Reimplement this slot to provide your own supported schemes |
| in a QNetworkAccessManager subclass. It is for instance necessary |
| when your subclass provides support for new protocols. |
| |
| Because of binary compatibility constraints, the supportedSchemes() |
| method (introduced in Qt 5.2) is not virtual. Instead, supportedSchemes() |
| will dynamically detect and call this slot. |
| |
| \sa supportedSchemes() |
| */ |
| QStringList QNetworkAccessManager::supportedSchemesImplementation() const |
| { |
| Q_D(const QNetworkAccessManager); |
| |
| QStringList schemes = d->backendSupportedSchemes(); |
| // Those ones don't exist in backends |
| #if QT_CONFIG(http) |
| schemes << QStringLiteral("http"); |
| #ifndef QT_NO_SSL |
| if (QSslSocket::supportsSsl()) |
| schemes << QStringLiteral("https"); |
| #endif |
| #endif |
| schemes << QStringLiteral("data"); |
| return schemes; |
| } |
| |
| /*! |
| \since 5.0 |
| |
| Flushes the internal cache of authentication data and network connections. |
| |
| This function is useful for doing auto tests. |
| |
| \sa clearConnectionCache() |
| */ |
| void QNetworkAccessManager::clearAccessCache() |
| { |
| QNetworkAccessManagerPrivate::clearAuthenticationCache(this); |
| QNetworkAccessManagerPrivate::clearConnectionCache(this); |
| } |
| |
| /*! |
| \since 5.9 |
| |
| Flushes the internal cache of network connections. |
| In contrast to clearAccessCache() the authentication data |
| is preserved. |
| |
| \sa clearAccessCache() |
| */ |
| void QNetworkAccessManager::clearConnectionCache() |
| { |
| QNetworkAccessManagerPrivate::clearConnectionCache(this); |
| } |
| |
| |
| /*! |
| \since 5.14 |
| |
| Returns the true if QNetworkAccessManager is currently configured |
| to automatically delete QNetworkReplies, false otherwise. |
| |
| \sa setAutoDeleteReplies, |
| QNetworkRequest::AutoDeleteReplyOnFinishAttribute |
| */ |
| bool QNetworkAccessManager::autoDeleteReplies() const |
| { |
| return d_func()->autoDeleteReplies; |
| } |
| |
| /*! |
| \since 5.14 |
| |
| Enables or disables automatic deletion of \l {QNetworkReply} {QNetworkReplies}. |
| |
| Setting \a shouldAutoDelete to true is the same as setting the |
| QNetworkRequest::AutoDeleteReplyOnFinishAttribute attribute to |
| true on all \e{future} \l {QNetworkRequest} {QNetworkRequests} |
| passed to this instance of QNetworkAccessManager unless the |
| attribute was already explicitly set on the QNetworkRequest. |
| |
| \sa autoDeleteReplies, |
| QNetworkRequest::AutoDeleteReplyOnFinishAttribute |
| */ |
| void QNetworkAccessManager::setAutoDeleteReplies(bool shouldAutoDelete) |
| { |
| d_func()->autoDeleteReplies = shouldAutoDelete; |
| } |
| |
| void QNetworkAccessManagerPrivate::_q_replyFinished() |
| { |
| Q_Q(QNetworkAccessManager); |
| |
| QNetworkReply *reply = qobject_cast<QNetworkReply *>(q->sender()); |
| if (reply) { |
| emit q->finished(reply); |
| if (reply->request().attribute(QNetworkRequest::AutoDeleteReplyOnFinishAttribute, false).toBool()) |
| QMetaObject::invokeMethod(reply, [reply] { reply->deleteLater(); }, Qt::QueuedConnection); |
| } |
| |
| #ifndef QT_NO_BEARERMANAGEMENT |
| // If there are no active requests, release our reference to the network session. |
| // It will not be destroyed immediately, but rather when the connection cache is flushed |
| // after 2 minutes. |
| activeReplyCount--; |
| if (networkSessionStrongRef && activeReplyCount == 0) |
| networkSessionStrongRef.clear(); |
| #endif |
| } |
| |
| void QNetworkAccessManagerPrivate::_q_replyEncrypted() |
| { |
| #ifndef QT_NO_SSL |
| Q_Q(QNetworkAccessManager); |
| QNetworkReply *reply = qobject_cast<QNetworkReply *>(q->sender()); |
| if (reply) |
| emit q->encrypted(reply); |
| #endif |
| } |
| |
| void QNetworkAccessManagerPrivate::_q_replySslErrors(const QList<QSslError> &errors) |
| { |
| #ifndef QT_NO_SSL |
| Q_Q(QNetworkAccessManager); |
| QNetworkReply *reply = qobject_cast<QNetworkReply *>(q->sender()); |
| if (reply) |
| emit q->sslErrors(reply, errors); |
| #else |
| Q_UNUSED(errors); |
| #endif |
| } |
| |
| void QNetworkAccessManagerPrivate::_q_replyPreSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator *authenticator) |
| { |
| #ifndef QT_NO_SSL |
| Q_Q(QNetworkAccessManager); |
| QNetworkReply *reply = qobject_cast<QNetworkReply *>(q->sender()); |
| if (reply) |
| emit q->preSharedKeyAuthenticationRequired(reply, authenticator); |
| #else |
| Q_UNUSED(authenticator); |
| #endif |
| } |
| |
| QNetworkReply *QNetworkAccessManagerPrivate::postProcess(QNetworkReply *reply) |
| { |
| Q_Q(QNetworkAccessManager); |
| QNetworkReplyPrivate::setManager(reply, q); |
| q->connect(reply, SIGNAL(finished()), SLOT(_q_replyFinished())); |
| #ifndef QT_NO_SSL |
| /* In case we're compiled without SSL support, we don't have this signal and we need to |
| * avoid getting a connection error. */ |
| q->connect(reply, SIGNAL(encrypted()), SLOT(_q_replyEncrypted())); |
| q->connect(reply, SIGNAL(sslErrors(QList<QSslError>)), SLOT(_q_replySslErrors(QList<QSslError>))); |
| q->connect(reply, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)), SLOT(_q_replyPreSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*))); |
| #endif |
| #ifndef QT_NO_BEARERMANAGEMENT |
| activeReplyCount++; |
| #endif |
| |
| return reply; |
| } |
| |
| void QNetworkAccessManagerPrivate::createCookieJar() const |
| { |
| if (!cookieJarCreated) { |
| // keep the ugly hack in here |
| QNetworkAccessManagerPrivate *that = const_cast<QNetworkAccessManagerPrivate *>(this); |
| that->cookieJar = new QNetworkCookieJar(that->q_func()); |
| that->cookieJarCreated = true; |
| } |
| } |
| |
| void QNetworkAccessManagerPrivate::authenticationRequired(QAuthenticator *authenticator, |
| QNetworkReply *reply, |
| bool synchronous, |
| QUrl &url, |
| QUrl *urlForLastAuthentication, |
| bool allowAuthenticationReuse) |
| { |
| Q_Q(QNetworkAccessManager); |
| |
| // don't try the cache for the same URL twice in a row |
| // being called twice for the same URL means the authentication failed |
| // also called when last URL is empty, e.g. on first call |
| if (allowAuthenticationReuse && (urlForLastAuthentication->isEmpty() |
| || url != *urlForLastAuthentication)) { |
| // if credentials are included in the url, then use them |
| if (!url.userName().isEmpty() |
| && !url.password().isEmpty()) { |
| authenticator->setUser(url.userName(QUrl::FullyDecoded)); |
| authenticator->setPassword(url.password(QUrl::FullyDecoded)); |
| *urlForLastAuthentication = url; |
| authenticationManager->cacheCredentials(url, authenticator); |
| return; |
| } |
| |
| QNetworkAuthenticationCredential cred = authenticationManager->fetchCachedCredentials(url, authenticator); |
| if (!cred.isNull()) { |
| authenticator->setUser(cred.user); |
| authenticator->setPassword(cred.password); |
| *urlForLastAuthentication = url; |
| return; |
| } |
| } |
| |
| // if we emit a signal here in synchronous mode, the user might spin |
| // an event loop, which might recurse and lead to problems |
| if (synchronous) |
| return; |
| |
| *urlForLastAuthentication = url; |
| emit q->authenticationRequired(reply, authenticator); |
| if (allowAuthenticationReuse) |
| authenticationManager->cacheCredentials(url, authenticator); |
| } |
| |
| #ifndef QT_NO_NETWORKPROXY |
| void QNetworkAccessManagerPrivate::proxyAuthenticationRequired(const QUrl &url, |
| const QNetworkProxy &proxy, |
| bool synchronous, |
| QAuthenticator *authenticator, |
| QNetworkProxy *lastProxyAuthentication) |
| { |
| Q_Q(QNetworkAccessManager); |
| QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(*authenticator); |
| if (proxy != *lastProxyAuthentication && (!priv || !priv->hasFailed)) { |
| QNetworkAuthenticationCredential cred = authenticationManager->fetchCachedProxyCredentials(proxy); |
| if (!cred.isNull()) { |
| authenticator->setUser(cred.user); |
| authenticator->setPassword(cred.password); |
| return; |
| } |
| } |
| |
| #if defined(Q_OS_OSX) |
| //now we try to get the username and password from keychain |
| //if not successful signal will be emitted |
| QString username; |
| QString password; |
| if (getProxyAuth(proxy.hostName(), url.scheme(), username, password)) { |
| // only cache the system credentials if they are correct (or if they have changed) |
| // to not run into an endless loop in case they are wrong |
| QNetworkAuthenticationCredential cred = authenticationManager->fetchCachedProxyCredentials(proxy); |
| if (!priv->hasFailed || cred.user != username || cred.password != password) { |
| authenticator->setUser(username); |
| authenticator->setPassword(password); |
| authenticationManager->cacheProxyCredentials(proxy, authenticator); |
| return; |
| } |
| } |
| #else |
| Q_UNUSED(url); |
| #endif |
| |
| // if we emit a signal here in synchronous mode, the user might spin |
| // an event loop, which might recurse and lead to problems |
| if (synchronous) |
| return; |
| |
| *lastProxyAuthentication = proxy; |
| emit q->proxyAuthenticationRequired(proxy, authenticator); |
| authenticationManager->cacheProxyCredentials(proxy, authenticator); |
| } |
| |
| QList<QNetworkProxy> QNetworkAccessManagerPrivate::queryProxy(const QNetworkProxyQuery &query) |
| { |
| QList<QNetworkProxy> proxies; |
| if (proxyFactory) { |
| proxies = proxyFactory->queryProxy(query); |
| if (proxies.isEmpty()) { |
| qWarning("QNetworkAccessManager: factory %p has returned an empty result set", |
| proxyFactory); |
| proxies << QNetworkProxy::NoProxy; |
| } |
| } else if (proxy.type() == QNetworkProxy::DefaultProxy) { |
| // no proxy set, query the application |
| return QNetworkProxyFactory::proxyForQuery(query); |
| } else { |
| proxies << proxy; |
| } |
| |
| return proxies; |
| } |
| #endif |
| |
| void QNetworkAccessManagerPrivate::clearAuthenticationCache(QNetworkAccessManager *manager) |
| { |
| manager->d_func()->authenticationManager->clearCache(); |
| } |
| |
| void QNetworkAccessManagerPrivate::clearConnectionCache(QNetworkAccessManager *manager) |
| { |
| manager->d_func()->objectCache.clear(); |
| manager->d_func()->destroyThread(); |
| } |
| |
| QNetworkAccessManagerPrivate::~QNetworkAccessManagerPrivate() |
| { |
| destroyThread(); |
| } |
| |
| QThread * QNetworkAccessManagerPrivate::createThread() |
| { |
| if (!thread) { |
| thread = new QThread; |
| thread->setObjectName(QStringLiteral("QNetworkAccessManager thread")); |
| thread->start(); |
| } |
| Q_ASSERT(thread); |
| return thread; |
| } |
| |
| void QNetworkAccessManagerPrivate::destroyThread() |
| { |
| if (thread) { |
| thread->quit(); |
| thread->wait(5000); |
| if (thread->isFinished()) |
| delete thread; |
| else |
| QObject::connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); |
| thread = nullptr; |
| } |
| } |
| |
| #ifndef QT_NO_BEARERMANAGEMENT |
| void QNetworkAccessManagerPrivate::createSession(const QNetworkConfiguration &config) |
| { |
| Q_Q(QNetworkAccessManager); |
| |
| initializeSession = false; |
| |
| //resurrect weak ref if possible |
| networkSessionStrongRef = networkSessionWeakRef.toStrongRef(); |
| |
| QSharedPointer<QNetworkSession> newSession; |
| if (config.isValid()) |
| newSession = QSharedNetworkSessionManager::getSession(config); |
| |
| QNetworkSession::State oldState = QNetworkSession::Invalid; |
| if (networkSessionStrongRef) { |
| //do nothing if new and old session are the same |
| if (networkSessionStrongRef == newSession) |
| return; |
| //disconnect from old session |
| QObject::disconnect(networkSessionStrongRef.data(), SIGNAL(opened()), q, SIGNAL(networkSessionConnected())); |
| QObject::disconnect(networkSessionStrongRef.data(), SIGNAL(closed()), q, SLOT(_q_networkSessionClosed())); |
| QObject::disconnect(networkSessionStrongRef.data(), SIGNAL(stateChanged(QNetworkSession::State)), |
| q, SLOT(_q_networkSessionStateChanged(QNetworkSession::State))); |
| QObject::disconnect(networkSessionStrongRef.data(), SIGNAL(error(QNetworkSession::SessionError)), |
| q, SLOT(_q_networkSessionFailed(QNetworkSession::SessionError))); |
| oldState = networkSessionStrongRef->state(); |
| } |
| |
| //switch to new session (null if config was invalid) |
| networkSessionStrongRef = newSession; |
| networkSessionWeakRef = networkSessionStrongRef.toWeakRef(); |
| |
| if (!networkSessionStrongRef) { |
| |
| if (networkAccessible == QNetworkAccessManager::NotAccessible || !online) |
| emit q->networkAccessibleChanged(QNetworkAccessManager::NotAccessible); |
| else |
| emit q->networkAccessibleChanged(QNetworkAccessManager::UnknownAccessibility); |
| |
| return; |
| } |
| |
| //connect to new session |
| QObject::connect(networkSessionStrongRef.data(), SIGNAL(opened()), q, SIGNAL(networkSessionConnected()), Qt::QueuedConnection); |
| //QueuedConnection is used to avoid deleting the networkSession inside its closed signal |
| QObject::connect(networkSessionStrongRef.data(), SIGNAL(closed()), q, SLOT(_q_networkSessionClosed()), Qt::QueuedConnection); |
| QObject::connect(networkSessionStrongRef.data(), SIGNAL(stateChanged(QNetworkSession::State)), |
| q, SLOT(_q_networkSessionStateChanged(QNetworkSession::State)), Qt::QueuedConnection); |
| QObject::connect(networkSessionStrongRef.data(), SIGNAL(error(QNetworkSession::SessionError)), |
| q, SLOT(_q_networkSessionFailed(QNetworkSession::SessionError))); |
| |
| const QNetworkSession::State newState = networkSessionStrongRef->state(); |
| if (newState != oldState) { |
| QMetaObject::invokeMethod(q, "_q_networkSessionStateChanged", Qt::QueuedConnection, |
| Q_ARG(QNetworkSession::State, newState)); |
| } |
| } |
| |
| void QNetworkAccessManagerPrivate::_q_networkSessionClosed() |
| { |
| Q_Q(QNetworkAccessManager); |
| QSharedPointer<QNetworkSession> networkSession(getNetworkSession()); |
| if (networkSession) { |
| networkConfiguration = networkSession->configuration(); |
| |
| //disconnect from old session |
| QObject::disconnect(networkSession.data(), SIGNAL(opened()), q, SIGNAL(networkSessionConnected())); |
| QObject::disconnect(networkSession.data(), SIGNAL(closed()), q, SLOT(_q_networkSessionClosed())); |
| QObject::disconnect(networkSession.data(), SIGNAL(stateChanged(QNetworkSession::State)), |
| q, SLOT(_q_networkSessionStateChanged(QNetworkSession::State))); |
| QObject::disconnect(networkSession.data(), SIGNAL(error(QNetworkSession::SessionError)), |
| q, SLOT(_q_networkSessionFailed(QNetworkSession::SessionError))); |
| |
| networkSessionStrongRef.clear(); |
| networkSessionWeakRef.clear(); |
| } |
| } |
| |
| void QNetworkAccessManagerPrivate::_q_networkSessionStateChanged(QNetworkSession::State state) |
| { |
| Q_Q(QNetworkAccessManager); |
| bool reallyOnline = false; |
| //Do not emit the networkSessionConnected signal here, except for roaming -> connected |
| //transition, otherwise it is emitted twice in a row when opening a connection. |
| if (state == QNetworkSession::Connected && lastSessionState != QNetworkSession::Roaming) |
| emit q->networkSessionConnected(); |
| lastSessionState = state; |
| |
| if (online && (state == QNetworkSession::Disconnected |
| || state == QNetworkSession::NotAvailable)) { |
| const auto cfgs = networkConfigurationManager.allConfigurations(); |
| for (const QNetworkConfiguration &cfg : cfgs) { |
| if (cfg.state().testFlag(QNetworkConfiguration::Active)) { |
| reallyOnline = true; |
| } |
| } |
| } else if (state == QNetworkSession::Connected || state == QNetworkSession::Roaming) { |
| reallyOnline = true; |
| } |
| online = reallyOnline; |
| |
| if (!reallyOnline) { |
| if (state != QNetworkSession::Connected && state != QNetworkSession::Roaming) { |
| if (networkAccessible != QNetworkAccessManager::NotAccessible) { |
| networkAccessible = QNetworkAccessManager::NotAccessible; |
| emit q->networkAccessibleChanged(networkAccessible); |
| } |
| } |
| } else { |
| if (defaultAccessControl) |
| if (networkAccessible != QNetworkAccessManager::Accessible) { |
| networkAccessible = QNetworkAccessManager::Accessible; |
| emit q->networkAccessibleChanged(networkAccessible); |
| } |
| } |
| if (online && (state != QNetworkSession::Connected && state != QNetworkSession::Roaming)) { |
| _q_networkSessionClosed(); |
| createSession(q->configuration()); |
| } |
| } |
| |
| void QNetworkAccessManagerPrivate::_q_onlineStateChanged(bool isOnline) |
| { |
| Q_Q(QNetworkAccessManager); |
| |
| if (statusMonitor.isEnabled()) { |
| networkAccessible = isOnline ? QNetworkAccessManager::Accessible : QNetworkAccessManager::NotAccessible; |
| return; |
| } |
| |
| |
| // if the user set a config, we only care whether this one is active. |
| // Otherwise, this QNAM is online if there is an online config. |
| if (customNetworkConfiguration) { |
| online = (networkConfiguration.state() & QNetworkConfiguration::Active); |
| } else { |
| if (online != isOnline) { |
| online = isOnline; |
| _q_networkSessionClosed(); |
| createSession(q->configuration()); |
| } |
| } |
| if (online) { |
| if (defaultAccessControl) { |
| if (networkAccessible != QNetworkAccessManager::Accessible) { |
| networkAccessible = QNetworkAccessManager::Accessible; |
| emit q->networkAccessibleChanged(networkAccessible); |
| } |
| } |
| } else { |
| if (networkAccessible != QNetworkAccessManager::NotAccessible) { |
| networkAccessible = QNetworkAccessManager::NotAccessible; |
| emit q->networkAccessibleChanged(networkAccessible); |
| } |
| } |
| } |
| |
| void QNetworkAccessManagerPrivate::_q_configurationChanged(const QNetworkConfiguration &configuration) |
| { |
| if (statusMonitor.isEnabled()) |
| return; |
| |
| const QString id = configuration.identifier(); |
| if (configuration.state().testFlag(QNetworkConfiguration::Active)) { |
| if (!onlineConfigurations.contains(id)) { |
| QSharedPointer<QNetworkSession> session(getNetworkSession()); |
| if (session) { |
| if (online && session->configuration().identifier() |
| != networkConfigurationManager.defaultConfiguration().identifier()) { |
| |
| onlineConfigurations.insert(id); |
| // CHECK: If it's having Active flag - why would it be disconnected ??? |
| //this one disconnected but another one is online, |
| // close and create new session |
| _q_networkSessionClosed(); |
| createSession(networkConfigurationManager.defaultConfiguration()); |
| } |
| } |
| } |
| |
| } else if (onlineConfigurations.contains(id)) { |
| //this one is disconnecting |
| // CHECK: If it disconnected while we create a session over a down configuration ??? |
| onlineConfigurations.remove(id); |
| if (!onlineConfigurations.isEmpty()) { |
| _q_networkSessionClosed(); |
| createSession(configuration); |
| } |
| } |
| } |
| |
| |
| void QNetworkAccessManagerPrivate::_q_networkSessionFailed(QNetworkSession::SessionError) |
| { |
| if (statusMonitor.isEnabled()) |
| return; |
| |
| const auto cfgs = networkConfigurationManager.allConfigurations(); |
| for (const QNetworkConfiguration &cfg : cfgs) { |
| if (cfg.state().testFlag(QNetworkConfiguration::Active)) { |
| online = true; |
| _q_networkSessionClosed(); |
| createSession(networkConfigurationManager.defaultConfiguration()); |
| return; |
| } |
| } |
| } |
| |
| #else |
| |
| void QNetworkAccessManagerPrivate::_q_onlineStateChanged(bool isOnline) |
| { |
| networkAccessible = isOnline; |
| } |
| |
| #endif // QT_NO_BEARERMANAGEMENT |
| |
| #if QT_CONFIG(http) |
| QNetworkRequest QNetworkAccessManagerPrivate::prepareMultipart(const QNetworkRequest &request, QHttpMultiPart *multiPart) |
| { |
| // copy the request, we probably need to add some headers |
| QNetworkRequest newRequest(request); |
| |
| // add Content-Type header if not there already |
| if (!request.header(QNetworkRequest::ContentTypeHeader).isValid()) { |
| QByteArray contentType; |
| contentType.reserve(34 + multiPart->d_func()->boundary.count()); |
| contentType += "multipart/"; |
| switch (multiPart->d_func()->contentType) { |
| case QHttpMultiPart::RelatedType: |
| contentType += "related"; |
| break; |
| case QHttpMultiPart::FormDataType: |
| contentType += "form-data"; |
| break; |
| case QHttpMultiPart::AlternativeType: |
| contentType += "alternative"; |
| break; |
| default: |
| contentType += "mixed"; |
| break; |
| } |
| // putting the boundary into quotes, recommended in RFC 2046 section 5.1.1 |
| contentType += "; boundary=\"" + multiPart->d_func()->boundary + '"'; |
| newRequest.setHeader(QNetworkRequest::ContentTypeHeader, QVariant(contentType)); |
| } |
| |
| // add MIME-Version header if not there already (we must include the header |
| // if the message conforms to RFC 2045, see section 4 of that RFC) |
| QByteArray mimeHeader("MIME-Version"); |
| if (!request.hasRawHeader(mimeHeader)) |
| newRequest.setRawHeader(mimeHeader, QByteArray("1.0")); |
| |
| QIODevice *device = multiPart->d_func()->device; |
| if (!device->isReadable()) { |
| if (!device->isOpen()) { |
| if (!device->open(QIODevice::ReadOnly)) |
| qWarning("could not open device for reading"); |
| } else { |
| qWarning("device is not readable"); |
| } |
| } |
| |
| return newRequest; |
| } |
| #endif // QT_CONFIG(http) |
| |
| QT_END_NAMESPACE |
| |
| #include "moc_qnetworkaccessmanager.cpp" |