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:
Mårten Nordheim 2020-06-23 17:07:37 +02:00
parent f7f79483e7
commit bba0bdb35c
17 changed files with 905 additions and 573 deletions

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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());

View File

@ -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();

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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);

View File

@ -61,6 +61,8 @@
#include "qnetworkcookiejar.h"
#include "qnetconmonitor_p.h"
#include "qnetworkreplyimpl_p.h"
#include <string.h> // for strchr
QT_BEGIN_NAMESPACE

View File

@ -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);

View File

@ -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;

View File

@ -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)