summaryrefslogtreecommitdiffstats
path: root/src/network/access
diff options
context:
space:
mode:
Diffstat (limited to 'src/network/access')
-rw-r--r--src/network/access/qnetworkaccessauthenticationmanager_p.h1
-rw-r--r--src/network/access/qnetworkaccessbackend.cpp634
-rw-r--r--src/network/access/qnetworkaccessbackend_p.h229
-rw-r--r--src/network/access/qnetworkaccesscachebackend.cpp27
-rw-r--r--src/network/access/qnetworkaccesscachebackend_p.h9
-rw-r--r--src/network/access/qnetworkaccessdebugpipebackend.cpp89
-rw-r--r--src/network/access/qnetworkaccessdebugpipebackend_p.h8
-rw-r--r--src/network/access/qnetworkaccessfilebackend.cpp81
-rw-r--r--src/network/access/qnetworkaccessfilebackend_p.h8
-rw-r--r--src/network/access/qnetworkaccessmanager.cpp34
-rw-r--r--src/network/access/qnetworkaccessmanager_p.h4
-rw-r--r--src/network/access/qnetworkreplyhttpimpl.cpp2
-rw-r--r--src/network/access/qnetworkreplyimpl.cpp116
-rw-r--r--src/network/access/qnetworkreplyimpl_p.h6
14 files changed, 791 insertions, 457 deletions
diff --git a/src/network/access/qnetworkaccessauthenticationmanager_p.h b/src/network/access/qnetworkaccessauthenticationmanager_p.h
index 361b17b594..bf57dffbb0 100644
--- a/src/network/access/qnetworkaccessauthenticationmanager_p.h
+++ b/src/network/access/qnetworkaccessauthenticationmanager_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"
diff --git a/src/network/access/qnetworkaccessbackend.cpp b/src/network/access/qnetworkaccessbackend.cpp
index 8013785cc1..f7e29340d9 100644
--- a/src/network/access/qnetworkaccessbackend.cpp
+++ b/src/network/access/qnetworkaccessbackend.cpp
@@ -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()
-{
- QMutexLocker locker(&factoryData()->mutex);
- factoryData()->append(this);
-}
-
-QNetworkAccessBackendFactory::~QNetworkAccessBackendFactory()
+class QNetworkAccessBackendPrivate : public QObjectPrivate
{
- if (QNetworkAccessBackendFactoryData::valid.loadRelaxed()) {
- QMutexLocker locker(&factoryData()->mutex);
- factoryData()->removeAll(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;
+
+ 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,260 +125,619 @@ 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
+
+ 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.
+
+ \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.
+*/
- // We want signal emissions only for normal asynchronous uploads
- if (!isSynchronous())
- connect(uploadByteDevice.data(), SIGNAL(readProgress(qint64,qint64)), this, SLOT(emitReplyUploadProgress(qint64,qint64)));
+/*!
+ \enum QNetworkAccessBackend::SecurityFeature
- return uploadByteDevice.data();
-}
+ 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.
+
+ \value None
+ No specific features are claimed to be supported.
+ \value TLS
+ The plugin supports and expects to use TLS.
+*/
+
+/*!
+ \enum QNetworkAccessBackend::IOFeature
+
+ Use the values in this enum to specify what type of IO
+ features the plugin may utilize.
+
+ \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
+*/
+
+/*!
+ 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.
-// 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)
+ \sa TargetType, SecurityFeature, IOFeature
+*/
+QNetworkAccessBackend::QNetworkAccessBackend(TargetTypes targetTypes,
+ SecurityFeatures securityFeatures,
+ IOFeatures ioFeatures)
+ : QObject(*(new QNetworkAccessBackendPrivate), nullptr)
{
- if (reply->isFinished)
- return;
- reply->emitUploadProgress(bytesSent, bytesTotal);
+ Q_D(QNetworkAccessBackend);
+ d->m_targetTypes = targetTypes;
+ d->m_securityFeatures = securityFeatures;
+ d->m_ioFeatures = ioFeatures;
}
-QNetworkAccessBackend::QNetworkAccessBackend()
- : manager(nullptr)
- , reply(nullptr)
- , synchronous(false)
+/*!
+ \overload
+*/
+QNetworkAccessBackend::QNetworkAccessBackend(TargetTypes targetTypes)
+ : QNetworkAccessBackend(targetTypes, SecurityFeature::None, IOFeature::None)
{
}
-QNetworkAccessBackend::~QNetworkAccessBackend()
+/*!
+ \overload
+*/
+QNetworkAccessBackend::QNetworkAccessBackend(TargetTypes targetTypes,
+ SecurityFeatures securityFeatures)
+ : QNetworkAccessBackend(targetTypes, securityFeatures, IOFeature::None)
{
}
-void QNetworkAccessBackend::downstreamReadyWrite()
+/*!
+ \overload
+*/
+QNetworkAccessBackend::QNetworkAccessBackend(TargetTypes targetTypes, IOFeatures ioFeatures)
+ : QNetworkAccessBackend(targetTypes, SecurityFeature::None, ioFeatures)
{
- // do nothing
}
-void QNetworkAccessBackend::setDownstreamLimited(bool b)
+/*!
+ 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
{
- Q_UNUSED(b);
- // do nothing
+ return d_func()->m_securityFeatures;
}
-void QNetworkAccessBackend::copyFinished(QIODevice *)
+/*!
+ Returns the TargetTypes that the backend claims to target.
+
+ \sa TargetType
+*/
+QNetworkAccessBackend::TargetTypes QNetworkAccessBackend::targetTypes() const noexcept
{
- // do nothing
+ return d_func()->m_targetTypes;
}
-void QNetworkAccessBackend::ignoreSslErrors()
+/*!
+ Returns the I/O features that the backend claims to support.
+
+ \sa IOFeature
+*/
+QNetworkAccessBackend::IOFeatures QNetworkAccessBackend::ioFeatures() const noexcept
{
- // do nothing
+ return d_func()->m_ioFeatures;
}
-void QNetworkAccessBackend::ignoreSslErrors(const QList<QSslError> &errors)
+/*!
+ Prepares the backend and calls open().
+ E.g. for TargetType::Networked it will prepare proxyList().
+
+ \sa TargetType, targetTypes
+*/
+bool QNetworkAccessBackend::start()
{
- Q_UNUSED(errors);
- // do nothing
+ Q_D(QNetworkAccessBackend);
+#ifndef QT_NO_NETWORKPROXY
+ if (targetTypes() & QNetworkAccessBackend::TargetType::Networked)
+ d->m_reply->proxyList = d->m_manager->queryProxy(QNetworkProxyQuery(url()));
+#endif
+
+ // now start the request
+ open();
+ return true;
}
-void QNetworkAccessBackend::fetchSslConfiguration(QSslConfiguration &) const
+#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)
{
- // do nothing
+ Q_UNUSED(configuration);
+ if (securityFeatures() & SecurityFeature::TLS) {
+ qWarning("Backend (%s) claiming to use TLS hasn't overridden setSslConfiguration.",
+ metaObject()->className());
+ }
}
-void QNetworkAccessBackend::setSslConfiguration(const QSslConfiguration &)
+/*!
+ Override this and return the QSslConfiguration used if you
+ have the TLS security feature
+
+ \sa SecurityFeature, securityFeatures
+*/
+QSslConfiguration QNetworkAccessBackend::sslConfiguration() const
{
- // do nothing
+ if (securityFeatures() & SecurityFeature::TLS) {
+ qWarning("Backend (%s) claiming to use TLS hasn't overridden sslConfiguration.",
+ metaObject()->className());
+ }
+ return {};
}
+#endif
-QNetworkCacheMetaData QNetworkAccessBackend::fetchCacheMetaData(const QNetworkCacheMetaData &) const
+/*!
+ 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()
{
- return QNetworkCacheMetaData();
+ if (securityFeatures() & SecurityFeature::TLS) {
+ qWarning("Backend (%s) claiming to use TLS hasn't overridden ignoreSslErrors.",
+ metaObject()->className());
+ }
}
-QNetworkAccessManager::Operation QNetworkAccessBackend::operation() const
+/*!
+ 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)
{
- return reply->operation;
+ Q_UNUSED(errors);
+ if (securityFeatures() & SecurityFeature::TLS) {
+ qWarning("Backend (%s) claiming to use TLS hasn't overridden ignoreSslErrors.",
+ metaObject()->className());
+ }
}
-QNetworkRequest QNetworkAccessBackend::request() const
+/*!
+ 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()
{
- return reply->request;
+ if (ioFeatures() & IOFeature::ZeroCopy) {
+ qWarning("Backend (%s) claiming to support ZeroCopy hasn't overridden readPointer.",
+ metaObject()->className());
+ }
+ return {};
}
-#ifndef QT_NO_NETWORKPROXY
-QList<QNetworkProxy> QNetworkAccessBackend::proxyList() const
+/*!
+ 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)
{
- return reply->proxyList;
+ Q_UNUSED(distance);
+ if (ioFeatures() & IOFeature::ZeroCopy) {
+ qWarning("Backend (%s) claiming to support ZeroCopy hasn't overridden advanceReadPointer.",
+ metaObject()->className());
+ }
}
-#endif
-QAbstractNetworkCache *QNetworkAccessBackend::networkCache() const
+/*!
+ 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)
{
- if (!manager)
- return nullptr;
- return manager->networkCache;
+ 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;
}
-void QNetworkAccessBackend::setCachingEnabled(bool enable)
+/*!
+ 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()
{
- reply->setCachingEnabled(enable);
+ // Base implementation does nothing
+ return false;
}
-bool QNetworkAccessBackend::isCachingEnabled() const
+#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
{
- return reply->isCachingEnabled();
+ Q_ASSERT(targetTypes() & TargetType::Networked);
+ return d_func()->m_reply->proxyList;
}
+#endif
-qint64 QNetworkAccessBackend::nextDownstreamBlockSize() const
+/*!
+ Returns the current URL of the reply
+*/
+QUrl QNetworkAccessBackend::url() const
{
- return reply->nextDownstreamBlockSize();
+ return d_func()->m_reply->url;
}
-void QNetworkAccessBackend::writeDownstreamData(QByteDataBuffer &list)
+/*!
+ 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)
{
- reply->appendDownstreamData(list);
+ d_func()->m_reply->url = url;
}
-void QNetworkAccessBackend::writeDownstreamData(QIODevice *data)
+/*!
+ 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
{
- reply->appendDownstreamData(data);
+ return d_func()->m_reply->cookedHeaders.value(header);
}
-// not actually appending data, it was already written to the user buffer
-void QNetworkAccessBackend::writeDownstreamDataDownloadBuffer(qint64 bytesReceived, qint64 bytesTotal)
+/*!
+ 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)
{
- reply->appendDownstreamDataDownloadBuffer(bytesReceived, bytesTotal);
+ d_func()->m_reply->setCookedHeader(header, value);
}
-char* QNetworkAccessBackend::getDownloadBuffer(qint64 size)
+/*!
+ 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 reply->getDownloadBuffer(size);
+ return d_func()->m_reply->q_func()->rawHeader(header);
}
-QVariant QNetworkAccessBackend::header(QNetworkRequest::KnownHeaders header) const
+/*!
+ 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)
{
- return reply->q_func()->header(header);
+ d_func()->m_reply->setRawHeader(header, value);
}
-void QNetworkAccessBackend::setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value)
+/*!
+ Returns the operation which was requested when calling
+ QNetworkAccessManager.
+*/
+QNetworkAccessManager::Operation QNetworkAccessBackend::operation() const
{
- reply->setCookedHeader(header, value);
+ return d_func()->m_reply->operation;
}
-bool QNetworkAccessBackend::hasRawHeader(const QByteArray &headerName) const
+/*!
+ 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 reply->q_func()->hasRawHeader(headerName);
+ return d_func()->m_canCache;
}
-QByteArray QNetworkAccessBackend::rawHeader(const QByteArray &headerName) const
+/*!
+ 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)
{
- return reply->q_func()->rawHeader(headerName);
+ d_func()->m_canCache = canCache;
}
-QList<QByteArray> QNetworkAccessBackend::rawHeaderList() const
+/*!
+ 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)
{
- return reply->q_func()->rawHeaderList();
+ Q_D(QNetworkAccessBackend);
+ if (value.isValid())
+ d->m_reply->attributes.insert(attribute, value);
+ else
+ d->m_reply->attributes.remove(attribute);
}
-void QNetworkAccessBackend::setRawHeader(const QByteArray &headerName, const QByteArray &headerValue)
+/*!
+ 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()
{
- reply->setRawHeader(headerName, headerValue);
+ 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;
}
-QVariant QNetworkAccessBackend::attribute(QNetworkRequest::Attribute code) const
+/*!
+ 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 reply->q_func()->attribute(code);
+ return d_func()->wrappedUploadByteDevice;
}
-void QNetworkAccessBackend::setAttribute(QNetworkRequest::Attribute code, const QVariant &value)
+/*!
+ \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
{
- if (value.isValid())
- reply->attributes.insert(code, value);
- else
- reply->attributes.remove(code);
+ return d_func()->m_isSynchronous;
}
-QUrl QNetworkAccessBackend::url() const
+
+/*!
+ \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)
{
- return reply->url;
+ if ((ioFeatures() & IOFeature::SupportsSynchronousMode) == 0)
+ return;
+ d_func()->m_isSynchronous = synchronous;
}
-void QNetworkAccessBackend::setUrl(const QUrl &url)
+/*!
+ Call this slot when you have more data available to notify
+ the backend that we can attempt to read again.
+*/
+void QNetworkAccessBackend::readyRead()
{
- reply->url = url;
+ 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()
{
- reply->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)
{
- reply->error(code, 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)
{
- manager->proxyAuthenticationRequired(QUrl(), proxy, synchronous, authenticator, &reply->lastProxyAuthentication);
+ 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)
{
- manager->authenticationRequired(authenticator, reply->q_func(), synchronous, reply->url, &reply->urlForLastAuthentication);
+ 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()
{
- reply->metaDataChanged();
+ d_func()->m_reply->metaDataChanged();
}
-void QNetworkAccessBackend::redirectionRequested(const QUrl &target)
+/*!
+ Call this slot if, when connecting to the resource, a redirect
+ to \a destination was requested.
+*/
+void QNetworkAccessBackend::redirectionRequested(const QUrl &destination)
{
- reply->redirectionRequested(target);
+ d_func()->m_reply->redirectionRequested(destination);
}
-void QNetworkAccessBackend::encrypted()
+/*!
+ \internal
+*/
+void QNetworkAccessBackend::setReplyPrivate(QNetworkReplyImplPrivate *reply)
{
-#ifndef QT_NO_SSL
- reply->encrypted();
-#endif
+ d_func()->m_reply = reply;
}
-void QNetworkAccessBackend::sslErrors(const QList<QSslError> &errors)
+/*!
+ \internal
+*/
+void QNetworkAccessBackend::setManagerPrivate(QNetworkAccessManagerPrivate *manager)
{
-#ifndef QT_NO_SSL
- reply->sslErrors(errors);
-#else
- Q_UNUSED(errors);
-#endif
+ d_func()->m_manager = manager;
}
/*!
- 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.
+ Returns the network cache object that was available when the
+ request was started. Returns \c nullptr if none was available.
*/
-bool QNetworkAccessBackend::start()
+QAbstractNetworkCache *QNetworkAccessBackend::networkCache() const
{
-#ifndef QT_NO_NETWORKPROXY
- reply->proxyList = manager->queryProxy(QNetworkProxyQuery(url()));
-#endif
+ return d_func()->m_manager->networkCache;
+}
- // now start the request
- open();
- return true;
+// -- QNetworkAccessBackendFactory
+/*!
+ Constructs QNetworkAccessBackendFactory
+*/
+QNetworkAccessBackendFactory::QNetworkAccessBackendFactory()
+{
+ if (factoryData())
+ factoryData->append(this);
}
+/*!
+ Destructs QNetworkAccessBackendFactory
+*/
+QNetworkAccessBackendFactory::~QNetworkAccessBackendFactory() = default;
+
QT_END_NAMESPACE
diff --git a/src/network/access/qnetworkaccessbackend_p.h b/src/network/access/qnetworkaccessbackend_p.h
index b18f30c287..0f7b21684a 100644
--- a/src/network/access/qnetworkaccessbackend_p.h
+++ b/src/network/access/qnetworkaccessbackend_p.h
@@ -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/private/qtnetworkglobal_p.h>
-#include "qnetworkreplyimpl_p.h"
-#include "QtCore/qobject.h"
-Q_MOC_INCLUDE(<QAuthenticator>)
-Q_MOC_INCLUDE(<QtNetwork/QSslError>)
+#include <QtNetwork/qtnetworkglobal.h>
-QT_BEGIN_NAMESPACE
+#include <QtNetwork/qnetworkrequest.h>
+#include <QtNetwork/qnetworkaccessmanager.h>
+#include <QtNetwork/qnetworkreply.h>
-class QNetworkProxy;
-class QNetworkProxyQuery;
-class QNetworkRequest;
-class QStringList;
-class QUrl;
-class QSslConfiguration;
+#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 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;
- virtual void open() = 0;
- virtual bool start();
- virtual void closeDownstreamChannel() = 0;
+ inline bool needsResetableUploadData() const noexcept
+ {
+ return ioFeatures() & IOFeature::NeedResetableUpload;
+ }
- // slot-like:
- virtual void downstreamReadyWrite();
- virtual void setDownstreamLimited(bool b);
- virtual void copyFinished(QIODevice *);
+ virtual bool start();
+ 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);
-
- // attributes:
- QVariant attribute(QNetworkRequest::Attribute code) const;
- void setAttribute(QNetworkRequest::Attribute code, const QVariant &value);
-
- bool isSynchronous() { return synchronous; }
- void setSynchronous(bool sync) { synchronous = sync; }
-
- // 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();
+ bool isCachingEnabled() const;
+ void setCachingEnabled(bool canCache);
- // 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);
+ void setAttribute(QNetworkRequest::Attribute attribute, const QVariant &value);
- // not actually appending data, it was already written to the user buffer
- void writeDownstreamDataDownloadBuffer(qint64, qint64);
- char* getDownloadBuffer(qint64);
+ QIODevice *createUploadByteDevice();
+ QIODevice *uploadByteDevice();
- 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;
-
- bool synchronous;
+ void setReplyPrivate(QNetworkReplyImplPrivate *reply);
+ void setManagerPrivate(QNetworkAccessManagerPrivate *manager);
+ bool isSynchronous() const;
+ void setSynchronous(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;
};
-QT_END_NAMESPACE
+#define QNetworkAccessBackendFactory_iid "org.qt-project.Qt.NetworkAccessBackendFactory"
+Q_DECLARE_INTERFACE(QNetworkAccessBackendFactory, QNetworkAccessBackendFactory_iid);
+QT_END_NAMESPACE
#endif
-
diff --git a/src/network/access/qnetworkaccesscachebackend.cpp b/src/network/access/qnetworkaccesscachebackend.cpp
index 1a4ef27dd6..9ed951751a 100644
--- a/src/network/access/qnetworkaccesscachebackend.cpp
+++ b/src/network/access/qnetworkaccesscachebackend.cpp
@@ -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::closeUpstreamChannel()
-{
- Q_ASSERT_X(false, Q_FUNC_INFO, "This function show not have been called!");
-}
+void QNetworkAccessCacheBackend::close() { }
-void QNetworkAccessCacheBackend::upstreamReadyRead()
+qint64 QNetworkAccessCacheBackend::bytesAvailable() const
{
- Q_ASSERT_X(false, Q_FUNC_INFO, "This function show not have been called!");
+ return device ? device->bytesAvailable() : qint64(0);
}
-void QNetworkAccessCacheBackend::downstreamReadyWrite()
+qint64 QNetworkAccessCacheBackend::read(char *data, qint64 maxlen)
{
- 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
diff --git a/src/network/access/qnetworkaccesscachebackend_p.h b/src/network/access/qnetworkaccesscachebackend_p.h
index ceb02946dc..fc26dda454 100644
--- a/src/network/access/qnetworkaccesscachebackend_p.h
+++ b/src/network/access/qnetworkaccesscachebackend_p.h
@@ -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
diff --git a/src/network/access/qnetworkaccessdebugpipebackend.cpp b/src/network/access/qnetworkaccessdebugpipebackend.cpp
index 0029df41fe..51c13b7241 100644
--- a/src/network/access/qnetworkaccessdebugpipebackend.cpp
+++ b/src/network/access/qnetworkaccessdebugpipebackend.cpp
@@ -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,65 +113,46 @@ 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;
}
-void QNetworkAccessDebugPipeBackend::socketBytesWritten(qint64)
+qint64 QNetworkAccessDebugPipeBackend::bytesAvailable() const
{
- pushFromUpstreamToSocket();
+ return socket.bytesAvailable();
}
-void QNetworkAccessDebugPipeBackend::uploadReadyReadSlot()
+void QNetworkAccessDebugPipeBackend::socketBytesWritten(qint64)
{
pushFromUpstreamToSocket();
}
-void QNetworkAccessDebugPipeBackend::pushFromSocketToDownstream()
+void QNetworkAccessDebugPipeBackend::uploadReadyReadSlot()
{
- 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);
- }
- }
+ pushFromUpstreamToSocket();
}
void QNetworkAccessDebugPipeBackend::pushFromUpstreamToSocket()
@@ -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());
diff --git a/src/network/access/qnetworkaccessdebugpipebackend_p.h b/src/network/access/qnetworkaccessdebugpipebackend_p.h
index 761c7055b8..50ef5cea7d 100644
--- a/src/network/access/qnetworkaccessdebugpipebackend_p.h
+++ b/src/network/access/qnetworkaccessdebugpipebackend_p.h
@@ -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();
diff --git a/src/network/access/qnetworkaccessfilebackend.cpp b/src/network/access/qnetworkaccessfilebackend.cpp
index 046a16162a..c5f1826203 100644
--- a/src/network/access/qnetworkaccessfilebackend.cpp
+++ b/src/network/access/qnetworkaccessfilebackend.cpp
@@ -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
diff --git a/src/network/access/qnetworkaccessfilebackend_p.h b/src/network/access/qnetworkaccessfilebackend_p.h
index 2204958ee0..32b2102abc 100644
--- a/src/network/access/qnetworkaccessfilebackend_p.h
+++ b/src/network/access/qnetworkaccessfilebackend_p.h
@@ -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
diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp
index b56da732e2..99c929d6a2 100644
--- a/src/network/access/qnetworkaccessmanager.cpp
+++ b/src/network/access/qnetworkaccessmanager.cpp
@@ -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"
diff --git a/src/network/access/qnetworkaccessmanager_p.h b/src/network/access/qnetworkaccessmanager_p.h
index da9f6fd0bd..5433876385 100644
--- a/src/network/access/qnetworkaccessmanager_p.h
+++ b/src/network/access/qnetworkaccessmanager_p.h
@@ -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);
diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp
index 88969c6483..bdb81ee0b1 100644
--- a/src/network/access/qnetworkreplyhttpimpl.cpp
+++ b/src/network/access/qnetworkreplyhttpimpl.cpp
@@ -61,6 +61,8 @@
#include "qnetworkcookiejar.h"
#include "qnetconmonitor_p.h"
+#include "qnetworkreplyimpl_p.h"
+
#include <string.h> // for strchr
QT_BEGIN_NAMESPACE
diff --git a/src/network/access/qnetworkreplyimpl.cpp b/src/network/access/qnetworkreplyimpl.cpp
index 400cfff8d3..61745155f5 100644
--- a/src/network/access/qnetworkreplyimpl.cpp
+++ b/src/network/access/qnetworkreplyimpl.cpp
@@ -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,22 +320,16 @@ void QNetworkReplyImplPrivate::handleNotifications()
return;
switch (notification) {
case NotifyDownstreamReadyWrite:
- if (copyDevice)
+ if (copyDevice) {
_q_copyReadyRead();
- else
- backend->downstreamReadyWrite();
- break;
-
- case NotifyCloseDownstreamChannel:
- backend->closeDownstreamChannel();
- break;
-
- case NotifyCopyFinished: {
- QIODevice *dev = qExchange(copyDevice, nullptr);
- backend->copyFinished(dev);
+ } else if (backend) {
+ if (backend->bytesAvailable() > 0)
+ readFromBackend();
+ else if (backend->wantToRead())
+ readFromBackend();
+ }
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);
diff --git a/src/network/access/qnetworkreplyimpl_p.h b/src/network/access/qnetworkreplyimpl_p.h
index bb5afe49d6..7d014f9173 100644
--- a/src/network/access/qnetworkreplyimpl_p.h
+++ b/src/network/access/qnetworkreplyimpl_p.h
@@ -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;