Redo QNetworkAccessBackend and various cleanups around it
Makes the QNetworkAccessBackendFactory a real interface to be used in plugins. Requires exporting some classes but they're not made public yet. Removes unused features and functions. Some things are likely still unused due to being specific for HTTP but the HTTP network replies don't use this backend system. Changes QNetworkAccessBackend to use a more traditional read(char*, qint64) function for the "downloaded" data. And an optional readPointer if supported. So far no backends have it so it's somewhat useless, but it may be useful going forward. If not it shall be deleted Converts all current backends to the new setup Easy enough, also gets rid of some unused functions. Task-number: QTBUG-80340 Change-Id: I9339e6c6eb394c471c921f5cafd3af6175936399 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
This commit is contained in:
parent
f7f79483e7
commit
bba0bdb35c
@ -5,6 +5,7 @@
|
||||
#####################################################################
|
||||
|
||||
qt_add_module(Network
|
||||
PLUGIN_TYPES networkaccessbackends
|
||||
SOURCES
|
||||
access/qabstractnetworkcache.cpp access/qabstractnetworkcache.h access/qabstractnetworkcache_p.h
|
||||
access/qhsts.cpp access/qhsts_p.h
|
||||
@ -70,13 +71,6 @@ qt_extend_target(Network CONDITION MSVC AND (TEST_architecture_arch STREQUAL "i3
|
||||
"/BASE:0x64000000"
|
||||
)
|
||||
|
||||
qt_extend_target(Network CONDITION QT_FEATURE_ftp
|
||||
SOURCES
|
||||
access/qftp.cpp access/qftp_p.h
|
||||
access/qnetworkaccessftpbackend.cpp access/qnetworkaccessftpbackend_p.h
|
||||
kernel/qurlinfo.cpp kernel/qurlinfo_p.h
|
||||
)
|
||||
|
||||
qt_extend_target(Network CONDITION QT_FEATURE_networkdiskcache
|
||||
SOURCES
|
||||
access/qnetworkdiskcache.cpp access/qnetworkdiskcache.h access/qnetworkdiskcache_p.h
|
||||
|
@ -5,6 +5,7 @@
|
||||
#####################################################################
|
||||
|
||||
qt_add_module(Network
|
||||
PLUGIN_TYPES networkaccessbackends
|
||||
SOURCES
|
||||
access/qabstractnetworkcache.cpp access/qabstractnetworkcache.h access/qabstractnetworkcache_p.h
|
||||
access/qhsts.cpp access/qhsts_p.h
|
||||
|
@ -54,7 +54,6 @@
|
||||
#include <QtNetwork/private/qtnetworkglobal_p.h>
|
||||
#include "qnetworkaccessmanager.h"
|
||||
#include "qnetworkaccesscache_p.h"
|
||||
#include "qnetworkaccessbackend_p.h"
|
||||
#include "QtNetwork/qnetworkproxy.h"
|
||||
#include "QtCore/QMutex"
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Copyright (C) 2020 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Qt Toolkit.
|
||||
@ -38,6 +38,7 @@
|
||||
****************************************************************************/
|
||||
|
||||
#include "qnetworkaccessbackend_p.h"
|
||||
#include "qnetworkreplyimpl_p.h"
|
||||
#include "qnetworkaccessmanager_p.h"
|
||||
#include "qnetworkrequest.h"
|
||||
#include "qnetworkreply.h"
|
||||
@ -73,22 +74,24 @@ public:
|
||||
Q_GLOBAL_STATIC(QNetworkAccessBackendFactoryData, factoryData)
|
||||
QBasicAtomicInt QNetworkAccessBackendFactoryData::valid = Q_BASIC_ATOMIC_INITIALIZER(0);
|
||||
|
||||
QNetworkAccessBackendFactory::QNetworkAccessBackendFactory()
|
||||
class QNetworkAccessBackendPrivate : public QObjectPrivate
|
||||
{
|
||||
QMutexLocker locker(&factoryData()->mutex);
|
||||
factoryData()->append(this);
|
||||
}
|
||||
public:
|
||||
QNetworkAccessBackend::TargetTypes m_targetTypes;
|
||||
QNetworkAccessBackend::SecurityFeatures m_securityFeatures;
|
||||
QNetworkAccessBackend::IOFeatures m_ioFeatures;
|
||||
QSharedPointer<QNonContiguousByteDevice> uploadByteDevice;
|
||||
QIODevice *wrappedUploadByteDevice;
|
||||
QNetworkReplyImplPrivate *m_reply = nullptr;
|
||||
QNetworkAccessManagerPrivate *m_manager = nullptr;
|
||||
|
||||
QNetworkAccessBackendFactory::~QNetworkAccessBackendFactory()
|
||||
{
|
||||
if (QNetworkAccessBackendFactoryData::valid.loadRelaxed()) {
|
||||
QMutexLocker locker(&factoryData()->mutex);
|
||||
factoryData()->removeAll(this);
|
||||
}
|
||||
}
|
||||
bool m_canCache = false;
|
||||
bool m_isSynchronous = false;
|
||||
};
|
||||
|
||||
QNetworkAccessBackend *QNetworkAccessManagerPrivate::findBackend(QNetworkAccessManager::Operation op,
|
||||
const QNetworkRequest &request)
|
||||
QNetworkAccessBackend *
|
||||
QNetworkAccessManagerPrivate::findBackend(QNetworkAccessManager::Operation op,
|
||||
const QNetworkRequest &request)
|
||||
{
|
||||
if (QNetworkAccessBackendFactoryData::valid.loadRelaxed()) {
|
||||
QMutexLocker locker(&factoryData()->mutex);
|
||||
@ -97,7 +100,7 @@ QNetworkAccessBackend *QNetworkAccessManagerPrivate::findBackend(QNetworkAccessM
|
||||
while (it != end) {
|
||||
QNetworkAccessBackend *backend = (*it)->create(op, request);
|
||||
if (backend) {
|
||||
backend->manager = this;
|
||||
backend->setManagerPrivate(this);
|
||||
return backend; // found a factory that handled our request
|
||||
}
|
||||
++it;
|
||||
@ -122,255 +125,150 @@ QStringList QNetworkAccessManagerPrivate::backendSupportedSchemes() const
|
||||
return QStringList();
|
||||
}
|
||||
|
||||
QNonContiguousByteDevice* QNetworkAccessBackend::createUploadByteDevice()
|
||||
{
|
||||
if (reply->outgoingDataBuffer)
|
||||
uploadByteDevice = QNonContiguousByteDeviceFactory::createShared(reply->outgoingDataBuffer);
|
||||
else if (reply->outgoingData) {
|
||||
uploadByteDevice = QNonContiguousByteDeviceFactory::createShared(reply->outgoingData);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
/*!
|
||||
\enum QNetworkAccessBackend::TargetType
|
||||
|
||||
// We want signal emissions only for normal asynchronous uploads
|
||||
if (!isSynchronous())
|
||||
connect(uploadByteDevice.data(), SIGNAL(readProgress(qint64,qint64)), this, SLOT(emitReplyUploadProgress(qint64,qint64)));
|
||||
Use the values in this enum to specify what type of target
|
||||
the plugin supports. Setting the right type can be important,
|
||||
for example: proxyList() is only available for a Networked
|
||||
plugin.
|
||||
|
||||
return uploadByteDevice.data();
|
||||
}
|
||||
\value Networked
|
||||
The plugin supports and expect to connect to networked
|
||||
resources. E.g. over TCP, UDP or similar.
|
||||
\value Local
|
||||
The plugin supports and expects to access local files,
|
||||
generate data and/or locally connected devices.
|
||||
*/
|
||||
|
||||
// need to have this function since the reply is a private member variable
|
||||
// and the special backends need to access this.
|
||||
void QNetworkAccessBackend::emitReplyUploadProgress(qint64 bytesSent, qint64 bytesTotal)
|
||||
{
|
||||
if (reply->isFinished)
|
||||
return;
|
||||
reply->emitUploadProgress(bytesSent, bytesTotal);
|
||||
}
|
||||
/*!
|
||||
\enum QNetworkAccessBackend::SecurityFeature
|
||||
|
||||
QNetworkAccessBackend::QNetworkAccessBackend()
|
||||
: manager(nullptr)
|
||||
, reply(nullptr)
|
||||
, synchronous(false)
|
||||
{
|
||||
}
|
||||
Use the values in this enum to specify what type of security
|
||||
features the plugin may utilize. Setting the right type(s)
|
||||
can be important, for example: setSslConfiguration() may not
|
||||
be called for any plugin that do not claim to support TLS.
|
||||
|
||||
QNetworkAccessBackend::~QNetworkAccessBackend()
|
||||
{
|
||||
}
|
||||
\value None
|
||||
No specific features are claimed to be supported.
|
||||
\value TLS
|
||||
The plugin supports and expects to use TLS.
|
||||
*/
|
||||
|
||||
void QNetworkAccessBackend::downstreamReadyWrite()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
/*!
|
||||
\enum QNetworkAccessBackend::IOFeature
|
||||
|
||||
void QNetworkAccessBackend::setDownstreamLimited(bool b)
|
||||
{
|
||||
Q_UNUSED(b);
|
||||
// do nothing
|
||||
}
|
||||
Use the values in this enum to specify what type of IO
|
||||
features the plugin may utilize.
|
||||
|
||||
void QNetworkAccessBackend::copyFinished(QIODevice *)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
\value None
|
||||
No specific features are claimed to be supported.
|
||||
\value ZeroCopy
|
||||
The plugin will have raw data available in contiguous
|
||||
segments and can return a pointer to the data at request.
|
||||
Claiming to support this requires implementing readPointer()
|
||||
and advanceReadPointer().
|
||||
\value NeedResetableUpload
|
||||
The plugin may encounter scenarios where data to upload that
|
||||
has already been consumed needs to be restored and re-sent.
|
||||
E.g. some data was consumed and sent before a redirect
|
||||
response was received, and after the redirect the
|
||||
previously-consumed data needs to be re-sent.
|
||||
\omitvalue SupportsSynchronousMode
|
||||
*/
|
||||
|
||||
void QNetworkAccessBackend::ignoreSslErrors()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
/*!
|
||||
Constructs the QNetworkAccessBackend.
|
||||
You can opt in to specific backend behaviors with \a targetTypes,
|
||||
\a securityFeatures and \a ioFeatures.
|
||||
See their respective enums and values for more information.
|
||||
|
||||
void QNetworkAccessBackend::ignoreSslErrors(const QList<QSslError> &errors)
|
||||
\sa TargetType, SecurityFeature, IOFeature
|
||||
*/
|
||||
QNetworkAccessBackend::QNetworkAccessBackend(TargetTypes targetTypes,
|
||||
SecurityFeatures securityFeatures,
|
||||
IOFeatures ioFeatures)
|
||||
: QObject(*(new QNetworkAccessBackendPrivate), nullptr)
|
||||
{
|
||||
Q_UNUSED(errors);
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void QNetworkAccessBackend::fetchSslConfiguration(QSslConfiguration &) const
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void QNetworkAccessBackend::setSslConfiguration(const QSslConfiguration &)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
QNetworkCacheMetaData QNetworkAccessBackend::fetchCacheMetaData(const QNetworkCacheMetaData &) const
|
||||
{
|
||||
return QNetworkCacheMetaData();
|
||||
}
|
||||
|
||||
QNetworkAccessManager::Operation QNetworkAccessBackend::operation() const
|
||||
{
|
||||
return reply->operation;
|
||||
}
|
||||
|
||||
QNetworkRequest QNetworkAccessBackend::request() const
|
||||
{
|
||||
return reply->request;
|
||||
}
|
||||
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
QList<QNetworkProxy> QNetworkAccessBackend::proxyList() const
|
||||
{
|
||||
return reply->proxyList;
|
||||
}
|
||||
#endif
|
||||
|
||||
QAbstractNetworkCache *QNetworkAccessBackend::networkCache() const
|
||||
{
|
||||
if (!manager)
|
||||
return nullptr;
|
||||
return manager->networkCache;
|
||||
}
|
||||
|
||||
void QNetworkAccessBackend::setCachingEnabled(bool enable)
|
||||
{
|
||||
reply->setCachingEnabled(enable);
|
||||
}
|
||||
|
||||
bool QNetworkAccessBackend::isCachingEnabled() const
|
||||
{
|
||||
return reply->isCachingEnabled();
|
||||
}
|
||||
|
||||
qint64 QNetworkAccessBackend::nextDownstreamBlockSize() const
|
||||
{
|
||||
return reply->nextDownstreamBlockSize();
|
||||
}
|
||||
|
||||
void QNetworkAccessBackend::writeDownstreamData(QByteDataBuffer &list)
|
||||
{
|
||||
reply->appendDownstreamData(list);
|
||||
}
|
||||
|
||||
void QNetworkAccessBackend::writeDownstreamData(QIODevice *data)
|
||||
{
|
||||
reply->appendDownstreamData(data);
|
||||
}
|
||||
|
||||
// not actually appending data, it was already written to the user buffer
|
||||
void QNetworkAccessBackend::writeDownstreamDataDownloadBuffer(qint64 bytesReceived, qint64 bytesTotal)
|
||||
{
|
||||
reply->appendDownstreamDataDownloadBuffer(bytesReceived, bytesTotal);
|
||||
}
|
||||
|
||||
char* QNetworkAccessBackend::getDownloadBuffer(qint64 size)
|
||||
{
|
||||
return reply->getDownloadBuffer(size);
|
||||
}
|
||||
|
||||
QVariant QNetworkAccessBackend::header(QNetworkRequest::KnownHeaders header) const
|
||||
{
|
||||
return reply->q_func()->header(header);
|
||||
}
|
||||
|
||||
void QNetworkAccessBackend::setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value)
|
||||
{
|
||||
reply->setCookedHeader(header, value);
|
||||
}
|
||||
|
||||
bool QNetworkAccessBackend::hasRawHeader(const QByteArray &headerName) const
|
||||
{
|
||||
return reply->q_func()->hasRawHeader(headerName);
|
||||
}
|
||||
|
||||
QByteArray QNetworkAccessBackend::rawHeader(const QByteArray &headerName) const
|
||||
{
|
||||
return reply->q_func()->rawHeader(headerName);
|
||||
}
|
||||
|
||||
QList<QByteArray> QNetworkAccessBackend::rawHeaderList() const
|
||||
{
|
||||
return reply->q_func()->rawHeaderList();
|
||||
}
|
||||
|
||||
void QNetworkAccessBackend::setRawHeader(const QByteArray &headerName, const QByteArray &headerValue)
|
||||
{
|
||||
reply->setRawHeader(headerName, headerValue);
|
||||
}
|
||||
|
||||
QVariant QNetworkAccessBackend::attribute(QNetworkRequest::Attribute code) const
|
||||
{
|
||||
return reply->q_func()->attribute(code);
|
||||
}
|
||||
|
||||
void QNetworkAccessBackend::setAttribute(QNetworkRequest::Attribute code, const QVariant &value)
|
||||
{
|
||||
if (value.isValid())
|
||||
reply->attributes.insert(code, value);
|
||||
else
|
||||
reply->attributes.remove(code);
|
||||
}
|
||||
QUrl QNetworkAccessBackend::url() const
|
||||
{
|
||||
return reply->url;
|
||||
}
|
||||
|
||||
void QNetworkAccessBackend::setUrl(const QUrl &url)
|
||||
{
|
||||
reply->url = url;
|
||||
}
|
||||
|
||||
void QNetworkAccessBackend::finished()
|
||||
{
|
||||
reply->finished();
|
||||
}
|
||||
|
||||
void QNetworkAccessBackend::error(QNetworkReply::NetworkError code, const QString &errorString)
|
||||
{
|
||||
reply->error(code, errorString);
|
||||
}
|
||||
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
void QNetworkAccessBackend::proxyAuthenticationRequired(const QNetworkProxy &proxy,
|
||||
QAuthenticator *authenticator)
|
||||
{
|
||||
manager->proxyAuthenticationRequired(QUrl(), proxy, synchronous, authenticator, &reply->lastProxyAuthentication);
|
||||
}
|
||||
#endif
|
||||
|
||||
void QNetworkAccessBackend::authenticationRequired(QAuthenticator *authenticator)
|
||||
{
|
||||
manager->authenticationRequired(authenticator, reply->q_func(), synchronous, reply->url, &reply->urlForLastAuthentication);
|
||||
}
|
||||
|
||||
void QNetworkAccessBackend::metaDataChanged()
|
||||
{
|
||||
reply->metaDataChanged();
|
||||
}
|
||||
|
||||
void QNetworkAccessBackend::redirectionRequested(const QUrl &target)
|
||||
{
|
||||
reply->redirectionRequested(target);
|
||||
}
|
||||
|
||||
void QNetworkAccessBackend::encrypted()
|
||||
{
|
||||
#ifndef QT_NO_SSL
|
||||
reply->encrypted();
|
||||
#endif
|
||||
}
|
||||
|
||||
void QNetworkAccessBackend::sslErrors(const QList<QSslError> &errors)
|
||||
{
|
||||
#ifndef QT_NO_SSL
|
||||
reply->sslErrors(errors);
|
||||
#else
|
||||
Q_UNUSED(errors);
|
||||
#endif
|
||||
Q_D(QNetworkAccessBackend);
|
||||
d->m_targetTypes = targetTypes;
|
||||
d->m_securityFeatures = securityFeatures;
|
||||
d->m_ioFeatures = ioFeatures;
|
||||
}
|
||||
|
||||
/*!
|
||||
Starts the backend. Returns \c true if the backend is started. Returns \c false if the backend
|
||||
could not be started due to an unopened or roaming session. The caller should recall this
|
||||
function once the session has been opened or the roaming process has finished.
|
||||
\overload
|
||||
*/
|
||||
QNetworkAccessBackend::QNetworkAccessBackend(TargetTypes targetTypes)
|
||||
: QNetworkAccessBackend(targetTypes, SecurityFeature::None, IOFeature::None)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
\overload
|
||||
*/
|
||||
QNetworkAccessBackend::QNetworkAccessBackend(TargetTypes targetTypes,
|
||||
SecurityFeatures securityFeatures)
|
||||
: QNetworkAccessBackend(targetTypes, securityFeatures, IOFeature::None)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
\overload
|
||||
*/
|
||||
QNetworkAccessBackend::QNetworkAccessBackend(TargetTypes targetTypes, IOFeatures ioFeatures)
|
||||
: QNetworkAccessBackend(targetTypes, SecurityFeature::None, ioFeatures)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Destructs the QNetworkAccessBackend base class.
|
||||
*/
|
||||
QNetworkAccessBackend::~QNetworkAccessBackend() { }
|
||||
|
||||
/*!
|
||||
Returns the security related features that the backend claims to
|
||||
support.
|
||||
|
||||
\sa SecurityFeature
|
||||
*/
|
||||
QNetworkAccessBackend::SecurityFeatures QNetworkAccessBackend::securityFeatures() const noexcept
|
||||
{
|
||||
return d_func()->m_securityFeatures;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the TargetTypes that the backend claims to target.
|
||||
|
||||
\sa TargetType
|
||||
*/
|
||||
QNetworkAccessBackend::TargetTypes QNetworkAccessBackend::targetTypes() const noexcept
|
||||
{
|
||||
return d_func()->m_targetTypes;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the I/O features that the backend claims to support.
|
||||
|
||||
\sa IOFeature
|
||||
*/
|
||||
QNetworkAccessBackend::IOFeatures QNetworkAccessBackend::ioFeatures() const noexcept
|
||||
{
|
||||
return d_func()->m_ioFeatures;
|
||||
}
|
||||
|
||||
/*!
|
||||
Prepares the backend and calls open().
|
||||
E.g. for TargetType::Networked it will prepare proxyList().
|
||||
|
||||
\sa TargetType, targetTypes
|
||||
*/
|
||||
bool QNetworkAccessBackend::start()
|
||||
{
|
||||
Q_D(QNetworkAccessBackend);
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
reply->proxyList = manager->queryProxy(QNetworkProxyQuery(url()));
|
||||
if (targetTypes() & QNetworkAccessBackend::TargetType::Networked)
|
||||
d->m_reply->proxyList = d->m_manager->queryProxy(QNetworkProxyQuery(url()));
|
||||
#endif
|
||||
|
||||
// now start the request
|
||||
@ -378,4 +276,468 @@ bool QNetworkAccessBackend::start()
|
||||
return true;
|
||||
}
|
||||
|
||||
#if QT_CONFIG(ssl)
|
||||
/*!
|
||||
Passes a \a configuration with the user's desired TLS
|
||||
configuration. If you don't have the TLS security feature this
|
||||
may not be called.
|
||||
|
||||
\sa SecurityFeature, securityFeatures
|
||||
*/
|
||||
void QNetworkAccessBackend::setSslConfiguration(const QSslConfiguration &configuration)
|
||||
{
|
||||
Q_UNUSED(configuration);
|
||||
if (securityFeatures() & SecurityFeature::TLS) {
|
||||
qWarning("Backend (%s) claiming to use TLS hasn't overridden setSslConfiguration.",
|
||||
metaObject()->className());
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Override this and return the QSslConfiguration used if you
|
||||
have the TLS security feature
|
||||
|
||||
\sa SecurityFeature, securityFeatures
|
||||
*/
|
||||
QSslConfiguration QNetworkAccessBackend::sslConfiguration() const
|
||||
{
|
||||
if (securityFeatures() & SecurityFeature::TLS) {
|
||||
qWarning("Backend (%s) claiming to use TLS hasn't overridden sslConfiguration.",
|
||||
metaObject()->className());
|
||||
}
|
||||
return {};
|
||||
}
|
||||
#endif
|
||||
|
||||
/*!
|
||||
This function will be called when the user wants to ignore
|
||||
all TLS handshake errors. Derive this function if TLS is
|
||||
supported.
|
||||
|
||||
\sa SecurityFeature, securityFeatures
|
||||
*/
|
||||
void QNetworkAccessBackend::ignoreSslErrors()
|
||||
{
|
||||
if (securityFeatures() & SecurityFeature::TLS) {
|
||||
qWarning("Backend (%s) claiming to use TLS hasn't overridden ignoreSslErrors.",
|
||||
metaObject()->className());
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
This function will be called when the user wants to ignore
|
||||
specific \a errors. Derive this function if TLS is supported.
|
||||
|
||||
\sa SecurityFeature, securityFeatures
|
||||
*/
|
||||
void QNetworkAccessBackend::ignoreSslErrors(const QList<QSslError> &errors)
|
||||
{
|
||||
Q_UNUSED(errors);
|
||||
if (securityFeatures() & SecurityFeature::TLS) {
|
||||
qWarning("Backend (%s) claiming to use TLS hasn't overridden ignoreSslErrors.",
|
||||
metaObject()->className());
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
The data which the returned value views must stay valid until
|
||||
at least the next call to a non-const function. advanceReadPointer
|
||||
will be called if any of the data was used.
|
||||
|
||||
Note: This will only be called if IOFeature::ZeroCopy was
|
||||
specified in the call to the constructor.
|
||||
|
||||
\sa advanceReadPointer, read
|
||||
*/
|
||||
QByteArrayView QNetworkAccessBackend::readPointer()
|
||||
{
|
||||
if (ioFeatures() & IOFeature::ZeroCopy) {
|
||||
qWarning("Backend (%s) claiming to support ZeroCopy hasn't overridden readPointer.",
|
||||
metaObject()->className());
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
/*!
|
||||
This function is to notify your class that \a distance
|
||||
bytes have been read using readPointer and next time
|
||||
readPointer() is called those bytes should not be included.
|
||||
|
||||
Note: This will only be called if IOFeature::ZeroCopy was
|
||||
specified in the call to the constructor.
|
||||
|
||||
\sa readPointer
|
||||
*/
|
||||
void QNetworkAccessBackend::advanceReadPointer(qint64 distance)
|
||||
{
|
||||
Q_UNUSED(distance);
|
||||
if (ioFeatures() & IOFeature::ZeroCopy) {
|
||||
qWarning("Backend (%s) claiming to support ZeroCopy hasn't overridden advanceReadPointer.",
|
||||
metaObject()->className());
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Implement this function to support reading from the resource
|
||||
made available by your plugin.
|
||||
Store data in \a data, up to a maximum of \a maxlen bytes.
|
||||
Then return the total amount of bytes that was copied.
|
||||
|
||||
\sa readPointer, wantToRead
|
||||
*/
|
||||
qint64 QNetworkAccessBackend::read(char *data, qint64 maxlen)
|
||||
{
|
||||
Q_UNUSED(data);
|
||||
Q_UNUSED(maxlen);
|
||||
if ((ioFeatures() & IOFeature::ZeroCopy) == 0) {
|
||||
qWarning("Backend (%s) is not ZeroCopy and has not implemented read(...)!",
|
||||
metaObject()->className());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
This is called before we read if there are no bytes available
|
||||
and we are ready to read more. Return \c true if new data was
|
||||
made available.
|
||||
|
||||
\sa read, readPointer
|
||||
*/
|
||||
bool QNetworkAccessBackend::wantToRead()
|
||||
{
|
||||
// Base implementation does nothing
|
||||
return false;
|
||||
}
|
||||
|
||||
#if QT_CONFIG(networkproxy)
|
||||
/*!
|
||||
Returns a list of proxies configured for the URL returned by
|
||||
url().
|
||||
|
||||
It is only valid to call this function if TargetType::Networked
|
||||
was specified in the call to the constructor.
|
||||
*/
|
||||
QList<QNetworkProxy> QNetworkAccessBackend::proxyList() const
|
||||
{
|
||||
Q_ASSERT(targetTypes() & TargetType::Networked);
|
||||
return d_func()->m_reply->proxyList;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*!
|
||||
Returns the current URL of the reply
|
||||
*/
|
||||
QUrl QNetworkAccessBackend::url() const
|
||||
{
|
||||
return d_func()->m_reply->url;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the URL of the reply. This could e.g. be needed if a
|
||||
redirect or similar was performed.
|
||||
*/
|
||||
void QNetworkAccessBackend::setUrl(const QUrl &url)
|
||||
{
|
||||
d_func()->m_reply->url = url;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the value of the \a header.
|
||||
If no such header was known it returns a default-constructed
|
||||
QVariant.
|
||||
|
||||
\sa setHeader, rawHeader, setRawHeader
|
||||
*/
|
||||
QVariant QNetworkAccessBackend::header(QNetworkRequest::KnownHeaders header) const
|
||||
{
|
||||
return d_func()->m_reply->cookedHeaders.value(header);
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the value of the \a header to \a value.
|
||||
This can be queried on the QNetworkReply instance which was
|
||||
returned when calling one of the appropriate functions on
|
||||
QNetworkAccessManager.
|
||||
|
||||
\sa header, rawHeader, setRawHeader
|
||||
*/
|
||||
void QNetworkAccessBackend::setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value)
|
||||
{
|
||||
d_func()->m_reply->setCookedHeader(header, value);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the value of the \a header.
|
||||
If no such header was known it returns a default-constructed
|
||||
QVariant.
|
||||
|
||||
\sa setHeader, rawHeader, setRawHeader
|
||||
*/
|
||||
QByteArray QNetworkAccessBackend::rawHeader(const QByteArray &header) const
|
||||
{
|
||||
return d_func()->m_reply->q_func()->rawHeader(header);
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the value of the \a header to \a value.
|
||||
|
||||
This value is accessible on the QNetworkReply instance which was
|
||||
returned when calling one of the appropriate functions on
|
||||
QNetworkAccessManager.
|
||||
|
||||
\sa header, rawHeader, setRawHeader
|
||||
*/
|
||||
void QNetworkAccessBackend::setRawHeader(const QByteArray &header, const QByteArray &value)
|
||||
{
|
||||
d_func()->m_reply->setRawHeader(header, value);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the operation which was requested when calling
|
||||
QNetworkAccessManager.
|
||||
*/
|
||||
QNetworkAccessManager::Operation QNetworkAccessBackend::operation() const
|
||||
{
|
||||
return d_func()->m_reply->operation;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns \c true if setCachingEnabled was previously called with \c true.
|
||||
Returns \c false otherwise, which is the default value.
|
||||
|
||||
\sa setCachingEnabled
|
||||
*/
|
||||
bool QNetworkAccessBackend::isCachingEnabled() const
|
||||
{
|
||||
return d_func()->m_canCache;
|
||||
}
|
||||
|
||||
/*!
|
||||
If \a canCache is \c true then this hints to us that we can cache
|
||||
the reply that is created.
|
||||
|
||||
\sa isCachingEnabled
|
||||
*/
|
||||
void QNetworkAccessBackend::setCachingEnabled(bool canCache)
|
||||
{
|
||||
d_func()->m_canCache = canCache;
|
||||
}
|
||||
|
||||
/*!
|
||||
Set \a attribute to \a value. If \c{value.isValid()} returns
|
||||
\c false then the attribute is unset.
|
||||
|
||||
This value is accessible on the QNetworkReply instance which was
|
||||
returned when calling one of the appropriate functions on
|
||||
QNetworkAccessManager.
|
||||
*/
|
||||
void QNetworkAccessBackend::setAttribute(QNetworkRequest::Attribute attribute,
|
||||
const QVariant &value)
|
||||
{
|
||||
Q_D(QNetworkAccessBackend);
|
||||
if (value.isValid())
|
||||
d->m_reply->attributes.insert(attribute, value);
|
||||
else
|
||||
d->m_reply->attributes.remove(attribute);
|
||||
}
|
||||
|
||||
/*!
|
||||
Creates a QIODevice for the data provided to upload, if any.
|
||||
|
||||
Emission of upload progress is handled internally as the device
|
||||
gets read from.
|
||||
|
||||
Returns a pointer to a device with data or nullptr if there was
|
||||
no data to upload.
|
||||
*/
|
||||
QIODevice *QNetworkAccessBackend::createUploadByteDevice()
|
||||
{
|
||||
Q_D(QNetworkAccessBackend);
|
||||
|
||||
if (d->m_reply->outgoingDataBuffer)
|
||||
d->uploadByteDevice =
|
||||
QNonContiguousByteDeviceFactory::createShared(d->m_reply->outgoingDataBuffer);
|
||||
else if (d->m_reply->outgoingData) {
|
||||
d->uploadByteDevice =
|
||||
QNonContiguousByteDeviceFactory::createShared(d->m_reply->outgoingData);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// We want signal emissions only for normal asynchronous uploads
|
||||
if (!isSynchronous()) {
|
||||
connect(d->uploadByteDevice.data(), &QNonContiguousByteDevice::readProgress, this,
|
||||
[this](qint64 a, qint64 b) {
|
||||
Q_D(QNetworkAccessBackend);
|
||||
if (!d->m_reply->isFinished)
|
||||
d->m_reply->emitUploadProgress(a, b);
|
||||
});
|
||||
}
|
||||
|
||||
d->wrappedUploadByteDevice = QNonContiguousByteDeviceFactory::wrap(d->uploadByteDevice.data());
|
||||
return d->wrappedUploadByteDevice;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the upload byte device associated with the current
|
||||
request. This does not create the request but simply returns
|
||||
the pointer stored in this base class so it doesn't need to be
|
||||
stored in the subclass too.
|
||||
*/
|
||||
QIODevice *QNetworkAccessBackend::uploadByteDevice()
|
||||
{
|
||||
return d_func()->wrappedUploadByteDevice;
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
Returns \c true if synchronous mode is enabled.
|
||||
If it is disabled or not supported it will return \c {false}.
|
||||
*/
|
||||
bool QNetworkAccessBackend::isSynchronous() const
|
||||
{
|
||||
return d_func()->m_isSynchronous;
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
Enables or disables synchronous mode depending on \a synchronous
|
||||
if the backend supports it. Otherwise it will always be disabled.
|
||||
*/
|
||||
void QNetworkAccessBackend::setSynchronous(bool synchronous)
|
||||
{
|
||||
if ((ioFeatures() & IOFeature::SupportsSynchronousMode) == 0)
|
||||
return;
|
||||
d_func()->m_isSynchronous = synchronous;
|
||||
}
|
||||
|
||||
/*!
|
||||
Call this slot when you have more data available to notify
|
||||
the backend that we can attempt to read again.
|
||||
*/
|
||||
void QNetworkAccessBackend::readyRead()
|
||||
{
|
||||
d_func()->m_reply->backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
|
||||
}
|
||||
|
||||
/*!
|
||||
Call this slot when there will be no more data available,
|
||||
regardless of whether the transfer was successful or unsuccessful.
|
||||
For unsuccessful transfers make sure to call error() first!
|
||||
*/
|
||||
void QNetworkAccessBackend::finished()
|
||||
{
|
||||
d_func()->m_reply->finished();
|
||||
}
|
||||
|
||||
/*!
|
||||
Call this slot if an error occurs. An error would be something
|
||||
you cannot recover from (e.g. the file requested is missing).
|
||||
The \a code and \a errorString is transferred to and stored in
|
||||
the QNetworkReply and the \a code is emitted through the
|
||||
QNetworkReply::errorOccurred() signal.
|
||||
*/
|
||||
void QNetworkAccessBackend::error(QNetworkReply::NetworkError code, const QString &errorString)
|
||||
{
|
||||
Q_ASSERT(!d_func()->m_reply->isFinished);
|
||||
d_func()->m_reply->error(code, errorString);
|
||||
}
|
||||
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
/*!
|
||||
Call this slot if, when connecting through a proxy, it requests
|
||||
authentication. This may cause the
|
||||
QNetworkAccessManager::proxyAuthenticationRequired() signal to be
|
||||
emitted if the credentials are not already stored in an internal
|
||||
cache.
|
||||
To be able to make the lookup in the cache and potentially the
|
||||
subsequent request the \a proxy needs to be known. The credentials
|
||||
will be stored in \a authenticator. While \a authenticator is a
|
||||
pointer, passing \c nullptr is invalid.
|
||||
*/
|
||||
void QNetworkAccessBackend::proxyAuthenticationRequired(const QNetworkProxy &proxy,
|
||||
QAuthenticator *authenticator)
|
||||
{
|
||||
Q_D(QNetworkAccessBackend);
|
||||
Q_ASSERT(authenticator);
|
||||
d->m_manager->proxyAuthenticationRequired(QUrl(), proxy, isSynchronous(), authenticator,
|
||||
&d->m_reply->lastProxyAuthentication);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*!
|
||||
Call this slot if the remote resource requests authentication.
|
||||
This may cause the
|
||||
QNetworkAccessManager::authenticationRequired() signal to be
|
||||
emitted if the credentials are not already stored in an internal
|
||||
cache.
|
||||
The credentials will be stored in \a authenticator. While
|
||||
\a authenticator is a pointer, passing \c nullptr is invalid.
|
||||
*/
|
||||
void QNetworkAccessBackend::authenticationRequired(QAuthenticator *authenticator)
|
||||
{
|
||||
Q_D(QNetworkAccessBackend);
|
||||
Q_ASSERT(authenticator);
|
||||
d->m_manager->authenticationRequired(authenticator, d->m_reply->q_func(), isSynchronous(),
|
||||
d->m_reply->url, &d->m_reply->urlForLastAuthentication);
|
||||
}
|
||||
|
||||
/*!
|
||||
Call this slot, if appropriate, after having processed and
|
||||
updated metadata (e.g. headers).
|
||||
*/
|
||||
void QNetworkAccessBackend::metaDataChanged()
|
||||
{
|
||||
d_func()->m_reply->metaDataChanged();
|
||||
}
|
||||
|
||||
/*!
|
||||
Call this slot if, when connecting to the resource, a redirect
|
||||
to \a destination was requested.
|
||||
*/
|
||||
void QNetworkAccessBackend::redirectionRequested(const QUrl &destination)
|
||||
{
|
||||
d_func()->m_reply->redirectionRequested(destination);
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
void QNetworkAccessBackend::setReplyPrivate(QNetworkReplyImplPrivate *reply)
|
||||
{
|
||||
d_func()->m_reply = reply;
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
void QNetworkAccessBackend::setManagerPrivate(QNetworkAccessManagerPrivate *manager)
|
||||
{
|
||||
d_func()->m_manager = manager;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the network cache object that was available when the
|
||||
request was started. Returns \c nullptr if none was available.
|
||||
*/
|
||||
QAbstractNetworkCache *QNetworkAccessBackend::networkCache() const
|
||||
{
|
||||
return d_func()->m_manager->networkCache;
|
||||
}
|
||||
|
||||
// -- QNetworkAccessBackendFactory
|
||||
/*!
|
||||
Constructs QNetworkAccessBackendFactory
|
||||
*/
|
||||
QNetworkAccessBackendFactory::QNetworkAccessBackendFactory()
|
||||
{
|
||||
if (factoryData())
|
||||
factoryData->append(this);
|
||||
}
|
||||
|
||||
/*!
|
||||
Destructs QNetworkAccessBackendFactory
|
||||
*/
|
||||
QNetworkAccessBackendFactory::~QNetworkAccessBackendFactory() = default;
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -1,6 +1,6 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Copyright (C) 2020 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Qt Toolkit.
|
||||
@ -40,153 +40,108 @@
|
||||
#ifndef QNETWORKACCESSBACKEND_P_H
|
||||
#define QNETWORKACCESSBACKEND_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Qt API. It exists for the convenience
|
||||
// of the Network Access API. This header file may change from
|
||||
// version to version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
#include <QtNetwork/qtnetworkglobal.h>
|
||||
|
||||
#include <QtNetwork/private/qtnetworkglobal_p.h>
|
||||
#include "qnetworkreplyimpl_p.h"
|
||||
#include "QtCore/qobject.h"
|
||||
Q_MOC_INCLUDE(<QAuthenticator>)
|
||||
Q_MOC_INCLUDE(<QtNetwork/QSslError>)
|
||||
#include <QtNetwork/qnetworkrequest.h>
|
||||
#include <QtNetwork/qnetworkaccessmanager.h>
|
||||
#include <QtNetwork/qnetworkreply.h>
|
||||
|
||||
#include <QtCore/qobject.h>
|
||||
#include <QtCore/qflags.h>
|
||||
#include <QtCore/qbytearrayview.h>
|
||||
|
||||
#if QT_CONFIG(ssl)
|
||||
#include <QtNetwork/qsslconfiguration.h>
|
||||
#endif
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QNetworkProxy;
|
||||
class QNetworkProxyQuery;
|
||||
class QNetworkRequest;
|
||||
class QStringList;
|
||||
class QUrl;
|
||||
class QSslConfiguration;
|
||||
|
||||
class QNetworkAccessManagerPrivate;
|
||||
class QNetworkReplyImplPrivate;
|
||||
class QAbstractNetworkCache;
|
||||
class QNetworkCacheMetaData;
|
||||
class QNonContiguousByteDevice;
|
||||
|
||||
// Should support direct file upload from disk or download to disk.
|
||||
//
|
||||
// - The HTTP handler will use two QIODevices for communication (pull mechanism)
|
||||
// - KIO uses a pull mechanism too (data/dataReq signals)
|
||||
class QNetworkAccessBackend : public QObject
|
||||
class QNetworkAccessManagerPrivate;
|
||||
class QNetworkAccessBackendPrivate;
|
||||
class Q_NETWORK_EXPORT QNetworkAccessBackend : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DECLARE_PRIVATE(QNetworkAccessBackend);
|
||||
|
||||
public:
|
||||
QNetworkAccessBackend();
|
||||
enum class TargetType {
|
||||
Networked = 0x1, // We need to query for proxy in case it is needed
|
||||
Local = 0x2, // Local file, generated data or local device
|
||||
};
|
||||
Q_ENUM(TargetType)
|
||||
Q_DECLARE_FLAGS(TargetTypes, TargetType)
|
||||
|
||||
enum class SecurityFeature {
|
||||
None = 0x0,
|
||||
TLS = 0x1, // We need to set QSslConfiguration
|
||||
};
|
||||
Q_ENUM(SecurityFeature)
|
||||
Q_DECLARE_FLAGS(SecurityFeatures, SecurityFeature)
|
||||
|
||||
enum class IOFeature {
|
||||
None = 0x0,
|
||||
ZeroCopy = 0x1, // readPointer and advanceReadPointer() is available!
|
||||
NeedResetableUpload = 0x2, // Need to buffer upload data
|
||||
SupportsSynchronousMode = 0x4, // Used for XMLHttpRequest
|
||||
};
|
||||
Q_ENUM(IOFeature)
|
||||
Q_DECLARE_FLAGS(IOFeatures, IOFeature)
|
||||
|
||||
QNetworkAccessBackend(TargetTypes targetTypes, SecurityFeatures securityFeatures,
|
||||
IOFeatures ioFeatures);
|
||||
QNetworkAccessBackend(TargetTypes targetTypes);
|
||||
QNetworkAccessBackend(TargetTypes targetTypes, SecurityFeatures securityFeatures);
|
||||
QNetworkAccessBackend(TargetTypes targetTypes, IOFeatures ioFeatures);
|
||||
virtual ~QNetworkAccessBackend();
|
||||
|
||||
// To avoid mistaking with QIODevice names, the functions here
|
||||
// have different names. The Connection has two streams:
|
||||
//
|
||||
// - Upstream:
|
||||
// The upstream uses a QNonContiguousByteDevice provided
|
||||
// by the backend. This device emits the usual readyRead()
|
||||
// signal when the backend has data available for the connection
|
||||
// to write. The different backends can listen on this signal
|
||||
// and then pull upload data from the QNonContiguousByteDevice and
|
||||
// deal with it.
|
||||
//
|
||||
//
|
||||
// - Downstream:
|
||||
// Downstream is the data that is being read from this
|
||||
// connection and is given to the user. Downstream operates in a
|
||||
// semi-"push" mechanism: the Connection object pushes the data
|
||||
// it gets from the network, but it should avoid writing too
|
||||
// much if the data isn't being used fast enough. The value
|
||||
// returned by suggestedDownstreamBlockSize() can be used to
|
||||
// determine how much should be written at a time. The
|
||||
// downstreamBytesConsumed() function will be called when the
|
||||
// downstream buffer is consumed by the user -- the Connection
|
||||
// may choose to re-fill it with more data it has ready or get
|
||||
// more data from the network (for instance, by reading from its
|
||||
// socket).
|
||||
SecurityFeatures securityFeatures() const noexcept;
|
||||
TargetTypes targetTypes() const noexcept;
|
||||
IOFeatures ioFeatures() const noexcept;
|
||||
|
||||
inline bool needsResetableUploadData() const noexcept
|
||||
{
|
||||
return ioFeatures() & IOFeature::NeedResetableUpload;
|
||||
}
|
||||
|
||||
virtual void open() = 0;
|
||||
virtual bool start();
|
||||
virtual void closeDownstreamChannel() = 0;
|
||||
|
||||
// slot-like:
|
||||
virtual void downstreamReadyWrite();
|
||||
virtual void setDownstreamLimited(bool b);
|
||||
virtual void copyFinished(QIODevice *);
|
||||
virtual void open() = 0;
|
||||
virtual void close() = 0;
|
||||
#if QT_CONFIG(ssl)
|
||||
virtual void setSslConfiguration(const QSslConfiguration &configuration);
|
||||
virtual QSslConfiguration sslConfiguration() const;
|
||||
#endif
|
||||
virtual void ignoreSslErrors();
|
||||
virtual void ignoreSslErrors(const QList<QSslError> &errors);
|
||||
virtual qint64 bytesAvailable() const = 0;
|
||||
virtual QByteArrayView readPointer();
|
||||
virtual void advanceReadPointer(qint64 distance);
|
||||
virtual qint64 read(char *data, qint64 maxlen);
|
||||
virtual bool wantToRead();
|
||||
|
||||
virtual void fetchSslConfiguration(QSslConfiguration &configuration) const;
|
||||
virtual void setSslConfiguration(const QSslConfiguration &configuration);
|
||||
|
||||
virtual QNetworkCacheMetaData fetchCacheMetaData(const QNetworkCacheMetaData &metaData) const;
|
||||
|
||||
// information about the request
|
||||
QNetworkAccessManager::Operation operation() const;
|
||||
QNetworkRequest request() const;
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
#if QT_CONFIG(networkproxy)
|
||||
QList<QNetworkProxy> proxyList() const;
|
||||
#endif
|
||||
|
||||
QAbstractNetworkCache *networkCache() const;
|
||||
void setCachingEnabled(bool enable);
|
||||
bool isCachingEnabled() const;
|
||||
|
||||
// information about the reply
|
||||
QUrl url() const;
|
||||
void setUrl(const QUrl &url);
|
||||
|
||||
// "cooked" headers
|
||||
QVariant header(QNetworkRequest::KnownHeaders header) const;
|
||||
void setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value);
|
||||
QByteArray rawHeader(const QByteArray &header) const;
|
||||
void setRawHeader(const QByteArray &header, const QByteArray &value);
|
||||
QNetworkAccessManager::Operation operation() const;
|
||||
|
||||
// raw headers:
|
||||
bool hasRawHeader(const QByteArray &headerName) const;
|
||||
QList<QByteArray> rawHeaderList() const;
|
||||
QByteArray rawHeader(const QByteArray &headerName) const;
|
||||
void setRawHeader(const QByteArray &headerName, const QByteArray &value);
|
||||
bool isCachingEnabled() const;
|
||||
void setCachingEnabled(bool canCache);
|
||||
|
||||
// attributes:
|
||||
QVariant attribute(QNetworkRequest::Attribute code) const;
|
||||
void setAttribute(QNetworkRequest::Attribute code, const QVariant &value);
|
||||
void setAttribute(QNetworkRequest::Attribute attribute, const QVariant &value);
|
||||
|
||||
bool isSynchronous() { return synchronous; }
|
||||
void setSynchronous(bool sync) { synchronous = sync; }
|
||||
QIODevice *createUploadByteDevice();
|
||||
QIODevice *uploadByteDevice();
|
||||
|
||||
// return true if the QNonContiguousByteDevice of the upload
|
||||
// data needs to support reset(). Currently needed for HTTP.
|
||||
// This will possibly enable buffering of the upload data.
|
||||
virtual bool needsResetableUploadData() { return false; }
|
||||
|
||||
// Returns \c true if backend is able to resume downloads.
|
||||
virtual bool canResume() const { return false; }
|
||||
virtual void setResumeOffset(quint64 offset) { Q_UNUSED(offset); }
|
||||
|
||||
virtual bool processRequestSynchronously() { return false; }
|
||||
|
||||
protected:
|
||||
// Create the device used for reading the upload data
|
||||
QNonContiguousByteDevice* createUploadByteDevice();
|
||||
|
||||
// these functions control the downstream mechanism
|
||||
// that is, data that has come via the connection and is going out the backend
|
||||
qint64 nextDownstreamBlockSize() const;
|
||||
void writeDownstreamData(QByteDataBuffer &list);
|
||||
|
||||
// not actually appending data, it was already written to the user buffer
|
||||
void writeDownstreamDataDownloadBuffer(qint64, qint64);
|
||||
char* getDownloadBuffer(qint64);
|
||||
|
||||
QSharedPointer<QNonContiguousByteDevice> uploadByteDevice;
|
||||
QAbstractNetworkCache *networkCache() const;
|
||||
|
||||
public slots:
|
||||
// for task 251801, needs to be a slot to be called asynchronously
|
||||
void writeDownstreamData(QIODevice *data);
|
||||
|
||||
void readyRead();
|
||||
protected slots:
|
||||
void finished();
|
||||
void error(QNetworkReply::NetworkError code, const QString &errorString);
|
||||
@ -196,26 +151,21 @@ protected slots:
|
||||
void authenticationRequired(QAuthenticator *auth);
|
||||
void metaDataChanged();
|
||||
void redirectionRequested(const QUrl &destination);
|
||||
void encrypted();
|
||||
void sslErrors(const QList<QSslError> &errors);
|
||||
void emitReplyUploadProgress(qint64 bytesSent, qint64 bytesTotal);
|
||||
|
||||
protected:
|
||||
// FIXME In the long run we should get rid of our QNAM architecture
|
||||
// and scrap this ReplyImpl/Backend distinction.
|
||||
QNetworkAccessManagerPrivate *manager;
|
||||
QNetworkReplyImplPrivate *reply;
|
||||
|
||||
private:
|
||||
friend class QNetworkAccessManager;
|
||||
friend class QNetworkAccessManagerPrivate;
|
||||
friend class QNetworkReplyImplPrivate;
|
||||
void setReplyPrivate(QNetworkReplyImplPrivate *reply);
|
||||
void setManagerPrivate(QNetworkAccessManagerPrivate *manager);
|
||||
bool isSynchronous() const;
|
||||
void setSynchronous(bool synchronous);
|
||||
|
||||
bool synchronous;
|
||||
friend class QNetworkAccessManager; // for setReplyPrivate
|
||||
friend class QNetworkAccessManagerPrivate; // for setManagerPrivate
|
||||
friend class QNetworkReplyImplPrivate; // for {set,is}Synchronous()
|
||||
};
|
||||
|
||||
class QNetworkAccessBackendFactory
|
||||
class Q_NETWORK_EXPORT QNetworkAccessBackendFactory : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QNetworkAccessBackendFactory();
|
||||
virtual ~QNetworkAccessBackendFactory();
|
||||
@ -224,7 +174,8 @@ public:
|
||||
const QNetworkRequest &request) const = 0;
|
||||
};
|
||||
|
||||
#define QNetworkAccessBackendFactory_iid "org.qt-project.Qt.NetworkAccessBackendFactory"
|
||||
Q_DECLARE_INTERFACE(QNetworkAccessBackendFactory, QNetworkAccessBackendFactory_iid);
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -48,7 +48,7 @@
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QNetworkAccessCacheBackend::QNetworkAccessCacheBackend()
|
||||
: QNetworkAccessBackend()
|
||||
: QNetworkAccessBackend(QNetworkAccessBackend::TargetType::Local)
|
||||
{
|
||||
}
|
||||
|
||||
@ -107,11 +107,11 @@ bool QNetworkAccessCacheBackend::sendCacheContents()
|
||||
metaDataChanged();
|
||||
|
||||
if (operation() == QNetworkAccessManager::GetOperation) {
|
||||
QIODevice *contents = nc->data(url());
|
||||
if (!contents)
|
||||
device = nc->data(url());
|
||||
if (!device)
|
||||
return false;
|
||||
contents->setParent(this);
|
||||
writeDownstreamData(contents);
|
||||
device->setParent(this);
|
||||
readyRead();
|
||||
}
|
||||
|
||||
#if defined(QNETWORKACCESSCACHEBACKEND_DEBUG)
|
||||
@ -126,23 +126,16 @@ bool QNetworkAccessCacheBackend::start()
|
||||
return true;
|
||||
}
|
||||
|
||||
void QNetworkAccessCacheBackend::closeDownstreamChannel()
|
||||
void QNetworkAccessCacheBackend::close() { }
|
||||
|
||||
qint64 QNetworkAccessCacheBackend::bytesAvailable() const
|
||||
{
|
||||
return device ? device->bytesAvailable() : qint64(0);
|
||||
}
|
||||
|
||||
void QNetworkAccessCacheBackend::closeUpstreamChannel()
|
||||
qint64 QNetworkAccessCacheBackend::read(char *data, qint64 maxlen)
|
||||
{
|
||||
Q_ASSERT_X(false, Q_FUNC_INFO, "This function show not have been called!");
|
||||
}
|
||||
|
||||
void QNetworkAccessCacheBackend::upstreamReadyRead()
|
||||
{
|
||||
Q_ASSERT_X(false, Q_FUNC_INFO, "This function show not have been called!");
|
||||
}
|
||||
|
||||
void QNetworkAccessCacheBackend::downstreamReadyWrite()
|
||||
{
|
||||
Q_ASSERT_X(false, Q_FUNC_INFO, "This function show not have been called!");
|
||||
return device ? device->read(data, maxlen) : qint64(0);
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -66,16 +66,15 @@ public:
|
||||
~QNetworkAccessCacheBackend();
|
||||
|
||||
void open() override;
|
||||
void closeDownstreamChannel() override;
|
||||
void closeUpstreamChannel();
|
||||
void close() override;
|
||||
bool start() override;
|
||||
|
||||
void upstreamReadyRead();
|
||||
void downstreamReadyWrite() override;
|
||||
qint64 bytesAvailable() const override;
|
||||
qint64 read(char *data, qint64 maxlen) override;
|
||||
|
||||
private:
|
||||
bool sendCacheContents();
|
||||
|
||||
QIODevice *device = nullptr;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -80,8 +80,13 @@ QNetworkAccessDebugPipeBackendFactory::create(QNetworkAccessManager::Operation o
|
||||
}
|
||||
|
||||
QNetworkAccessDebugPipeBackend::QNetworkAccessDebugPipeBackend()
|
||||
: bareProtocol(false), hasUploadFinished(false), hasDownloadFinished(false),
|
||||
hasEverythingFinished(false), bytesDownloaded(0), bytesUploaded(0)
|
||||
: QNetworkAccessBackend(QNetworkAccessBackend::TargetType::Networked),
|
||||
bareProtocol(false),
|
||||
hasUploadFinished(false),
|
||||
hasDownloadFinished(false),
|
||||
hasEverythingFinished(false),
|
||||
bytesDownloaded(0),
|
||||
bytesUploaded(0)
|
||||
{
|
||||
}
|
||||
|
||||
@ -108,19 +113,36 @@ void QNetworkAccessDebugPipeBackend::open()
|
||||
|
||||
if (operation() == QNetworkAccessManager::PutOperation) {
|
||||
createUploadByteDevice();
|
||||
QObject::connect(uploadByteDevice.data(), SIGNAL(readyRead()), this, SLOT(uploadReadyReadSlot()));
|
||||
QObject::connect(uploadByteDevice(), SIGNAL(readyRead()), this,
|
||||
SLOT(uploadReadyReadSlot()));
|
||||
QMetaObject::invokeMethod(this, "uploadReadyReadSlot", Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
void QNetworkAccessDebugPipeBackend::socketReadyRead()
|
||||
{
|
||||
pushFromSocketToDownstream();
|
||||
readyRead();
|
||||
}
|
||||
|
||||
void QNetworkAccessDebugPipeBackend::downstreamReadyWrite()
|
||||
qint64 QNetworkAccessDebugPipeBackend::read(char *data, qint64 maxlen)
|
||||
{
|
||||
pushFromSocketToDownstream();
|
||||
qint64 haveRead = socket.read(data, maxlen);
|
||||
|
||||
if (haveRead == -1) {
|
||||
hasDownloadFinished = true;
|
||||
// this ensures a good last downloadProgress is emitted
|
||||
setHeader(QNetworkRequest::ContentLengthHeader, QVariant());
|
||||
possiblyFinish();
|
||||
return haveRead;
|
||||
}
|
||||
|
||||
bytesDownloaded += haveRead;
|
||||
return haveRead;
|
||||
}
|
||||
|
||||
qint64 QNetworkAccessDebugPipeBackend::bytesAvailable() const
|
||||
{
|
||||
return socket.bytesAvailable();
|
||||
}
|
||||
|
||||
void QNetworkAccessDebugPipeBackend::socketBytesWritten(qint64)
|
||||
@ -133,42 +155,6 @@ void QNetworkAccessDebugPipeBackend::uploadReadyReadSlot()
|
||||
pushFromUpstreamToSocket();
|
||||
}
|
||||
|
||||
void QNetworkAccessDebugPipeBackend::pushFromSocketToDownstream()
|
||||
{
|
||||
QByteArray buffer;
|
||||
|
||||
if (socket.state() == QAbstractSocket::ConnectingState) {
|
||||
return;
|
||||
}
|
||||
|
||||
forever {
|
||||
if (hasDownloadFinished)
|
||||
return;
|
||||
|
||||
buffer.resize(ReadBufferSize);
|
||||
qint64 haveRead = socket.read(buffer.data(), ReadBufferSize);
|
||||
|
||||
if (haveRead == -1) {
|
||||
hasDownloadFinished = true;
|
||||
// this ensures a good last downloadProgress is emitted
|
||||
setHeader(QNetworkRequest::ContentLengthHeader, QVariant());
|
||||
possiblyFinish();
|
||||
break;
|
||||
} else if (haveRead == 0) {
|
||||
break;
|
||||
} else {
|
||||
// have read something
|
||||
buffer.resize(haveRead);
|
||||
bytesDownloaded += haveRead;
|
||||
|
||||
QByteDataBuffer list;
|
||||
list.append(buffer);
|
||||
buffer.clear(); // important because of implicit sharing!
|
||||
writeDownstreamData(list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QNetworkAccessDebugPipeBackend::pushFromUpstreamToSocket()
|
||||
{
|
||||
// FIXME
|
||||
@ -180,20 +166,20 @@ void QNetworkAccessDebugPipeBackend::pushFromUpstreamToSocket()
|
||||
if (socket.bytesToWrite() >= WriteBufferSize)
|
||||
return;
|
||||
|
||||
qint64 haveRead;
|
||||
const char *readPointer = uploadByteDevice->readPointer(WriteBufferSize, haveRead);
|
||||
QByteArray data(WriteBufferSize, Qt::Uninitialized);
|
||||
qint64 haveRead = uploadByteDevice()->peek(data.data(), data.size());
|
||||
if (haveRead == -1) {
|
||||
// EOF
|
||||
hasUploadFinished = true;
|
||||
emitReplyUploadProgress(bytesUploaded, bytesUploaded);
|
||||
possiblyFinish();
|
||||
break;
|
||||
} else if (haveRead == 0 || readPointer == nullptr) {
|
||||
} else if (haveRead == 0) {
|
||||
// nothing to read right now, we will be called again later
|
||||
break;
|
||||
} else {
|
||||
qint64 haveWritten;
|
||||
haveWritten = socket.write(readPointer, haveRead);
|
||||
data.truncate(haveRead);
|
||||
haveWritten = socket.write(std::move(data));
|
||||
|
||||
if (haveWritten < 0) {
|
||||
// write error!
|
||||
@ -203,13 +189,11 @@ void QNetworkAccessDebugPipeBackend::pushFromUpstreamToSocket()
|
||||
finished();
|
||||
return;
|
||||
} else {
|
||||
uploadByteDevice->advanceReadPointer(haveWritten);
|
||||
uploadByteDevice()->skip(haveWritten);
|
||||
bytesUploaded += haveWritten;
|
||||
emitReplyUploadProgress(bytesUploaded, -1);
|
||||
}
|
||||
|
||||
//QCoreApplication::processEvents();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -232,7 +216,7 @@ void QNetworkAccessDebugPipeBackend::possiblyFinish()
|
||||
|
||||
}
|
||||
|
||||
void QNetworkAccessDebugPipeBackend::closeDownstreamChannel()
|
||||
void QNetworkAccessDebugPipeBackend::close()
|
||||
{
|
||||
qWarning("QNetworkAccessDebugPipeBackend::closeDownstreamChannel() %d",operation());;
|
||||
//if (operation() == QNetworkAccessManager::GetOperation)
|
||||
@ -266,11 +250,10 @@ void QNetworkAccessDebugPipeBackend::socketError()
|
||||
|
||||
void QNetworkAccessDebugPipeBackend::socketDisconnected()
|
||||
{
|
||||
pushFromSocketToDownstream();
|
||||
|
||||
if (socket.bytesToWrite() == 0) {
|
||||
// normal close
|
||||
} else {
|
||||
readyRead(); // @todo this is odd
|
||||
// abnormal close
|
||||
QString msg = QNetworkAccessDebugPipeBackend::tr("Remote host closed the connection prematurely on %1")
|
||||
.arg(url().toString());
|
||||
|
@ -68,13 +68,13 @@ public:
|
||||
QNetworkAccessDebugPipeBackend();
|
||||
virtual ~QNetworkAccessDebugPipeBackend();
|
||||
|
||||
virtual void open() override;
|
||||
virtual void closeDownstreamChannel() override;
|
||||
void open() override;
|
||||
void close() override;
|
||||
|
||||
virtual void downstreamReadyWrite() override;
|
||||
qint64 read(char *data, qint64 maxlen) override;
|
||||
qint64 bytesAvailable() const override;
|
||||
|
||||
protected:
|
||||
void pushFromSocketToDownstream();
|
||||
void pushFromUpstreamToSocket();
|
||||
void possiblyFinish();
|
||||
|
||||
|
@ -95,8 +95,12 @@ QNetworkAccessFileBackendFactory::create(QNetworkAccessManager::Operation op,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// We pass TargetType::Local even though it's kind of Networked but we're using a QFile to access
|
||||
// the resource so it cannot use proxies anyway
|
||||
QNetworkAccessFileBackend::QNetworkAccessFileBackend()
|
||||
: totalBytes(0), hasUploadFinished(false)
|
||||
: QNetworkAccessBackend(QNetworkAccessBackend::TargetType::Local),
|
||||
totalBytes(0),
|
||||
hasUploadFinished(false)
|
||||
{
|
||||
}
|
||||
|
||||
@ -152,7 +156,7 @@ void QNetworkAccessFileBackend::open()
|
||||
case QNetworkAccessManager::PutOperation:
|
||||
mode = QIODevice::WriteOnly | QIODevice::Truncate;
|
||||
createUploadByteDevice();
|
||||
QObject::connect(uploadByteDevice.data(), SIGNAL(readyRead()), this, SLOT(uploadReadyReadSlot()));
|
||||
QObject::connect(uploadByteDevice(), SIGNAL(readyRead()), this, SLOT(uploadReadyReadSlot()));
|
||||
QMetaObject::invokeMethod(this, "uploadReadyReadSlot", Qt::QueuedConnection);
|
||||
break;
|
||||
default:
|
||||
@ -163,6 +167,8 @@ void QNetworkAccessFileBackend::open()
|
||||
|
||||
mode |= QIODevice::Unbuffered;
|
||||
bool opened = file.open(mode);
|
||||
if (file.isSequential())
|
||||
connect(&file, &QIODevice::readChannelFinished, this, [this]() { finished(); });
|
||||
|
||||
// could we open the file?
|
||||
if (!opened) {
|
||||
@ -186,8 +192,8 @@ void QNetworkAccessFileBackend::uploadReadyReadSlot()
|
||||
return;
|
||||
|
||||
forever {
|
||||
qint64 haveRead;
|
||||
const char *readPointer = uploadByteDevice->readPointer(-1, haveRead);
|
||||
QByteArray data(16 * 1024, Qt::Uninitialized);
|
||||
qint64 haveRead = uploadByteDevice()->peek(data.data(), data.size());
|
||||
if (haveRead == -1) {
|
||||
// EOF
|
||||
hasUploadFinished = true;
|
||||
@ -195,12 +201,13 @@ void QNetworkAccessFileBackend::uploadReadyReadSlot()
|
||||
file.close();
|
||||
finished();
|
||||
break;
|
||||
} else if (haveRead == 0 || readPointer == nullptr) {
|
||||
} else if (haveRead == 0) {
|
||||
// nothing to read right now, we will be called again later
|
||||
break;
|
||||
} else {
|
||||
qint64 haveWritten;
|
||||
haveWritten = file.write(readPointer, haveRead);
|
||||
data.truncate(haveRead);
|
||||
haveWritten = file.write(data);
|
||||
|
||||
if (haveWritten < 0) {
|
||||
// write error!
|
||||
@ -211,7 +218,7 @@ void QNetworkAccessFileBackend::uploadReadyReadSlot()
|
||||
finished();
|
||||
return;
|
||||
} else {
|
||||
uploadByteDevice->advanceReadPointer(haveWritten);
|
||||
uploadByteDevice()->skip(haveWritten);
|
||||
}
|
||||
|
||||
|
||||
@ -220,21 +227,13 @@ void QNetworkAccessFileBackend::uploadReadyReadSlot()
|
||||
}
|
||||
}
|
||||
|
||||
void QNetworkAccessFileBackend::closeDownstreamChannel()
|
||||
void QNetworkAccessFileBackend::close()
|
||||
{
|
||||
if (operation() == QNetworkAccessManager::GetOperation) {
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
|
||||
void QNetworkAccessFileBackend::downstreamReadyWrite()
|
||||
{
|
||||
Q_ASSERT_X(operation() == QNetworkAccessManager::GetOperation, "QNetworkAccessFileBackend",
|
||||
"We're being told to download data but operation isn't GET!");
|
||||
|
||||
readMoreFromFile();
|
||||
}
|
||||
|
||||
bool QNetworkAccessFileBackend::loadFileInfo()
|
||||
{
|
||||
QFileInfo fi(file);
|
||||
@ -254,40 +253,36 @@ bool QNetworkAccessFileBackend::loadFileInfo()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QNetworkAccessFileBackend::readMoreFromFile()
|
||||
qint64 QNetworkAccessFileBackend::bytesAvailable() const
|
||||
{
|
||||
qint64 wantToRead;
|
||||
while ((wantToRead = nextDownstreamBlockSize()) > 0) {
|
||||
// ### FIXME!!
|
||||
// Obtain a pointer from the ringbuffer!
|
||||
// Avoid extra copy
|
||||
QByteArray data;
|
||||
data.reserve(wantToRead);
|
||||
qint64 actuallyRead = file.read(data.data(), wantToRead);
|
||||
if (actuallyRead <= 0) {
|
||||
// EOF or error
|
||||
if (file.error() != QFile::NoError) {
|
||||
QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Read error reading from %1: %2")
|
||||
.arg(url().toString(), file.errorString());
|
||||
error(QNetworkReply::ProtocolFailure, msg);
|
||||
if (operation() != QNetworkAccessManager::GetOperation)
|
||||
return 0;
|
||||
return file.bytesAvailable();
|
||||
}
|
||||
|
||||
finished();
|
||||
return false;
|
||||
}
|
||||
qint64 QNetworkAccessFileBackend::read(char *data, qint64 maxlen)
|
||||
{
|
||||
if (operation() != QNetworkAccessManager::GetOperation)
|
||||
return 0;
|
||||
qint64 actuallyRead = file.read(data, maxlen);
|
||||
if (actuallyRead <= 0) {
|
||||
// EOF or error
|
||||
if (file.error() != QFile::NoError) {
|
||||
QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Read error reading from %1: %2")
|
||||
.arg(url().toString(), file.errorString());
|
||||
error(QNetworkReply::ProtocolFailure, msg);
|
||||
|
||||
finished();
|
||||
return true;
|
||||
return -1;
|
||||
}
|
||||
|
||||
data.resize(actuallyRead);
|
||||
totalBytes += actuallyRead;
|
||||
|
||||
QByteDataBuffer list;
|
||||
list.append(data);
|
||||
data.clear(); // important because of implicit sharing!
|
||||
writeDownstreamData(list);
|
||||
finished();
|
||||
return actuallyRead;
|
||||
}
|
||||
return true;
|
||||
if (!file.isSequential() && file.atEnd())
|
||||
finished();
|
||||
totalBytes += actuallyRead;
|
||||
return actuallyRead;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -66,10 +66,11 @@ public:
|
||||
QNetworkAccessFileBackend();
|
||||
virtual ~QNetworkAccessFileBackend();
|
||||
|
||||
virtual void open() override;
|
||||
virtual void closeDownstreamChannel() override;
|
||||
void open() override;
|
||||
void close() override;
|
||||
|
||||
virtual void downstreamReadyWrite() override;
|
||||
qint64 bytesAvailable() const;
|
||||
qint64 read(char *data, qint64 maxlen);
|
||||
|
||||
public slots:
|
||||
void uploadReadyReadSlot();
|
||||
@ -79,7 +80,6 @@ private:
|
||||
bool hasUploadFinished;
|
||||
|
||||
bool loadFileInfo();
|
||||
bool readMoreFromFile();
|
||||
};
|
||||
|
||||
class QNetworkAccessFileBackendFactory: public QNetworkAccessBackendFactory
|
||||
|
@ -60,6 +60,9 @@
|
||||
#include "qnetworkreplydataimpl_p.h"
|
||||
#include "qnetworkreplyfileimpl_p.h"
|
||||
|
||||
#include "qnetworkaccessbackend_p.h"
|
||||
#include "qnetworkreplyimpl_p.h"
|
||||
|
||||
#include "QtCore/qbuffer.h"
|
||||
#include "QtCore/qlist.h"
|
||||
#include "QtCore/qurl.h"
|
||||
@ -77,6 +80,8 @@
|
||||
|
||||
#include <QHostInfo>
|
||||
|
||||
#include <QtCore/private/qfactoryloader_p.h>
|
||||
|
||||
#if defined(Q_OS_MACOS)
|
||||
#include <CoreServices/CoreServices.h>
|
||||
#include <SystemConfiguration/SystemConfiguration.h>
|
||||
@ -96,6 +101,9 @@ Q_GLOBAL_STATIC(QNetworkAccessFileBackendFactory, fileBackend)
|
||||
Q_GLOBAL_STATIC(QNetworkAccessDebugPipeBackendFactory, debugpipeBackend)
|
||||
#endif
|
||||
|
||||
Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
|
||||
(QNetworkAccessBackendFactory_iid,
|
||||
QLatin1String("/networkaccessbackends")))
|
||||
#if defined(Q_OS_MACOS)
|
||||
bool getProxyAuth(const QString& proxyHostname, const QString &scheme, QString& username, QString& password)
|
||||
{
|
||||
@ -396,6 +404,7 @@ QNetworkAccessManager::QNetworkAccessManager(QObject *parent)
|
||||
: QObject(*new QNetworkAccessManagerPrivate, parent)
|
||||
{
|
||||
ensureInitialized();
|
||||
d_func()->ensureBackendPluginsLoaded();
|
||||
|
||||
qRegisterMetaType<QNetworkReply::NetworkError>();
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
@ -1171,9 +1180,9 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
|
||||
QNetworkReplyImplPrivate *priv = reply->d_func();
|
||||
priv->manager = this;
|
||||
priv->backend = new QNetworkAccessCacheBackend();
|
||||
priv->backend->manager = this->d_func();
|
||||
priv->backend->setManagerPrivate(this->d_func());
|
||||
priv->backend->setParent(reply);
|
||||
priv->backend->reply = priv;
|
||||
priv->backend->setReplyPrivate(priv);
|
||||
priv->setup(op, req, outgoingData);
|
||||
return reply;
|
||||
}
|
||||
@ -1251,7 +1260,7 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
|
||||
|
||||
if (priv->backend) {
|
||||
priv->backend->setParent(reply);
|
||||
priv->backend->reply = priv;
|
||||
priv->backend->setReplyPrivate(priv);
|
||||
}
|
||||
|
||||
#ifndef QT_NO_SSL
|
||||
@ -1685,6 +1694,25 @@ QNetworkRequest QNetworkAccessManagerPrivate::prepareMultipart(const QNetworkReq
|
||||
}
|
||||
#endif // QT_CONFIG(http)
|
||||
|
||||
/*!
|
||||
\internal
|
||||
Go through the instances so the factories will be created and
|
||||
register themselves to QNetworkAccessBackendFactoryData
|
||||
*/
|
||||
void QNetworkAccessManagerPrivate::ensureBackendPluginsLoaded()
|
||||
{
|
||||
static QBasicMutex mutex;
|
||||
std::unique_lock locker(mutex);
|
||||
if (!loader())
|
||||
return;
|
||||
#if QT_CONFIG(library)
|
||||
loader->update();
|
||||
#endif
|
||||
int index = 0;
|
||||
while (loader->instance(index))
|
||||
++index;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#include "moc_qnetworkaccessmanager.cpp"
|
||||
|
@ -130,6 +130,8 @@ public:
|
||||
QNetworkRequest prepareMultipart(const QNetworkRequest &request, QHttpMultiPart *multiPart);
|
||||
#endif
|
||||
|
||||
void ensureBackendPluginsLoaded();
|
||||
|
||||
// this is the cache for storing downloaded files
|
||||
QAbstractNetworkCache *networkCache;
|
||||
|
||||
@ -153,8 +155,6 @@ public:
|
||||
// this cache can be used by individual backends to cache e.g. their TCP connections to a server
|
||||
// and use the connections for multiple requests.
|
||||
QNetworkAccessCache objectCache;
|
||||
static inline QNetworkAccessCache *getObjectCache(QNetworkAccessBackend *backend)
|
||||
{ return &backend->manager->objectCache; }
|
||||
|
||||
Q_AUTOTEST_EXPORT static void clearAuthenticationCache(QNetworkAccessManager *manager);
|
||||
Q_AUTOTEST_EXPORT static void clearConnectionCache(QNetworkAccessManager *manager);
|
||||
|
@ -61,6 +61,8 @@
|
||||
#include "qnetworkcookiejar.h"
|
||||
#include "qnetconmonitor_p.h"
|
||||
|
||||
#include "qnetworkreplyimpl_p.h"
|
||||
|
||||
#include <string.h> // for strchr
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
@ -56,7 +56,7 @@ inline QNetworkReplyImplPrivate::QNetworkReplyImplPrivate()
|
||||
copyDevice(nullptr),
|
||||
cacheEnabled(false), cacheSaveDevice(nullptr),
|
||||
notificationHandlingPaused(false),
|
||||
bytesDownloaded(0), lastBytesDownloaded(-1), bytesUploaded(-1),
|
||||
bytesDownloaded(0), bytesUploaded(-1),
|
||||
httpStatusCode(0),
|
||||
state(Idle)
|
||||
, downloadBufferReadPosition(0)
|
||||
@ -124,7 +124,7 @@ void QNetworkReplyImplPrivate::_q_copyReadyRead()
|
||||
// FIXME Optimize to use download buffer if it is a QBuffer.
|
||||
// Needs to be done where sendCacheContents() (?) of HTTP is emitting
|
||||
// metaDataChanged ?
|
||||
|
||||
qint64 lastBytesDownloaded = bytesDownloaded;
|
||||
forever {
|
||||
qint64 bytesToRead = nextDownstreamBlockSize();
|
||||
if (bytesToRead == 0)
|
||||
@ -135,13 +135,11 @@ void QNetworkReplyImplPrivate::_q_copyReadyRead()
|
||||
qint64 bytesActuallyRead = copyDevice->read(buffer.reserve(bytesToRead), bytesToRead);
|
||||
if (bytesActuallyRead == -1) {
|
||||
buffer.chop(bytesToRead);
|
||||
backendNotify(NotifyCopyFinished);
|
||||
break;
|
||||
}
|
||||
buffer.chop(bytesToRead - bytesActuallyRead);
|
||||
|
||||
if (!copyDevice->isSequential() && copyDevice->atEnd()) {
|
||||
backendNotify(NotifyCopyFinished);
|
||||
bytesDownloaded += bytesActuallyRead;
|
||||
break;
|
||||
}
|
||||
@ -154,7 +152,6 @@ void QNetworkReplyImplPrivate::_q_copyReadyRead()
|
||||
return;
|
||||
}
|
||||
|
||||
lastBytesDownloaded = bytesDownloaded;
|
||||
QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
|
||||
pauseNotificationHandling();
|
||||
// emit readyRead before downloadProgress incase this will cause events to be
|
||||
@ -323,21 +320,15 @@ void QNetworkReplyImplPrivate::handleNotifications()
|
||||
return;
|
||||
switch (notification) {
|
||||
case NotifyDownstreamReadyWrite:
|
||||
if (copyDevice)
|
||||
if (copyDevice) {
|
||||
_q_copyReadyRead();
|
||||
else
|
||||
backend->downstreamReadyWrite();
|
||||
} else if (backend) {
|
||||
if (backend->bytesAvailable() > 0)
|
||||
readFromBackend();
|
||||
else if (backend->wantToRead())
|
||||
readFromBackend();
|
||||
}
|
||||
break;
|
||||
|
||||
case NotifyCloseDownstreamChannel:
|
||||
backend->closeDownstreamChannel();
|
||||
break;
|
||||
|
||||
case NotifyCopyFinished: {
|
||||
QIODevice *dev = qExchange(copyDevice, nullptr);
|
||||
backend->copyFinished(dev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -463,7 +454,8 @@ void QNetworkReplyImplPrivate::initCacheSaveDevice()
|
||||
// save the meta data
|
||||
QNetworkCacheMetaData metaData;
|
||||
metaData.setUrl(url);
|
||||
metaData = backend->fetchCacheMetaData(metaData);
|
||||
// @todo @future: fetchCacheMetaData is not currently implemented in any backend, but can be useful again in the future
|
||||
// metaData = backend->fetchCacheMetaData(metaData);
|
||||
|
||||
// save the redirect request also in the cache
|
||||
QVariant redirectionTarget = q->attribute(QNetworkRequest::RedirectionTargetAttribute);
|
||||
@ -512,7 +504,6 @@ void QNetworkReplyImplPrivate::appendDownstreamData(QByteDataBuffer &data)
|
||||
data.clear();
|
||||
|
||||
bytesDownloaded += bytesWritten;
|
||||
lastBytesDownloaded = bytesDownloaded;
|
||||
|
||||
appendDownstreamDataSignalEmissions();
|
||||
}
|
||||
@ -562,16 +553,6 @@ void QNetworkReplyImplPrivate::appendDownstreamData(QIODevice *data)
|
||||
_q_copyReadyRead();
|
||||
}
|
||||
|
||||
void QNetworkReplyImplPrivate::appendDownstreamData(const QByteArray &data)
|
||||
{
|
||||
Q_UNUSED(data);
|
||||
// TODO implement
|
||||
|
||||
// TODO call
|
||||
|
||||
qFatal("QNetworkReplyImplPrivate::appendDownstreamData not implemented");
|
||||
}
|
||||
|
||||
static void downloadBufferDeleter(char *ptr)
|
||||
{
|
||||
delete[] ptr;
|
||||
@ -620,16 +601,11 @@ void QNetworkReplyImplPrivate::appendDownstreamDataDownloadBuffer(qint64 bytesRe
|
||||
initCacheSaveDevice();
|
||||
|
||||
if (cacheSaveDevice && bytesReceived == bytesTotal) {
|
||||
// if (lastBytesDownloaded == -1)
|
||||
// lastBytesDownloaded = 0;
|
||||
// cacheSaveDevice->write(downloadBuffer + lastBytesDownloaded, bytesReceived - lastBytesDownloaded);
|
||||
|
||||
// Write everything in one go if we use a download buffer. might be more performant.
|
||||
cacheSaveDevice->write(downloadBuffer, bytesTotal);
|
||||
}
|
||||
|
||||
bytesDownloaded = bytesReceived;
|
||||
lastBytesDownloaded = bytesReceived;
|
||||
|
||||
downloadBufferCurrentSize = bytesReceived;
|
||||
|
||||
@ -748,6 +724,30 @@ void QNetworkReplyImplPrivate::sslErrors(const QList<QSslError> &errors)
|
||||
#endif
|
||||
}
|
||||
|
||||
void QNetworkReplyImplPrivate::readFromBackend()
|
||||
{
|
||||
Q_Q(QNetworkReplyImpl);
|
||||
if (!backend)
|
||||
return;
|
||||
|
||||
if (backend->ioFeatures() & QNetworkAccessBackend::IOFeature::ZeroCopy) {
|
||||
if (backend->bytesAvailable())
|
||||
emit q->readyRead();
|
||||
} else {
|
||||
while (backend->bytesAvailable()
|
||||
&& (!readBufferMaxSize || buffer.size() < readBufferMaxSize)) {
|
||||
qint64 toRead = qMin(nextDownstreamBlockSize(), backend->bytesAvailable());
|
||||
if (toRead == 0)
|
||||
toRead = 16 * 1024; // try to read something
|
||||
char *data = buffer.reserve(toRead);
|
||||
qint64 bytesRead = backend->read(data, toRead);
|
||||
Q_ASSERT(bytesRead <= toRead);
|
||||
buffer.chop(toRead - bytesRead);
|
||||
emit q->readyRead();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QNetworkReplyImpl::QNetworkReplyImpl(QObject *parent)
|
||||
: QNetworkReply(*new QNetworkReplyImplPrivate, parent)
|
||||
{
|
||||
@ -799,7 +799,7 @@ void QNetworkReplyImpl::close()
|
||||
|
||||
// stop the download
|
||||
if (d->backend)
|
||||
d->backend->closeDownstreamChannel();
|
||||
d->backend->close();
|
||||
if (d->copyDevice)
|
||||
disconnect(d->copyDevice, nullptr, this, nullptr);
|
||||
|
||||
@ -823,21 +823,16 @@ qint64 QNetworkReplyImpl::bytesAvailable() const
|
||||
qint64 maxAvail = d->downloadBufferCurrentSize - d->downloadBufferReadPosition;
|
||||
return QNetworkReply::bytesAvailable() + maxAvail;
|
||||
}
|
||||
|
||||
return QNetworkReply::bytesAvailable();
|
||||
return QNetworkReply::bytesAvailable() + (d->backend ? d->backend->bytesAvailable() : 0);
|
||||
}
|
||||
|
||||
void QNetworkReplyImpl::setReadBufferSize(qint64 size)
|
||||
{
|
||||
Q_D(QNetworkReplyImpl);
|
||||
if (size > d->readBufferMaxSize &&
|
||||
size > d->buffer.size())
|
||||
d->backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
|
||||
|
||||
qint64 oldMaxSize = d->readBufferMaxSize;
|
||||
QNetworkReply::setReadBufferSize(size);
|
||||
|
||||
if (d->backend)
|
||||
d->backend->setDownstreamLimited(d->readBufferMaxSize > 0);
|
||||
if (size > oldMaxSize && size > d->buffer.size())
|
||||
d->readFromBackend();
|
||||
}
|
||||
|
||||
#ifndef QT_NO_SSL
|
||||
@ -845,7 +840,7 @@ void QNetworkReplyImpl::sslConfigurationImplementation(QSslConfiguration &config
|
||||
{
|
||||
Q_D(const QNetworkReplyImpl);
|
||||
if (d->backend)
|
||||
d->backend->fetchSslConfiguration(configuration);
|
||||
configuration = d->backend->sslConfiguration();
|
||||
}
|
||||
|
||||
void QNetworkReplyImpl::setSslConfigurationImplementation(const QSslConfiguration &config)
|
||||
@ -877,6 +872,35 @@ qint64 QNetworkReplyImpl::readData(char *data, qint64 maxlen)
|
||||
{
|
||||
Q_D(QNetworkReplyImpl);
|
||||
|
||||
if (d->backend
|
||||
&& d->backend->ioFeatures().testFlag(QNetworkAccessBackend::IOFeature::ZeroCopy)) {
|
||||
qint64 bytesRead = 0;
|
||||
while (d->backend->bytesAvailable()) {
|
||||
QByteArrayView view = d->backend->readPointer();
|
||||
if (view.size()) {
|
||||
qint64 bytesToCopy = qMin(qint64(view.size()), maxlen - bytesRead);
|
||||
memcpy(data + bytesRead, view.data(), bytesToCopy); // from zero to one copy
|
||||
|
||||
// We might have to cache this
|
||||
if (d->cacheEnabled && !d->cacheSaveDevice)
|
||||
d->initCacheSaveDevice();
|
||||
if (d->cacheEnabled && d->cacheSaveDevice)
|
||||
d->cacheSaveDevice->write(view.data(), view.size());
|
||||
|
||||
bytesRead += bytesToCopy;
|
||||
d->backend->advanceReadPointer(bytesToCopy);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
QVariant totalSize = d->cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
|
||||
emit downloadProgress(bytesRead,
|
||||
totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
|
||||
return bytesRead;
|
||||
} else if (d->backend && d->backend->bytesAvailable()) {
|
||||
return d->backend->read(data, maxlen);
|
||||
}
|
||||
|
||||
// Special case code if we have the "zero copy" download buffer
|
||||
if (d->downloadBuffer) {
|
||||
qint64 maxAvail = qMin<qint64>(d->downloadBufferCurrentSize - d->downloadBufferReadPosition, maxlen);
|
||||
|
@ -106,8 +106,6 @@ class QNetworkReplyImplPrivate: public QNetworkReplyPrivate
|
||||
public:
|
||||
enum InternalNotifications {
|
||||
NotifyDownstreamReadyWrite,
|
||||
NotifyCloseDownstreamChannel,
|
||||
NotifyCopyFinished
|
||||
};
|
||||
|
||||
QNetworkReplyImplPrivate();
|
||||
@ -139,7 +137,6 @@ public:
|
||||
void appendDownstreamDataSignalEmissions();
|
||||
void appendDownstreamData(QByteDataBuffer &data);
|
||||
void appendDownstreamData(QIODevice *data);
|
||||
void appendDownstreamData(const QByteArray &data);
|
||||
|
||||
void setDownloadBuffer(QSharedPointer<char> sp, qint64 size);
|
||||
char* getDownloadBuffer(qint64 size);
|
||||
@ -152,6 +149,8 @@ public:
|
||||
void encrypted();
|
||||
void sslErrors(const QList<QSslError> &errors);
|
||||
|
||||
void readFromBackend();
|
||||
|
||||
QNetworkAccessBackend *backend;
|
||||
QIODevice *outgoingData;
|
||||
QSharedPointer<QRingBuffer> outgoingDataBuffer;
|
||||
@ -171,7 +170,6 @@ public:
|
||||
#endif
|
||||
|
||||
qint64 bytesDownloaded;
|
||||
qint64 lastBytesDownloaded;
|
||||
qint64 bytesUploaded;
|
||||
|
||||
QString httpReasonPhrase;
|
||||
|
@ -14,6 +14,9 @@ msvc:equals(QT_ARCH, i386): QMAKE_LFLAGS += /BASE:0x64000000
|
||||
|
||||
QMAKE_DOCS = $$PWD/doc/qtnetwork.qdocconf
|
||||
|
||||
MODULE_PLUGIN_TYPES = \
|
||||
networkaccessbackends
|
||||
|
||||
include(access/access.pri)
|
||||
include(kernel/kernel.pri)
|
||||
include(socket/socket.pri)
|
||||
|
Loading…
Reference in New Issue
Block a user