/**************************************************************************** | |
** | |
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). | |
** All rights reserved. | |
** Contact: Nokia Corporation (qt-info@nokia.com) | |
** | |
** This file is part of the QtNetwork module of the Qt Toolkit. | |
** | |
** $QT_BEGIN_LICENSE:LGPL$ | |
** GNU Lesser General Public License Usage | |
** This file may be used under the terms of the GNU Lesser General Public | |
** License version 2.1 as published by the Free Software Foundation and | |
** appearing in the file LICENSE.LGPL included in the packaging of this | |
** file. Please review the following information to ensure the GNU Lesser | |
** General Public License version 2.1 requirements will be met: | |
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. | |
** | |
** In addition, as a special exception, Nokia gives you certain additional | |
** rights. These rights are described in the Nokia Qt LGPL Exception | |
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. | |
** | |
** GNU General Public License Usage | |
** Alternatively, this file may be used under the terms of the GNU General | |
** Public License version 3.0 as published by the Free Software Foundation | |
** and appearing in the file LICENSE.GPL included in the packaging of this | |
** file. Please review the following information to ensure the GNU General | |
** Public License version 3.0 requirements will be met: | |
** http://www.gnu.org/copyleft/gpl.html. | |
** | |
** Other Usage | |
** Alternatively, this file may be used in accordance with the terms and | |
** conditions contained in a signed written agreement between you and Nokia. | |
** | |
** | |
** | |
** | |
** | |
** $QT_END_LICENSE$ | |
** | |
****************************************************************************/ | |
#include "qnetworkaccessmanager.h" | |
#include "qnetworkaccessmanager_p.h" | |
#include "qnetworkrequest.h" | |
#include "qnetworkreply.h" | |
#include "qnetworkreply_p.h" | |
#include "qnetworkcookie.h" | |
#include "qabstractnetworkcache.h" | |
#include "QtNetwork/qnetworksession.h" | |
#include "QtNetwork/private/qsharednetworksession_p.h" | |
#include "qnetworkaccesshttpbackend_p.h" | |
#include "qnetworkaccessftpbackend_p.h" | |
#include "qnetworkaccessfilebackend_p.h" | |
#include "qnetworkaccessdatabackend_p.h" | |
#include "qnetworkaccessdebugpipebackend_p.h" | |
#include "qnetworkaccesscachebackend_p.h" | |
#include "qfilenetworkreply_p.h" | |
#include "QtCore/qbuffer.h" | |
#include "QtCore/qurl.h" | |
#include "QtCore/qvector.h" | |
#include "QtNetwork/qauthenticator.h" | |
#include "QtNetwork/qsslconfiguration.h" | |
#include "QtNetwork/qnetworkconfigmanager.h" | |
QT_BEGIN_NAMESPACE | |
#ifndef QT_NO_HTTP | |
Q_GLOBAL_STATIC(QNetworkAccessHttpBackendFactory, httpBackend) | |
#endif // QT_NO_HTTP | |
Q_GLOBAL_STATIC(QNetworkAccessFileBackendFactory, fileBackend) | |
Q_GLOBAL_STATIC(QNetworkAccessDataBackendFactory, dataBackend) | |
#ifndef QT_NO_FTP | |
Q_GLOBAL_STATIC(QNetworkAccessFtpBackendFactory, ftpBackend) | |
#endif // QT_NO_FTP | |
#ifdef QT_BUILD_INTERNAL | |
Q_GLOBAL_STATIC(QNetworkAccessDebugPipeBackendFactory, debugpipeBackend) | |
#endif | |
static void ensureInitialized() | |
{ | |
#ifndef QT_NO_HTTP | |
(void) httpBackend(); | |
#endif // QT_NO_HTTP | |
(void) dataBackend(); | |
#ifndef QT_NO_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 | |
should be enough for the whole Qt application. | |
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 doc/src/snippets/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 doc/src/snippets/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 platform 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 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. Currently only | |
Meego/Harmattan and Symbian platforms provide connection management support. | |
\note This feature cannot be used in combination with the Bearer Management | |
API as provided by QtMobility. Applications have to migrate to the Qt version | |
of Bearer Management. | |
\section1 Symbian Platform Security Requirements | |
On Symbian, processes which use this class must have the | |
\c NetworkServices platform security capability. If the client | |
process lacks this capability, operations will result in a panic. | |
Platform security capabilities are added via the | |
\l{qmake-variable-reference.html#target-capability}{TARGET.CAPABILITY} | |
qmake variable. | |
\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 doc/src/snippets/code/src_network_access_qnetworkaccessmanager.cpp 4 | |
Network requests can be reenabled again by calling | |
\snippet doc/src/snippets/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. | |
\sa proxyAuthenticationRequired() | |
*/ | |
/*! | |
\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::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() | |
*/ | |
class QNetworkAuthenticationCredential | |
{ | |
public: | |
QString domain; | |
QString user; | |
QString password; | |
}; | |
Q_DECLARE_TYPEINFO(QNetworkAuthenticationCredential, Q_MOVABLE_TYPE); | |
inline bool operator<(const QNetworkAuthenticationCredential &t1, const QString &t2) | |
{ return t1.domain < t2; } | |
class QNetworkAuthenticationCache: private QVector<QNetworkAuthenticationCredential>, | |
public QNetworkAccessCache::CacheableObject | |
{ | |
public: | |
QNetworkAuthenticationCache() | |
{ | |
setExpires(false); | |
setShareable(true); | |
reserve(1); | |
} | |
QNetworkAuthenticationCredential *findClosestMatch(const QString &domain) | |
{ | |
iterator it = qLowerBound(begin(), end(), domain); | |
if (it == end() && !isEmpty()) | |
--it; | |
if (it == end() || !domain.startsWith(it->domain)) | |
return 0; | |
return &*it; | |
} | |
void insert(const QString &domain, const QString &user, const QString &password) | |
{ | |
QNetworkAuthenticationCredential *closestMatch = findClosestMatch(domain); | |
if (closestMatch && closestMatch->domain == domain) { | |
// we're overriding the current credentials | |
closestMatch->user = user; | |
closestMatch->password = password; | |
} else { | |
QNetworkAuthenticationCredential newCredential; | |
newCredential.domain = domain; | |
newCredential.user = user; | |
newCredential.password = password; | |
if (closestMatch) | |
QVector<QNetworkAuthenticationCredential>::insert(++closestMatch, newCredential); | |
else | |
QVector<QNetworkAuthenticationCredential>::insert(end(), newCredential); | |
} | |
} | |
virtual void dispose() { delete this; } | |
}; | |
#ifndef QT_NO_NETWORKPROXY | |
static QByteArray proxyAuthenticationKey(const QNetworkProxy &proxy, const QString &realm) | |
{ | |
QUrl key; | |
switch (proxy.type()) { | |
case QNetworkProxy::Socks5Proxy: | |
key.setScheme(QLatin1String("proxy-socks5")); | |
break; | |
case QNetworkProxy::HttpProxy: | |
case QNetworkProxy::HttpCachingProxy: | |
key.setScheme(QLatin1String("proxy-http")); | |
break; | |
case QNetworkProxy::FtpCachingProxy: | |
key.setScheme(QLatin1String("proxy-ftp")); | |
break; | |
case QNetworkProxy::DefaultProxy: | |
case QNetworkProxy::NoProxy: | |
// shouldn't happen | |
return QByteArray(); | |
// no default: | |
// let there be errors if a new proxy type is added in the future | |
} | |
if (key.scheme().isEmpty()) | |
// proxy type not handled | |
return QByteArray(); | |
key.setUserName(proxy.user()); | |
key.setHost(proxy.hostName()); | |
key.setPort(proxy.port()); | |
key.setFragment(realm); | |
return "auth:" + key.toEncoded(); | |
} | |
#endif | |
static inline QByteArray authenticationKey(const QUrl &url, const QString &realm) | |
{ | |
QUrl copy = url; | |
copy.setFragment(realm); | |
return "auth:" + copy.toEncoded(QUrl::RemovePassword | QUrl::RemovePath | QUrl::RemoveQuery); | |
} | |
/*! | |
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(); | |
} | |
/*! | |
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 = 0; | |
} | |
/*! | |
\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 | |
\o 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 | |
\o if the request is FTP, return an FTP proxy | |
\o if the request is HTTP or HTTPS, then return an HTTP proxy | |
\o 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 (thread() == cookieJar->thread()) | |
d->cookieJar->setParent(this); | |
} | |
} | |
/*! | |
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; | |
} | |
/*! | |
Uploads the contents of \a data to the destination \a request and | |
returnes 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(). | |
\snippet doc/src/snippets/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 doc/src/snippets/code/src_network_access_qnetworkaccessmanager.cpp 3 | |
\sa configuration(), QNetworkSession | |
*/ | |
void QNetworkAccessManager::setConfiguration(const QNetworkConfiguration &config) | |
{ | |
d_func()->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); | |
if (d->networkSession) | |
return d->networkSession->configuration(); | |
else | |
return QNetworkConfiguration(); | |
} | |
/*! | |
\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); | |
if (d->networkSession) { | |
QNetworkConfigurationManager manager; | |
return manager.configurationFromIdentifier( | |
d->networkSession->sessionProperty(QLatin1String("ActiveConfiguration")).toString()); | |
} else { | |
return QNetworkConfiguration(); | |
} | |
} | |
/*! | |
\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); | |
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->networkSession) { | |
// d->online holds online/offline state of this network session. | |
if (d->online) | |
return d->networkAccessible; | |
else | |
return NotAccessible; | |
} else { | |
// Network accessibility is either disabled or unknown. | |
return (d->networkAccessible == NotAccessible) ? NotAccessible : UnknownAccessibility; | |
} | |
} | |
#endif // QT_NO_BEARERMANAGEMENT | |
/*! | |
\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)); | |
} | |
/*! | |
Returns a new QNetworkReply object to handle the operation \a op | |
and request \a req. 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 &req, | |
QIODevice *outgoingData) | |
{ | |
Q_D(QNetworkAccessManager); | |
// 4.7 only hotfix fast path for data:// URLs | |
// In 4.8 this is solved with QNetworkReplyDataImpl and will work there | |
// This hotfix is done for not needing a QNetworkSession for data:// | |
if ((op == QNetworkAccessManager::GetOperation || op == QNetworkAccessManager::HeadOperation) | |
&& (req.url().scheme() == QLatin1String("data"))) { | |
QNetworkReplyImpl *reply = new QNetworkReplyImpl(this); | |
QNetworkReplyImplPrivate *priv = reply->d_func(); | |
priv->manager = this; | |
priv->backend = new QNetworkAccessDataBackend(); | |
priv->backend->manager = this->d_func(); | |
priv->backend->setParent(reply); | |
priv->backend->reply = priv; | |
priv->setup(op, req, outgoingData); | |
return reply; | |
} | |
// fast path for GET on file:// URLs | |
// Also if the scheme is empty we consider it a file. | |
// The QNetworkAccessFileBackend will right now only be used for PUT | |
if ((op == QNetworkAccessManager::GetOperation || op == QNetworkAccessManager::HeadOperation) | |
&& (req.url().scheme() == QLatin1String("file") | |
|| req.url().scheme() == QLatin1String("qrc") | |
|| req.url().scheme().isEmpty())) { | |
return new QFileNetworkReply(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 | |
&& (op == QNetworkAccessManager::GetOperation | |
|| op == QNetworkAccessManager::HeadOperation)) { | |
// 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; | |
} | |
#ifndef QT_NO_BEARERMANAGEMENT | |
// Return a disabled network reply if network access is disabled. | |
// Except if the scheme is empty or file://. | |
if (!d->networkAccessible && !(req.url().scheme() == QLatin1String("file") || | |
req.url().scheme().isEmpty())) { | |
return new QDisabledNetworkReply(this, req, op); | |
} | |
if (!d->networkSession && (d->initializeSession || !d->networkConfiguration.isEmpty())) { | |
QNetworkConfigurationManager manager; | |
if (!d->networkConfiguration.isEmpty()) { | |
d->createSession(manager.configurationFromIdentifier(d->networkConfiguration)); | |
} else { | |
if (manager.capabilities() & QNetworkConfigurationManager::NetworkSessionRequired) | |
d->createSession(manager.defaultConfiguration()); | |
else | |
d->initializeSession = false; | |
} | |
} | |
if (d->networkSession) | |
d->networkSession->setSessionProperty(QLatin1String("AutoCloseSessionTimeout"), -1); | |
#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, qVariantFromValue(cookies)); | |
} | |
} | |
// first step: create the reply | |
QUrl url = request.url(); | |
QNetworkReplyImpl *reply = new QNetworkReplyImpl(this); | |
#ifndef QT_NO_BEARERMANAGEMENT | |
if (req.url().scheme() != QLatin1String("file") && !req.url().scheme().isEmpty()) { | |
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); | |
#ifndef QT_NO_NETWORKPROXY | |
QList<QNetworkProxy> proxyList = d->queryProxy(QNetworkProxyQuery(request.url())); | |
priv->proxyList = proxyList; | |
#endif | |
if (priv->backend) { | |
priv->backend->setParent(reply); | |
priv->backend->reply = priv; | |
} | |
#ifndef QT_NO_OPENSSL | |
reply->setSslConfiguration(request.sslConfiguration()); | |
#endif | |
// fourth step: setup the reply | |
priv->setup(op, request, outgoingData); | |
return reply; | |
} | |
void QNetworkAccessManagerPrivate::_q_replyFinished() | |
{ | |
Q_Q(QNetworkAccessManager); | |
QNetworkReply *reply = qobject_cast<QNetworkReply *>(q->sender()); | |
if (reply) | |
emit q->finished(reply); | |
#ifndef QT_NO_BEARERMANAGEMENT | |
if (networkSession && q->findChildren<QNetworkReply *>().count() == 1) | |
networkSession->setSessionProperty(QLatin1String("AutoCloseSessionTimeout"), 120000); | |
#endif | |
} | |
void QNetworkAccessManagerPrivate::_q_replySslErrors(const QList<QSslError> &errors) | |
{ | |
#ifndef QT_NO_OPENSSL | |
Q_Q(QNetworkAccessManager); | |
QNetworkReply *reply = qobject_cast<QNetworkReply *>(q->sender()); | |
if (reply) | |
emit q->sslErrors(reply, errors); | |
#else | |
Q_UNUSED(errors); | |
#endif | |
} | |
QNetworkReply *QNetworkAccessManagerPrivate::postProcess(QNetworkReply *reply) | |
{ | |
Q_Q(QNetworkAccessManager); | |
QNetworkReplyPrivate::setManager(reply, q); | |
q->connect(reply, SIGNAL(finished()), SLOT(_q_replyFinished())); | |
#ifndef QT_NO_OPENSSL | |
/* 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(sslErrors(QList<QSslError>)), SLOT(_q_replySslErrors(QList<QSslError>))); | |
#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(QNetworkAccessBackend *backend, | |
QAuthenticator *authenticator) | |
{ | |
Q_Q(QNetworkAccessManager); | |
// FIXME: Add support for domains (i.e., the leading path) | |
QUrl url = backend->reply->url; | |
// 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 (backend->reply->urlForLastAuthentication.isEmpty() | |
|| url != backend->reply->urlForLastAuthentication) { | |
QNetworkAuthenticationCredential *cred = fetchCachedCredentials(url, authenticator); | |
if (cred) { | |
authenticator->setUser(cred->user); | |
authenticator->setPassword(cred->password); | |
backend->reply->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 (backend->isSynchronous()) | |
return; | |
backend->reply->urlForLastAuthentication = url; | |
emit q->authenticationRequired(backend->reply->q_func(), authenticator); | |
cacheCredentials(url, authenticator); | |
} | |
#ifndef QT_NO_NETWORKPROXY | |
void QNetworkAccessManagerPrivate::proxyAuthenticationRequired(QNetworkAccessBackend *backend, | |
const QNetworkProxy &proxy, | |
QAuthenticator *authenticator) | |
{ | |
Q_Q(QNetworkAccessManager); | |
// ### FIXME Tracking of successful authentications | |
// This code is a bit broken right now for SOCKS authentication | |
// first request: proxyAuthenticationRequired gets emitted, credentials gets saved | |
// second request: (proxy != backend->reply->lastProxyAuthentication) does not evaluate to true, | |
// proxyAuthenticationRequired gets emitted again | |
// possible solution: some tracking inside the authenticator | |
// or a new function proxyAuthenticationSucceeded(true|false) | |
if (proxy != backend->reply->lastProxyAuthentication) { | |
QNetworkAuthenticationCredential *cred = fetchCachedProxyCredentials(proxy); | |
if (cred) { | |
authenticator->setUser(cred->user); | |
authenticator->setPassword(cred->password); | |
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 (backend->isSynchronous()) | |
return; | |
backend->reply->lastProxyAuthentication = proxy; | |
emit q->proxyAuthenticationRequired(proxy, authenticator); | |
cacheProxyCredentials(proxy, authenticator); | |
} | |
void QNetworkAccessManagerPrivate::cacheProxyCredentials(const QNetworkProxy &p, | |
const QAuthenticator *authenticator) | |
{ | |
Q_ASSERT(authenticator); | |
Q_ASSERT(p.type() != QNetworkProxy::DefaultProxy); | |
Q_ASSERT(p.type() != QNetworkProxy::NoProxy); | |
QString realm = authenticator->realm(); | |
QNetworkProxy proxy = p; | |
proxy.setUser(authenticator->user()); | |
// Set two credentials: one with the username and one without | |
do { | |
// Set two credentials actually: one with and one without the realm | |
do { | |
QByteArray cacheKey = proxyAuthenticationKey(proxy, realm); | |
if (cacheKey.isEmpty()) | |
return; // should not happen | |
QNetworkAuthenticationCache *auth = new QNetworkAuthenticationCache; | |
auth->insert(QString(), authenticator->user(), authenticator->password()); | |
objectCache.addEntry(cacheKey, auth); // replace the existing one, if there's any | |
if (realm.isEmpty()) { | |
break; | |
} else { | |
realm.clear(); | |
} | |
} while (true); | |
if (proxy.user().isEmpty()) | |
break; | |
else | |
proxy.setUser(QString()); | |
} while (true); | |
} | |
QNetworkAuthenticationCredential * | |
QNetworkAccessManagerPrivate::fetchCachedProxyCredentials(const QNetworkProxy &p, | |
const QAuthenticator *authenticator) | |
{ | |
QNetworkProxy proxy = p; | |
if (proxy.type() == QNetworkProxy::DefaultProxy) { | |
proxy = QNetworkProxy::applicationProxy(); | |
} | |
if (!proxy.password().isEmpty()) | |
return 0; // no need to set credentials if it already has them | |
QString realm; | |
if (authenticator) | |
realm = authenticator->realm(); | |
QByteArray cacheKey = proxyAuthenticationKey(proxy, realm); | |
if (cacheKey.isEmpty()) | |
return 0; | |
if (!objectCache.hasEntry(cacheKey)) | |
return 0; | |
QNetworkAuthenticationCache *auth = | |
static_cast<QNetworkAuthenticationCache *>(objectCache.requestEntryNow(cacheKey)); | |
QNetworkAuthenticationCredential *cred = auth->findClosestMatch(QString()); | |
objectCache.releaseEntry(cacheKey); | |
// proxy cache credentials always have exactly one item | |
Q_ASSERT_X(cred, "QNetworkAccessManager", | |
"Internal inconsistency: found a cache key for a proxy, but it's empty"); | |
return cred; | |
} | |
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::cacheCredentials(const QUrl &url, | |
const QAuthenticator *authenticator) | |
{ | |
Q_ASSERT(authenticator); | |
QString domain = QString::fromLatin1("/"); // FIXME: make QAuthenticator return the domain | |
QString realm = authenticator->realm(); | |
// Set two credentials actually: one with and one without the username in the URL | |
QUrl copy = url; | |
copy.setUserName(authenticator->user()); | |
do { | |
QByteArray cacheKey = authenticationKey(copy, realm); | |
if (objectCache.hasEntry(cacheKey)) { | |
QNetworkAuthenticationCache *auth = | |
static_cast<QNetworkAuthenticationCache *>(objectCache.requestEntryNow(cacheKey)); | |
auth->insert(domain, authenticator->user(), authenticator->password()); | |
objectCache.releaseEntry(cacheKey); | |
} else { | |
QNetworkAuthenticationCache *auth = new QNetworkAuthenticationCache; | |
auth->insert(domain, authenticator->user(), authenticator->password()); | |
objectCache.addEntry(cacheKey, auth); | |
} | |
if (copy.userName().isEmpty()) { | |
break; | |
} else { | |
copy.setUserName(QString()); | |
} | |
} while (true); | |
} | |
/*! | |
Fetch the credential data from the credential cache. | |
If auth is 0 (as it is when called from createRequest()), this will try to | |
look up with an empty realm. That fails in most cases for HTTP (because the | |
realm is seldom empty for HTTP challenges). In any case, QHttpNetworkConnection | |
never sends the credentials on the first attempt: it needs to find out what | |
authentication methods the server supports. | |
For FTP, realm is always empty. | |
*/ | |
QNetworkAuthenticationCredential * | |
QNetworkAccessManagerPrivate::fetchCachedCredentials(const QUrl &url, | |
const QAuthenticator *authentication) | |
{ | |
if (!url.password().isEmpty()) | |
return 0; // no need to set credentials if it already has them | |
QString realm; | |
if (authentication) | |
realm = authentication->realm(); | |
QByteArray cacheKey = authenticationKey(url, realm); | |
if (!objectCache.hasEntry(cacheKey)) | |
return 0; | |
QNetworkAuthenticationCache *auth = | |
static_cast<QNetworkAuthenticationCache *>(objectCache.requestEntryNow(cacheKey)); | |
QNetworkAuthenticationCredential *cred = auth->findClosestMatch(url.path()); | |
objectCache.releaseEntry(cacheKey); | |
return cred; | |
} | |
void QNetworkAccessManagerPrivate::clearCache(QNetworkAccessManager *manager) | |
{ | |
manager->d_func()->objectCache.clear(); | |
} | |
QNetworkAccessManagerPrivate::~QNetworkAccessManagerPrivate() | |
{ | |
} | |
#ifndef QT_NO_BEARERMANAGEMENT | |
void QNetworkAccessManagerPrivate::createSession(const QNetworkConfiguration &config) | |
{ | |
Q_Q(QNetworkAccessManager); | |
initializeSession = false; | |
if (!config.isValid()) { | |
networkSession.clear(); | |
online = false; | |
if (networkAccessible == QNetworkAccessManager::NotAccessible) | |
emit q->networkAccessibleChanged(QNetworkAccessManager::NotAccessible); | |
else | |
emit q->networkAccessibleChanged(QNetworkAccessManager::UnknownAccessibility); | |
return; | |
} | |
networkSession = QSharedNetworkSessionManager::getSession(config); | |
QObject::connect(networkSession.data(), SIGNAL(opened()), q, SIGNAL(networkSessionConnected()), Qt::QueuedConnection); | |
//QueuedConnection is used to avoid deleting the networkSession inside its closed signal | |
QObject::connect(networkSession.data(), SIGNAL(closed()), q, SLOT(_q_networkSessionClosed()), Qt::QueuedConnection); | |
QObject::connect(networkSession.data(), SIGNAL(stateChanged(QNetworkSession::State)), | |
q, SLOT(_q_networkSessionStateChanged(QNetworkSession::State)), Qt::QueuedConnection); | |
_q_networkSessionStateChanged(networkSession->state()); | |
} | |
void QNetworkAccessManagerPrivate::_q_networkSessionClosed() | |
{ | |
if (networkSession) { | |
networkConfiguration = networkSession->configuration().identifier(); | |
networkSession.clear(); | |
} | |
} | |
void QNetworkAccessManagerPrivate::_q_networkSessionStateChanged(QNetworkSession::State state) | |
{ | |
Q_Q(QNetworkAccessManager); | |
if (state == QNetworkSession::Connected) | |
emit q->networkSessionConnected(); | |
if (online) { | |
if (state != QNetworkSession::Connected && state != QNetworkSession::Roaming) { | |
online = false; | |
emit q->networkAccessibleChanged(QNetworkAccessManager::NotAccessible); | |
} | |
} else { | |
if (state == QNetworkSession::Connected || state == QNetworkSession::Roaming) { | |
online = true; | |
emit q->networkAccessibleChanged(networkAccessible); | |
} | |
} | |
} | |
#endif // QT_NO_BEARERMANAGEMENT | |
QT_END_NAMESPACE | |
#include "moc_qnetworkaccessmanager.cpp" |