2262 lines
81 KiB
C++
2262 lines
81 KiB
C++
/****************************************************************************
|
|
**
|
|
** 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 HTTP2 via a request, so we need to check
|
|
// the ssl configuration whether HTTP2 is allowed here.
|
|
if (sslConfiguration.allowedNextProtocols().contains(QSslConfiguration::ALPNProtocolHTTP2))
|
|
request.setAttribute(QNetworkRequest::Http2AllowedAttribute, 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 QT_CONFIG(http)
|
|
if (!req.transferTimeout())
|
|
req.setTransferTimeout(transferTimeout());
|
|
#endif
|
|
|
|
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;
|
|
}
|
|
|
|
/*!
|
|
\since 5.15
|
|
|
|
Returns the timeout used for transfers, in milliseconds.
|
|
|
|
This timeout is zero if setTransferTimeout() hasn't been
|
|
called, which means that the timeout is not used.
|
|
*/
|
|
int QNetworkAccessManager::transferTimeout()
|
|
{
|
|
return d_func()->transferTimeout;
|
|
}
|
|
|
|
/*!
|
|
\since 5.15
|
|
|
|
Sets \a timeout as the transfer timeout in milliseconds.
|
|
|
|
Transfers are aborted if no bytes are transferred before
|
|
the timeout expires. Zero means no timer is set. If no
|
|
argument is provided, the timeout is
|
|
QNetworkRequest::TransferTimeoutPreset. If this function
|
|
is not called, the timeout is disabled and has the
|
|
value zero. The request-specific non-zero timeouts set for
|
|
the requests that are executed override this value. This means
|
|
that if QNetworkAccessManager has an enabled timeout, it needs
|
|
to be disabled to execute a request without a timeout.
|
|
|
|
\sa transferTimeout()
|
|
*/
|
|
void QNetworkAccessManager::setTransferTimeout(int timeout)
|
|
{
|
|
d_func()->transferTimeout = timeout;
|
|
}
|
|
|
|
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"
|