diff options
Diffstat (limited to 'src/network/access')
23 files changed, 369 insertions, 163 deletions
diff --git a/src/network/access/qabstractnetworkcache.cpp b/src/network/access/qabstractnetworkcache.cpp index cfc9fe7..3f44059 100644 --- a/src/network/access/qabstractnetworkcache.cpp +++ b/src/network/access/qabstractnetworkcache.cpp @@ -283,6 +283,8 @@ void QNetworkCacheMetaData::setExpirationDate(const QDateTime &dateTime) } /*! + \since 4.6 + Returns all the attributes stored with this cache item. \sa setAttributes(), QNetworkRequest::Attribute @@ -293,6 +295,8 @@ QNetworkCacheMetaData::AttributesMap QNetworkCacheMetaData::attributes() const } /*! + \since 4.6 + Sets all attributes of this cache item to be the map \a attributes. \sa attributes(), QNetworkRequest::setAttribute() diff --git a/src/network/access/qftp.cpp b/src/network/access/qftp.cpp index 6fc0626..acf1a27 100644 --- a/src/network/access/qftp.cpp +++ b/src/network/access/qftp.cpp @@ -871,6 +871,9 @@ void QFtpPI::connected() #if defined(QFTPPI_DEBUG) // qDebug("QFtpPI state: %d [connected()]", state); #endif + // try to improve performance by setting TCP_NODELAY + commandSocket.setSocketOption(QAbstractSocket::LowDelayOption, 1); + emit connectState(QFtp::Connected); } diff --git a/src/network/access/qhttp.cpp b/src/network/access/qhttp.cpp index 91f9f4e..bb72f89 100644 --- a/src/network/access/qhttp.cpp +++ b/src/network/access/qhttp.cpp @@ -513,6 +513,7 @@ public: /*! \class QHttpHeader + \obsolete \brief The QHttpHeader class contains header information for HTTP. \ingroup io @@ -1006,6 +1007,7 @@ public: /*! \class QHttpResponseHeader + \obsolete \brief The QHttpResponseHeader class contains response header information for HTTP. \ingroup io @@ -1151,7 +1153,7 @@ int QHttpResponseHeader::minorVersion() const return d->minVer; } -/*! \reimp +/*! \internal */ bool QHttpResponseHeader::parseLine(const QString &line, int number) { @@ -1210,6 +1212,7 @@ public: /*! \class QHttpRequestHeader + \obsolete \brief The QHttpRequestHeader class contains request header information for HTTP. \ingroup io @@ -1365,7 +1368,7 @@ int QHttpRequestHeader::minorVersion() const return d->minVer; } -/*! \reimp +/*! \internal */ bool QHttpRequestHeader::parseLine(const QString &line, int number) { diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp index 827681f..d67f84b 100644 --- a/src/network/access/qhttpnetworkconnection.cpp +++ b/src/network/access/qhttpnetworkconnection.cpp @@ -177,26 +177,43 @@ bool QHttpNetworkConnectionPrivate::isSocketReading(QAbstractSocket *socket) con return (i != -1 && (channels[i].state & ReadingState)); } +void QHttpNetworkConnectionPrivate::appendUncompressedData(QHttpNetworkReply &reply, QByteArray &qba) +{ + reply.d_func()->responseData.append(qba); + + // clear the original! helps with implicit sharing and + // avoiding memcpy when the user is reading the data + qba.clear(); +} -void QHttpNetworkConnectionPrivate::appendUncompressedData(QHttpNetworkReply &reply, const QByteArray &fragment) +void QHttpNetworkConnectionPrivate::appendUncompressedData(QHttpNetworkReply &reply, QByteDataBuffer &data) { - char *dst = reply.d_func()->responseData.reserve(fragment.size()); - qMemCopy(dst, fragment.constData(), fragment.size()); + reply.d_func()->responseData.append(data); + + // clear the original! helps with implicit sharing and + // avoiding memcpy when the user is reading the data + data.clear(); } -void QHttpNetworkConnectionPrivate::appendCompressedData(QHttpNetworkReply &reply, const QByteArray &fragment) +void QHttpNetworkConnectionPrivate::appendCompressedData(QHttpNetworkReply &reply, QByteDataBuffer &data) { - reply.d_func()->compressedData.append(fragment); + // Work in progress: Later we will directly use a list of QByteArray or a QRingBuffer + // instead of one QByteArray. + for(int i = 0; i < data.bufferCount(); i++) { + QByteArray &byteData = data[i]; + reply.d_func()->compressedData.append(byteData.constData(), byteData.size()); + } + data.clear(); } qint64 QHttpNetworkConnectionPrivate::uncompressedBytesAvailable(const QHttpNetworkReply &reply) const { - return reply.d_func()->responseData.size(); + return reply.d_func()->responseData.byteAmount(); } qint64 QHttpNetworkConnectionPrivate::uncompressedBytesAvailableNextBlock(const QHttpNetworkReply &reply) const { - return reply.d_func()->responseData.nextDataBlockSize(); + return reply.d_func()->responseData.sizeNextBlock(); } qint64 QHttpNetworkConnectionPrivate::compressedBytesAvailable(const QHttpNetworkReply &reply) const @@ -204,21 +221,6 @@ qint64 QHttpNetworkConnectionPrivate::compressedBytesAvailable(const QHttpNetwor return reply.d_func()->compressedData.size(); } -qint64 QHttpNetworkConnectionPrivate::read(QHttpNetworkReply &reply, QByteArray &data, qint64 maxSize) -{ - QRingBuffer *rb = &reply.d_func()->responseData; - if (maxSize == -1 || maxSize >= rb->size()) { - // read the whole data - data = rb->readAll(); - rb->clear(); - } else { - // read only the requested length - data.resize(maxSize); - rb->read(data.data(), maxSize); - } - return data.size(); -} - void QHttpNetworkConnectionPrivate::eraseData(QHttpNetworkReply *reply) { reply->d_func()->compressedData.clear(); @@ -261,7 +263,12 @@ void QHttpNetworkConnectionPrivate::prepareRequest(HttpMessagePair &messagePair) #ifndef QT_NO_NETWORKPROXY } #endif - // set the gzip header + + // If the request had a accept-encoding set, we better not mess + // with it. If it was not set, we announce that we understand gzip + // and remember this fact in request.d->autoDecompress so that + // we can later decompress the HTTP reply if it has such an + // encoding. value = request.headerField("accept-encoding"); if (value.isEmpty()) { #ifndef QT_NO_COMPRESS @@ -335,8 +342,9 @@ bool QHttpNetworkConnectionPrivate::ensureConnection(QAbstractSocket *socket) #ifndef QT_NO_OPENSSL QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket); sslSocket->connectToHostEncrypted(connectHost, connectPort); - if (channels[index].ignoreSSLErrors) + if (channels[index].ignoreAllSslErrors) sslSocket->ignoreSslErrors(); + sslSocket->ignoreSslErrors(channels[index].ignoreSslErrorsList); #else emitReplyError(socket, channels[index].reply, QNetworkReply::ProtocolUnknownError); #endif @@ -403,6 +411,7 @@ bool QHttpNetworkConnectionPrivate::sendRequest(QAbstractSocket *socket) channels[i].bytesTotal = channels[i].request.contentLength(); } else { + socket->flush(); // ### Remove this when pipelining is implemented. We want less TCP packets! channels[i].state = WaitingState; break; } @@ -558,6 +567,8 @@ bool QHttpNetworkConnectionPrivate::expand(QAbstractSocket *socket, QHttpNetwork reply->d_func()->totalProgress += inflated.size(); appendUncompressedData(*reply, inflated); if (shouldEmitSignals(reply)) { + // important: At the point of this readyRead(), inflated must be cleared, + // else implicit sharing will trigger memcpy when the user is reading data! emit reply->readyRead(); // make sure that the reply is valid if (channels[i].reply != reply) @@ -674,18 +685,19 @@ void QHttpNetworkConnectionPrivate::receiveReply(QAbstractSocket *socket, QHttpN { // use the traditional slower reading (for compressed encoding, chunked encoding, // no content-length etc) - QBuffer fragment; - fragment.open(QIODevice::WriteOnly); - bytes = reply->d_func()->readBody(socket, &fragment); + QByteDataBuffer byteDatas; + bytes = reply->d_func()->readBody(socket, &byteDatas); if (bytes) { if (reply->d_func()->autoDecompress) - appendCompressedData(*reply, fragment.data()); + appendCompressedData(*reply, byteDatas); else - appendUncompressedData(*reply, fragment.data()); + appendUncompressedData(*reply, byteDatas); if (!reply->d_func()->autoDecompress) { - reply->d_func()->totalProgress += fragment.size(); + reply->d_func()->totalProgress += bytes; if (shouldEmitSignals(reply)) { + // important: At the point of this readyRead(), the byteDatas list must be empty, + // else implicit sharing will trigger memcpy when the user is reading data! emit reply->readyRead(); // make sure that the reply is valid if (channels[i].reply != reply) @@ -1189,6 +1201,10 @@ void QHttpNetworkConnectionPrivate::_q_connected() QAbstractSocket *socket = qobject_cast<QAbstractSocket*>(q->sender()); if (!socket) return; // ### error + + // improve performance since we get the request sent by the kernel ASAP + socket->setSocketOption(QAbstractSocket::LowDelayOption, 1); + int i = indexOf(socket); // ### FIXME: if the server closes the connection unexpectedly, we shouldn't send the same broken request again! //channels[i].reconnectAttempts = 2; @@ -1440,15 +1456,32 @@ void QHttpNetworkConnection::ignoreSslErrors(int channel) if (channel == -1) { // ignore for all channels for (int i = 0; i < d->channelCount; ++i) { static_cast<QSslSocket *>(d->channels[i].socket)->ignoreSslErrors(); - d->channels[i].ignoreSSLErrors = true; + d->channels[i].ignoreAllSslErrors = true; } } else { static_cast<QSslSocket *>(d->channels[channel].socket)->ignoreSslErrors(); - d->channels[channel].ignoreSSLErrors = true; + d->channels[channel].ignoreAllSslErrors = true; } } +void QHttpNetworkConnection::ignoreSslErrors(const QList<QSslError> &errors, int channel) +{ + Q_D(QHttpNetworkConnection); + if (!d->encrypt) + return; + + if (channel == -1) { // ignore for all channels + for (int i = 0; i < d->channelCount; ++i) { + static_cast<QSslSocket *>(d->channels[i].socket)->ignoreSslErrors(errors); + d->channels[i].ignoreSslErrorsList = errors; + } + + } else { + static_cast<QSslSocket *>(d->channels[channel].socket)->ignoreSslErrors(errors); + d->channels[channel].ignoreSslErrorsList = errors; + } +} #endif //QT_NO_OPENSSL diff --git a/src/network/access/qhttpnetworkconnection_p.h b/src/network/access/qhttpnetworkconnection_p.h index a0813d4..52a73a7 100644 --- a/src/network/access/qhttpnetworkconnection_p.h +++ b/src/network/access/qhttpnetworkconnection_p.h @@ -79,6 +79,7 @@ QT_BEGIN_NAMESPACE class QHttpNetworkRequest; class QHttpNetworkReply; +class QByteArray; class QHttpNetworkConnectionPrivate; class Q_AUTOTEST_EXPORT QHttpNetworkConnection : public QObject @@ -116,6 +117,7 @@ public: #ifndef QT_NO_OPENSSL void setSslConfiguration(const QSslConfiguration &config); void ignoreSslErrors(int channel = -1); + void ignoreSslErrors(const QList<QSslError> &errors, int channel = -1); Q_SIGNALS: void sslErrors(const QList<QSslError> &errors); @@ -240,13 +242,14 @@ public: QAuthenticator authenticator; QAuthenticator proxyAuthenticator; #ifndef QT_NO_OPENSSL - bool ignoreSSLErrors; + bool ignoreAllSslErrors; + QList<QSslError> ignoreSslErrorsList; #endif Channel() : socket(0), state(IdleState), reply(0), written(0), bytesTotal(0), resendCurrent(false), lastStatus(0), pendingEncrypt(false), reconnectAttempts(2), authMehtod(QAuthenticatorPrivate::None), proxyAuthMehtod(QAuthenticatorPrivate::None) #ifndef QT_NO_OPENSSL - , ignoreSSLErrors(false) + , ignoreAllSslErrors(false) #endif {} }; @@ -255,15 +258,14 @@ public: bool pendingAuthSignal; // there is an incomplete authentication signal bool pendingProxyAuthSignal; // there is an incomplete proxy authentication signal - void appendUncompressedData(QHttpNetworkReply &reply, const QByteArray &fragment); - void appendCompressedData(QHttpNetworkReply &reply, const QByteArray &fragment); + void appendUncompressedData(QHttpNetworkReply &reply, QByteArray &qba); + void appendUncompressedData(QHttpNetworkReply &reply, QByteDataBuffer &data); + void appendCompressedData(QHttpNetworkReply &reply, QByteDataBuffer &data); qint64 uncompressedBytesAvailable(const QHttpNetworkReply &reply) const; qint64 uncompressedBytesAvailableNextBlock(const QHttpNetworkReply &reply) const; qint64 compressedBytesAvailable(const QHttpNetworkReply &reply) const; - qint64 read(QHttpNetworkReply &reply, QByteArray &data, qint64 maxSize); - void emitReplyError(QAbstractSocket *socket, QHttpNetworkReply *reply, QNetworkReply::NetworkError errorCode); bool handleAuthenticateChallenge(QAbstractSocket *socket, QHttpNetworkReply *reply, bool isProxy, bool &resend); void allDone(QAbstractSocket *socket, QHttpNetworkReply *reply); diff --git a/src/network/access/qhttpnetworkreply.cpp b/src/network/access/qhttpnetworkreply.cpp index ef4a865..c11b1a6 100644 --- a/src/network/access/qhttpnetworkreply.cpp +++ b/src/network/access/qhttpnetworkreply.cpp @@ -176,13 +176,10 @@ qint64 QHttpNetworkReply::bytesAvailableNextBlock() const return -1; } -QByteArray QHttpNetworkReply::read(qint64 maxSize) +QByteArray QHttpNetworkReply::readAny() { Q_D(QHttpNetworkReply); - QByteArray data; - if (d->connection) - d->connection->d_func()->read(*this, data, maxSize); - return data; + return d->responseData.read(); } bool QHttpNetworkReply::isFinished() const @@ -195,8 +192,9 @@ bool QHttpNetworkReply::isFinished() const QHttpNetworkReplyPrivate::QHttpNetworkReplyPrivate(const QUrl &newUrl) : QHttpNetworkHeaderPrivate(newUrl), state(NothingDoneState), statusCode(100), majorVersion(0), minorVersion(0), bodyLength(0), contentRead(0), totalProgress(0), + chunkedTransferEncoding(0), currentChunkSize(0), currentChunkRead(0), connection(0), initInflate(false), - autoDecompress(false), requestIsPrepared(false) + autoDecompress(false), responseData(), requestIsPrepared(false) { } @@ -498,6 +496,9 @@ qint64 QHttpNetworkReplyPrivate::readHeader(QAbstractSocket *socket) state = ReadingDataState; fragment.clear(); // next fragment bodyLength = contentLength(); // cache the length + + // cache isChunked() since it is called often + chunkedTransferEncoding = headerField("transfer-encoding").toLower().contains("chunked"); } return bytes; } @@ -538,7 +539,7 @@ void QHttpNetworkReplyPrivate::parseHeader(const QByteArray &header) bool QHttpNetworkReplyPrivate::isChunked() { - return headerField("transfer-encoding").toLower().contains("chunked"); + return chunkedTransferEncoding; } bool QHttpNetworkReplyPrivate::connectionCloseEnabled() @@ -549,17 +550,19 @@ bool QHttpNetworkReplyPrivate::connectionCloseEnabled() // note this function can only be used for non-chunked, non-compressed with // known content length -qint64 QHttpNetworkReplyPrivate::readBodyFast(QAbstractSocket *socket, QRingBuffer *rb) -{ - quint64 toBeRead = qMin(socket->bytesAvailable(), bodyLength - contentRead); - char* dst = rb->reserve(toBeRead); - qint64 haveRead = socket->read(dst, toBeRead); +qint64 QHttpNetworkReplyPrivate::readBodyFast(QAbstractSocket *socket, QByteDataBuffer *rb) +{ + qint64 toBeRead = qMin(socket->bytesAvailable(), bodyLength - contentRead); + QByteArray bd; + bd.resize(toBeRead); + qint64 haveRead = socket->read(bd.data(), bd.size()); if (haveRead == -1) { - rb->chop(toBeRead); + bd.clear(); return 0; // ### error checking here; } + bd.resize(haveRead); - rb->chop(toBeRead - haveRead); + rb->append(bd); if (contentRead + haveRead == bodyLength) { state = AllDoneState; @@ -571,7 +574,7 @@ qint64 QHttpNetworkReplyPrivate::readBodyFast(QAbstractSocket *socket, QRingBuff } -qint64 QHttpNetworkReplyPrivate::readBody(QAbstractSocket *socket, QIODevice *out) +qint64 QHttpNetworkReplyPrivate::readBody(QAbstractSocket *socket, QByteDataBuffer *out) { qint64 bytes = 0; if (isChunked()) { @@ -589,33 +592,35 @@ qint64 QHttpNetworkReplyPrivate::readBody(QAbstractSocket *socket, QIODevice *ou return bytes; } -qint64 QHttpNetworkReplyPrivate::readReplyBodyRaw(QIODevice *in, QIODevice *out, qint64 size) +qint64 QHttpNetworkReplyPrivate::readReplyBodyRaw(QIODevice *in, QByteDataBuffer *out, qint64 size) { qint64 bytes = 0; Q_ASSERT(in); Q_ASSERT(out); int toBeRead = qMin<qint64>(128*1024, qMin<qint64>(size, in->bytesAvailable())); - QByteArray raw(toBeRead, 0); - while (size > 0) { - qint64 read = in->read(raw.data(), raw.size()); - if (read == 0) - return bytes; - // ### error checking here - qint64 written = out->write(raw.data(), read); - if (written == 0) + while (toBeRead > 0) { + QByteArray byteData; + byteData.resize(toBeRead); + qint64 haveRead = in->read(byteData.data(), byteData.size()); + if (haveRead <= 0) { + // ### error checking here + byteData.clear(); return bytes; - if (read != written) - qDebug() << "### read" << read << "written" << written; - bytes += read; - size -= read; - out->waitForBytesWritten(-1); // throttle + } + + byteData.resize(haveRead); + out->append(byteData); + bytes += haveRead; + size -= haveRead; + + toBeRead = qMin<qint64>(128*1024, qMin<qint64>(size, in->bytesAvailable())); } return bytes; } -qint64 QHttpNetworkReplyPrivate::readReplyBodyChunked(QIODevice *in, QIODevice *out) +qint64 QHttpNetworkReplyPrivate::readReplyBodyChunked(QIODevice *in, QByteDataBuffer *out) { qint64 bytes = 0; while (in->bytesAvailable()) { // while we can read from input @@ -636,17 +641,14 @@ qint64 QHttpNetworkReplyPrivate::readReplyBodyChunked(QIODevice *in, QIODevice * state = AllDoneState; break; } - // otherwise, read data - qint64 readSize = qMin(in->bytesAvailable(), currentChunkSize - currentChunkRead); - QByteArray buffer(readSize, 0); - qint64 read = in->read(buffer.data(), readSize); - bytes += read; - currentChunkRead += read; - qint64 written = out->write(buffer); - Q_UNUSED(written); // Avoid compile warning when building release - Q_ASSERT(read == written); + + // otherwise, try to read what is missing for this chunk + qint64 haveRead = readReplyBodyRaw (in, out, currentChunkSize - currentChunkRead); + currentChunkRead += haveRead; + bytes += haveRead; + // ### error checking here - out->waitForBytesWritten(-1); + } return bytes; } @@ -708,6 +710,13 @@ void QHttpNetworkReply::ignoreSslErrors() d->connection->ignoreSslErrors(); } +void QHttpNetworkReply::ignoreSslErrors(const QList<QSslError> &errors) +{ + Q_D(QHttpNetworkReply); + if (d->connection) + d->connection->ignoreSslErrors(errors); +} + #endif //QT_NO_OPENSSL diff --git a/src/network/access/qhttpnetworkreply_p.h b/src/network/access/qhttpnetworkreply_p.h index 26e1047..575e824 100644 --- a/src/network/access/qhttpnetworkreply_p.h +++ b/src/network/access/qhttpnetworkreply_p.h @@ -80,6 +80,7 @@ static const unsigned char gz_magic[2] = {0x1f, 0x8b}; // gzip magic header #include <private/qhttpnetworkrequest_p.h> #include <private/qauthenticator_p.h> #include <private/qringbuffer_p.h> +#include <private/qbytedata_p.h> QT_BEGIN_NAMESPACE @@ -122,7 +123,7 @@ public: qint64 bytesAvailable() const; qint64 bytesAvailableNextBlock() const; - QByteArray read(qint64 maxSize = -1); + QByteArray readAny(); bool isFinished() const; @@ -130,6 +131,7 @@ public: QSslConfiguration sslConfiguration() const; void setSslConfiguration(const QSslConfiguration &config); void ignoreSslErrors(); + void ignoreSslErrors(const QList<QSslError> &errors); Q_SIGNALS: void sslErrors(const QList<QSslError> &errors); @@ -159,14 +161,14 @@ public: bool parseStatus(const QByteArray &status); qint64 readHeader(QAbstractSocket *socket); void parseHeader(const QByteArray &header); - qint64 readBody(QAbstractSocket *socket, QIODevice *out); - qint64 readBodyFast(QAbstractSocket *socket, QRingBuffer *rb); + qint64 readBody(QAbstractSocket *socket, QByteDataBuffer *out); + qint64 readBodyFast(QAbstractSocket *socket, QByteDataBuffer *rb); bool findChallenge(bool forProxy, QByteArray &challenge) const; QAuthenticatorPrivate::Method authenticationMethod(bool isProxy) const; void clear(); - qint64 readReplyBodyRaw(QIODevice *in, QIODevice *out, qint64 size); - qint64 readReplyBodyChunked(QIODevice *in, QIODevice *out); + qint64 readReplyBodyRaw(QIODevice *in, QByteDataBuffer *out, qint64 size); + qint64 readReplyBodyChunked(QIODevice *in, QByteDataBuffer *out); qint64 getChunkSize(QIODevice *in, qint64 *chunkSize); qint64 bytesAvailable() const; @@ -197,6 +199,7 @@ public: qint64 contentRead; qint64 totalProgress; QByteArray fragment; // used for header, status, chunk header etc, not for reply data + bool chunkedTransferEncoding; qint64 currentChunkSize; qint64 currentChunkRead; QPointer<QHttpNetworkConnection> connection; @@ -207,7 +210,7 @@ public: #endif bool autoDecompress; - QRingBuffer responseData; // uncompressed body + QByteDataBuffer responseData; // uncompressed body QByteArray compressedData; // compressed body (temporary) bool requestIsPrepared; }; diff --git a/src/network/access/qnetworkaccessbackend.cpp b/src/network/access/qnetworkaccessbackend.cpp index 88ae894..caaa38e 100644 --- a/src/network/access/qnetworkaccessbackend.cpp +++ b/src/network/access/qnetworkaccessbackend.cpp @@ -165,6 +165,12 @@ void QNetworkAccessBackend::ignoreSslErrors() // do nothing } +void QNetworkAccessBackend::ignoreSslErrors(const QList<QSslError> &errors) +{ + Q_UNUSED(errors); + // do nothing +} + void QNetworkAccessBackend::fetchSslConfiguration(QSslConfiguration &) const { // do nothing @@ -217,9 +223,9 @@ qint64 QNetworkAccessBackend::nextDownstreamBlockSize() const return reply->nextDownstreamBlockSize(); } -void QNetworkAccessBackend::writeDownstreamData(const QByteArray &data) +void QNetworkAccessBackend::writeDownstreamData(QByteDataBuffer &list) { - reply->appendDownstreamData(data); + reply->appendDownstreamData(list); } void QNetworkAccessBackend::writeDownstreamData(QIODevice *data) diff --git a/src/network/access/qnetworkaccessbackend_p.h b/src/network/access/qnetworkaccessbackend_p.h index 21cb4a6..27da5bc 100644 --- a/src/network/access/qnetworkaccessbackend_p.h +++ b/src/network/access/qnetworkaccessbackend_p.h @@ -118,6 +118,7 @@ public: virtual void downstreamReadyWrite(); virtual void copyFinished(QIODevice *); virtual void ignoreSslErrors(); + virtual void ignoreSslErrors(const QList<QSslError> &errors); virtual void fetchSslConfiguration(QSslConfiguration &configuration) const; virtual void setSslConfiguration(const QSslConfiguration &configuration); @@ -166,7 +167,7 @@ protected: // 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(const QByteArray &data); + void writeDownstreamData(QByteDataBuffer &list); public slots: // for task 251801, needs to be a slot to be called asynchronously diff --git a/src/network/access/qnetworkaccesscachebackend.cpp b/src/network/access/qnetworkaccesscachebackend.cpp index f46a50a..8571ba3 100644 --- a/src/network/access/qnetworkaccesscachebackend.cpp +++ b/src/network/access/qnetworkaccesscachebackend.cpp @@ -86,6 +86,20 @@ bool QNetworkAccessCacheBackend::sendCacheContents() setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, attributes.value(QNetworkRequest::HttpReasonPhraseAttribute)); setAttribute(QNetworkRequest::SourceIsFromCacheAttribute, true); + // set the raw headers + QNetworkCacheMetaData::RawHeaderList rawHeaders = item.rawHeaders(); + QNetworkCacheMetaData::RawHeaderList::ConstIterator it = rawHeaders.constBegin(), + end = rawHeaders.constEnd(); + for ( ; it != end; ++it) + setRawHeader(it->first, it->second); + + // handle a possible redirect + QVariant redirectionTarget = attributes.value(QNetworkRequest::RedirectionTargetAttribute); + if (redirectionTarget.isValid()) { + setAttribute(QNetworkRequest::RedirectionTargetAttribute, redirectionTarget); + redirectionRequested(redirectionTarget.toUrl()); + } + // signal we're open metaDataChanged(); diff --git a/src/network/access/qnetworkaccessdatabackend.cpp b/src/network/access/qnetworkaccessdatabackend.cpp index 609f0c5..4436cf4 100644 --- a/src/network/access/qnetworkaccessdatabackend.cpp +++ b/src/network/access/qnetworkaccessdatabackend.cpp @@ -117,7 +117,11 @@ void QNetworkAccessDataBackend::open() setHeader(QNetworkRequest::ContentLengthHeader, payload.size()); emit metaDataChanged(); - writeDownstreamData(payload); + QByteDataBuffer list; + list.append(payload); + payload.clear(); // important because of implicit sharing! + writeDownstreamData(list); + finished(); return; } diff --git a/src/network/access/qnetworkaccessdebugpipebackend.cpp b/src/network/access/qnetworkaccessdebugpipebackend.cpp index ce768f5..394e196 100644 --- a/src/network/access/qnetworkaccessdebugpipebackend.cpp +++ b/src/network/access/qnetworkaccessdebugpipebackend.cpp @@ -155,7 +155,11 @@ void QNetworkAccessDebugPipeBackend::pushFromSocketToDownstream() // have read something buffer.resize(haveRead); bytesDownloaded += haveRead; - writeDownstreamData(buffer); + + QByteDataBuffer list; + list.append(buffer); + buffer.clear(); // important because of implicit sharing! + writeDownstreamData(list); } } } @@ -276,7 +280,7 @@ void QNetworkAccessDebugPipeBackend::socketConnected() bool QNetworkAccessDebugPipeBackend::waitForDownstreamReadyRead(int ms) { - Q_UNUSED(ms); + Q_UNUSED(ms); qCritical("QNetworkAccess: Debug pipe backend does not support waitForReadyRead()"); return false; } diff --git a/src/network/access/qnetworkaccessfilebackend.cpp b/src/network/access/qnetworkaccessfilebackend.cpp index e3fc8bf..533fc75 100644 --- a/src/network/access/qnetworkaccessfilebackend.cpp +++ b/src/network/access/qnetworkaccessfilebackend.cpp @@ -263,7 +263,11 @@ bool QNetworkAccessFileBackend::readMoreFromFile() data.resize(actuallyRead); totalBytes += actuallyRead; - writeDownstreamData(data); + + QByteDataBuffer list; + list.append(data); + data.clear(); // important because of implicit sharing! + writeDownstreamData(list); } return true; } diff --git a/src/network/access/qnetworkaccessftpbackend.cpp b/src/network/access/qnetworkaccessftpbackend.cpp index d6276a3..911b31a 100644 --- a/src/network/access/qnetworkaccessftpbackend.cpp +++ b/src/network/access/qnetworkaccessftpbackend.cpp @@ -355,7 +355,11 @@ void QNetworkAccessFtpBackend::ftpDone() void QNetworkAccessFtpBackend::ftpReadyRead() { - writeDownstreamData(ftp->readAll()); + QByteArray data = ftp->readAll(); + QByteDataBuffer list; + list.append(data); + data.clear(); // important because of implicit sharing! + writeDownstreamData(list); } void QNetworkAccessFtpBackend::ftpRawCommandReply(int code, const QString &text) diff --git a/src/network/access/qnetworkaccesshttpbackend.cpp b/src/network/access/qnetworkaccesshttpbackend.cpp index 4bc36db..293f857 100644 --- a/src/network/access/qnetworkaccesshttpbackend.cpp +++ b/src/network/access/qnetworkaccesshttpbackend.cpp @@ -294,7 +294,7 @@ public: QNetworkAccessHttpBackend::QNetworkAccessHttpBackend() : QNetworkAccessBackend(), httpReply(0), http(0), uploadDevice(0) #ifndef QT_NO_OPENSSL - , pendingSslConfiguration(0), pendingIgnoreSslErrors(false) + , pendingSslConfiguration(0), pendingIgnoreAllSslErrors(false) #endif { } @@ -521,8 +521,9 @@ void QNetworkAccessHttpBackend::postRequest() #ifndef QT_NO_OPENSSL if (pendingSslConfiguration) httpReply->setSslConfiguration(*pendingSslConfiguration); - if (pendingIgnoreSslErrors) + if (pendingIgnoreAllSslErrors) httpReply->ignoreSslErrors(); + httpReply->ignoreSslErrors(pendingIgnoreSslErrorsList); #endif connect(httpReply, SIGNAL(readyRead()), SLOT(replyReadyRead())); @@ -649,16 +650,19 @@ void QNetworkAccessHttpBackend::readFromHttp() if (!httpReply) return; - // We implement the download rate control - // Don't read from QHttpNetworkAccess more than QNetworkAccessBackend wants - // One of the two functions above will be called when we can read again + // We read possibly more than nextDownstreamBlockSize(), but + // this is not a critical thing since it is already in the + // memory anyway - qint64 bytesToRead = qBound<qint64>(0, httpReply->bytesAvailable(), nextDownstreamBlockSize()); - if (!bytesToRead) - return; + QByteDataBuffer list; + + while (httpReply->bytesAvailable() != 0 && nextDownstreamBlockSize() != 0 && nextDownstreamBlockSize() > list.byteAmount()) { + QByteArray data = httpReply->readAny(); + list.append(data); + } - QByteArray data = httpReply->read(bytesToRead); - writeDownstreamData(data); + if (!list.isEmpty()) + writeDownstreamData(list); } void QNetworkAccessHttpBackend::replyFinished() @@ -885,7 +889,18 @@ void QNetworkAccessHttpBackend::ignoreSslErrors() if (httpReply) httpReply->ignoreSslErrors(); else - pendingIgnoreSslErrors = true; + pendingIgnoreAllSslErrors = true; +} + +void QNetworkAccessHttpBackend::ignoreSslErrors(const QList<QSslError> &errors) +{ + if (httpReply) { + httpReply->ignoreSslErrors(errors); + } else { + // the pending list is set if QNetworkReply::ignoreSslErrors(const QList<QSslError> &errors) + // is called before QNetworkAccessManager::get() (or post(), etc.) + pendingIgnoreSslErrorsList = errors; + } } void QNetworkAccessHttpBackend::fetchSslConfiguration(QSslConfiguration &config) const diff --git a/src/network/access/qnetworkaccesshttpbackend_p.h b/src/network/access/qnetworkaccesshttpbackend_p.h index dec69d0..968f4a5 100644 --- a/src/network/access/qnetworkaccesshttpbackend_p.h +++ b/src/network/access/qnetworkaccesshttpbackend_p.h @@ -85,6 +85,7 @@ public: virtual void copyFinished(QIODevice *); #ifndef QT_NO_OPENSSL virtual void ignoreSslErrors(); + virtual void ignoreSslErrors(const QList<QSslError> &errors); virtual void fetchSslConfiguration(QSslConfiguration &configuration) const; virtual void setSslConfiguration(const QSslConfiguration &configuration); @@ -112,7 +113,8 @@ private: #ifndef QT_NO_OPENSSL QSslConfiguration *pendingSslConfiguration; - bool pendingIgnoreSslErrors; + bool pendingIgnoreAllSslErrors; + QList<QSslError> pendingIgnoreSslErrorsList; #endif void disconnectFromHttp(); diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp index cd736c8..802e603 100644 --- a/src/network/access/qnetworkaccessmanager.cpp +++ b/src/network/access/qnetworkaccessmanager.cpp @@ -540,10 +540,10 @@ void QNetworkAccessManager::setCookieJar(QNetworkCookieJar *cookieJar) } /*! - This function is used to post a request to obtain the network - headers for \a request. It takes its name after the HTTP request - associated (HEAD). It returns a new QNetworkReply object which - will contain such headers. + Posts a request to obtain the network headers for \a request + and returns a new QNetworkReply object which will contain such headers + + The function is named after the HTTP request associated (HEAD). */ QNetworkReply *QNetworkAccessManager::head(const QNetworkRequest &request) { @@ -551,11 +551,12 @@ QNetworkReply *QNetworkAccessManager::head(const QNetworkRequest &request) } /*! - This function is used to post a request to obtain the contents of - the target \a request. It will cause the contents to be - downloaded, along with the headers associated with it. It returns - a new QNetworkReply object opened for reading which emits its - QIODevice::readyRead() signal whenever new data arrives. + Posts a request to obtain the contents of the target \a request + and returns a new QNetworkReply object opened for reading which emits the + \l{QIODevice::readyRead()}{readyRead()} signal whenever new data + arrives. + + The contents as well as associated headers will be downloaded. \sa post(), put(), deleteResource() */ @@ -565,18 +566,15 @@ QNetworkReply *QNetworkAccessManager::get(const QNetworkRequest &request) } /*! - This function is used to send an HTTP POST request to the - destination specified by \a request. The contents of the \a data + Sends an HTTP POST request to the destination specified by \a request + and returns a new QNetworkReply object opened for reading that will + contain the reply sent by the server. The contents of the \a data device will be uploaded to the server. - \a data must be opened for reading when this function is called - and must remain valid until the finished() signal is emitted for - this reply. - - The returned QNetworkReply object will be open for reading and - will contain the reply sent by the server to the POST request. + \a data must be open for reading and must remain valid until the + finished() signal is emitted for this reply. - Note: sending a POST request on protocols other than HTTP and + \note Sending a POST request on protocols other than HTTP and HTTPS is undefined and will probably fail. \sa get(), put(), deleteResource() @@ -588,8 +586,9 @@ QNetworkReply *QNetworkAccessManager::post(const QNetworkRequest &request, QIODe /*! \overload - This function sends the contents of the \a data byte array to the - destination specified by \a request. + + Sends the contents of the \a data byte array to the destination + specified by \a request. */ QNetworkReply *QNetworkAccessManager::post(const QNetworkRequest &request, const QByteArray &data) { @@ -603,20 +602,19 @@ QNetworkReply *QNetworkAccessManager::post(const QNetworkRequest &request, const } /*! - This function is used to upload the contents of \a data to the - destination \a request. + Uploads the contents of \a data to the destination \a request and + returnes a new QNetworkReply object that will be open for reply. \a data must be opened for reading when this function is called and must remain valid until the finished() signal is emitted for this reply. - The returned QNetworkReply object will be open for reply, but - whether anything will be available for reading is protocol - dependent. For HTTP, the server may send a small HTML page - indicating the upload was successful (or not). Other protocols - will probably have content in their replies. + Whether anything will be available for reading from the returned + object is protocol dependent. For HTTP, the server may send a + small HTML page indicating the upload was successful (or not). + Other protocols will probably have content in their replies. - For HTTP, this request will send a PUT request, which most servers + \note For HTTP, this request will send a PUT request, which most servers do not allow. Form upload mechanisms, including that of uploading files through HTML forms, use the POST mechanism. @@ -629,8 +627,8 @@ QNetworkReply *QNetworkAccessManager::put(const QNetworkRequest &request, QIODev /*! \overload - This function sends the contents of the \a data byte array to the - destination specified by \a request. + Sends the contents of the \a data byte array to the destination + specified by \a request. */ QNetworkReply *QNetworkAccessManager::put(const QNetworkRequest &request, const QByteArray &data) { @@ -646,9 +644,10 @@ QNetworkReply *QNetworkAccessManager::put(const QNetworkRequest &request, const /*! \since 4.6 - This function is used to send a request to delete the resource - identified by the URL of \a request. - This feature is currently available for HTTP only, performing an HTTP DELETE request. + Sends a request to delete the resource identified by the URL of \a request. + + \note This feature is currently available for HTTP only, performing an + HTTP DELETE request. \sa get(), post(), put() */ diff --git a/src/network/access/qnetworkreply.cpp b/src/network/access/qnetworkreply.cpp index 1b0d9f5..3abf927 100644 --- a/src/network/access/qnetworkreply.cpp +++ b/src/network/access/qnetworkreply.cpp @@ -442,6 +442,31 @@ QNetworkReply::NetworkError QNetworkReply::error() const } /*! + \since 4.6 + + Returns true when the reply has finished or was aborted. + + \sa isRunning() +*/ +bool QNetworkReply::isFinished() const +{ + return d_func()->isFinished(); +} + +/*! + \since 4.6 + + Returns true when the request is still processing and the + reply has not finished or was aborted yet. + + \sa isFinished() +*/ +bool QNetworkReply::isRunning() const +{ + return !isFinished(); +} + +/*! Returns the URL of the content downloaded or uploaded. Note that the URL may be different from that of the original request. @@ -559,6 +584,38 @@ void QNetworkReply::setSslConfiguration(const QSslConfiguration &config) qt_metacall(QMetaObject::InvokeMetaMethod, id, arr); } } + +/*! + \overload + \since 4.6 + + If this function is called, the SSL errors given in \a errors + will be ignored. + + Note that you can set the expected certificate in the SSL error: + If, for instance, you want to issue a request to a server that uses + a self-signed certificate, consider the following snippet: + + \snippet doc/src/snippets/code/src_network_access_qnetworkreply.cpp 0 + + Multiple calls to this function will replace the list of errors that + were passed in previous calls. + You can clear the list of errors you want to ignore by calling this + function with an empty list. + + \sa sslConfiguration(), sslErrors(), QSslSocket::ignoreSslErrors() +*/ +void QNetworkReply::ignoreSslErrors(const QList<QSslError> &errors) +{ + // do this cryptic trick, because we could not add a virtual method to this class later on + // since that breaks binary compatibility + int id = metaObject()->indexOfMethod("ignoreSslErrorsImplementation(QList<QSslError>)"); + if (id != -1) { + QList<QSslError> copy(errors); + void *arr[] = { 0, © }; + qt_metacall(QMetaObject::InvokeMetaMethod, id, arr); + } +} #endif /*! @@ -573,7 +630,7 @@ void QNetworkReply::setSslConfiguration(const QSslConfiguration &config) sslErrors() signal, which indicates which errors were found. - \sa sslConfiguration(), sslErrors() + \sa sslConfiguration(), sslErrors(), QSslSocket::ignoreSslErrors() */ void QNetworkReply::ignoreSslErrors() { diff --git a/src/network/access/qnetworkreply.h b/src/network/access/qnetworkreply.h index 7cb082f..679ab71 100644 --- a/src/network/access/qnetworkreply.h +++ b/src/network/access/qnetworkreply.h @@ -116,6 +116,8 @@ public: QNetworkAccessManager::Operation operation() const; QNetworkRequest request() const; NetworkError error() const; + bool isFinished() const; + bool isRunning() const; QUrl url() const; // "cooked" headers @@ -132,6 +134,7 @@ public: #ifndef QT_NO_OPENSSL QSslConfiguration sslConfiguration() const; void setSslConfiguration(const QSslConfiguration &configuration); + void ignoreSslErrors(const QList<QSslError> &errors); #endif public Q_SLOTS: diff --git a/src/network/access/qnetworkreply_p.h b/src/network/access/qnetworkreply_p.h index c8543f0..b51e3fb 100644 --- a/src/network/access/qnetworkreply_p.h +++ b/src/network/access/qnetworkreply_p.h @@ -75,6 +75,8 @@ public: static inline void setManager(QNetworkReply *reply, QNetworkAccessManager *manager) { reply->d_func()->manager = manager; } + virtual bool isFinished() const { return false; } + Q_DECLARE_PUBLIC(QNetworkReply) }; diff --git a/src/network/access/qnetworkreplyimpl.cpp b/src/network/access/qnetworkreplyimpl.cpp index 6eacdf1..de7f8b4 100644 --- a/src/network/access/qnetworkreplyimpl.cpp +++ b/src/network/access/qnetworkreplyimpl.cpp @@ -91,6 +91,8 @@ void QNetworkReplyImplPrivate::_q_startOperation() void QNetworkReplyImplPrivate::_q_copyReadyRead() { Q_Q(QNetworkReplyImpl); + if (state != Working) + return; if (!copyDevice || !q->isOpen()) return; @@ -101,16 +103,17 @@ void QNetworkReplyImplPrivate::_q_copyReadyRead() break; bytesToRead = qBound<qint64>(1, bytesToRead, copyDevice->bytesAvailable()); - char *ptr = readBuffer.reserve(bytesToRead); - qint64 bytesActuallyRead = copyDevice->read(ptr, bytesToRead); + QByteArray byteData; + byteData.resize(bytesToRead); + qint64 bytesActuallyRead = copyDevice->read(byteData.data(), byteData.size()); if (bytesActuallyRead == -1) { - readBuffer.chop(bytesToRead); + byteData.clear(); backendNotify(NotifyCopyFinished); break; } - if (bytesActuallyRead != bytesToRead) - readBuffer.chop(bytesToRead - bytesActuallyRead); + byteData.resize(bytesActuallyRead); + readBuffer.append(byteData); if (!copyDevice->isSequential() && copyDevice->atEnd()) { backendNotify(NotifyCopyFinished); @@ -382,26 +385,33 @@ qint64 QNetworkReplyImplPrivate::nextDownstreamBlockSize() const if (readBufferMaxSize == 0) return DesiredBufferSize; - return qMax<qint64>(0, readBufferMaxSize - readBuffer.size()); + return qMax<qint64>(0, readBufferMaxSize - readBuffer.byteAmount()); } // we received downstream data and send this to the cache // and to our readBuffer (which in turn gets read by the user of QNetworkReply) -void QNetworkReplyImplPrivate::appendDownstreamData(const QByteArray &data) +void QNetworkReplyImplPrivate::appendDownstreamData(QByteDataBuffer &data) { Q_Q(QNetworkReplyImpl); if (!q->isOpen()) return; - char *ptr = readBuffer.reserve(data.size()); - memcpy(ptr, data.constData(), data.size()); - if (cacheEnabled && !cacheSaveDevice) { // save the meta data QNetworkCacheMetaData metaData; metaData.setUrl(url); metaData = backend->fetchCacheMetaData(metaData); + + // save the redirect request also in the cache + QVariant redirectionTarget = q->attribute(QNetworkRequest::RedirectionTargetAttribute); + if (redirectionTarget.isValid()) { + QNetworkCacheMetaData::AttributesMap attributes = metaData.attributes(); + attributes.insert(QNetworkRequest::RedirectionTargetAttribute, redirectionTarget); + metaData.setAttributes(attributes); + } + cacheSaveDevice = networkCache->prepare(metaData); + if (!cacheSaveDevice || (cacheSaveDevice && !cacheSaveDevice->isOpen())) { if (cacheSaveDevice && !cacheSaveDevice->isOpen()) qCritical("QNetworkReplyImpl: network cache returned a device that is not open -- " @@ -414,10 +424,19 @@ void QNetworkReplyImplPrivate::appendDownstreamData(const QByteArray &data) } } - if (cacheSaveDevice) - cacheSaveDevice->write(data); + qint64 bytesWritten = 0; + for (int i = 0; i < data.bufferCount(); i++) { + QByteArray item = data[i]; + + if (cacheSaveDevice) + cacheSaveDevice->write(item.constData(), item.size()); + readBuffer.append(item); + + bytesWritten += item.size(); + } + data.clear(); - bytesDownloaded += data.size(); + bytesDownloaded += bytesWritten; lastBytesDownloaded = bytesDownloaded; QPointer<QNetworkReplyImpl> qq = q; @@ -426,6 +445,8 @@ void QNetworkReplyImplPrivate::appendDownstreamData(const QByteArray &data) pauseNotificationHandling(); emit q->downloadProgress(bytesDownloaded, totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong()); + // important: At the point of this readyRead(), the data parameter list must be empty, + // else implicit sharing will trigger memcpy when the user is reading data! emit q->readyRead(); // hopefully we haven't been deleted here @@ -462,8 +483,8 @@ void QNetworkReplyImplPrivate::appendDownstreamData(QIODevice *data) void QNetworkReplyImplPrivate::finished() { Q_Q(QNetworkReplyImpl); - Q_ASSERT_X(state != Finished, "QNetworkReplyImpl", - "Backend called finished/finishedWithError more than once"); + if (state == Finished || state == Aborted) + return; state = Finished; pendingNotifications.clear(); @@ -531,6 +552,11 @@ void QNetworkReplyImplPrivate::sslErrors(const QList<QSslError> &errors) #endif } +bool QNetworkReplyImplPrivate::isFinished() const +{ + return (state == Finished || state == Aborted); +} + QNetworkReplyImpl::QNetworkReplyImpl(QObject *parent) : QNetworkReply(*new QNetworkReplyImplPrivate, parent) { @@ -596,14 +622,14 @@ void QNetworkReplyImpl::close() */ qint64 QNetworkReplyImpl::bytesAvailable() const { - return QNetworkReply::bytesAvailable() + d_func()->readBuffer.size(); + return QNetworkReply::bytesAvailable() + d_func()->readBuffer.byteAmount(); } void QNetworkReplyImpl::setReadBufferSize(qint64 size) { Q_D(QNetworkReplyImpl); if (size > d->readBufferMaxSize && - size == d->readBuffer.size()) + size > d->readBuffer.byteAmount()) d->backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite); QNetworkReply::setReadBufferSize(size); @@ -633,6 +659,12 @@ void QNetworkReplyImpl::ignoreSslErrors() d->backend->ignoreSslErrors(); } +void QNetworkReplyImpl::ignoreSslErrorsImplementation(const QList<QSslError> &errors) +{ + Q_D(QNetworkReplyImpl); + if (d->backend) + d->backend->ignoreSslErrors(errors); +} #endif // QT_NO_OPENSSL /*! @@ -651,7 +683,7 @@ qint64 QNetworkReplyImpl::readData(char *data, qint64 maxlen) return 1; } - maxlen = qMin<qint64>(maxlen, d->readBuffer.size()); + maxlen = qMin<qint64>(maxlen, d->readBuffer.byteAmount()); return d->readBuffer.read(data, maxlen); } diff --git a/src/network/access/qnetworkreplyimpl_p.h b/src/network/access/qnetworkreplyimpl_p.h index 3e89a00..fba8d34 100644 --- a/src/network/access/qnetworkreplyimpl_p.h +++ b/src/network/access/qnetworkreplyimpl_p.h @@ -61,6 +61,7 @@ #include "QtCore/qqueue.h" #include "QtCore/qbuffer.h" #include "private/qringbuffer_p.h" +#include "private/qbytedata_p.h" QT_BEGIN_NAMESPACE @@ -88,6 +89,7 @@ public: Q_INVOKABLE QSslConfiguration sslConfigurationImplementation() const; Q_INVOKABLE void setSslConfigurationImplementation(const QSslConfiguration &configuration); virtual void ignoreSslErrors(); + Q_INVOKABLE virtual void ignoreSslErrorsImplementation(const QList<QSslError> &errors); #endif Q_DECLARE_PRIVATE(QNetworkReplyImpl) @@ -144,7 +146,7 @@ public: void consume(qint64 count); void emitUploadProgress(qint64 bytesSent, qint64 bytesTotal); qint64 nextDownstreamBlockSize() const; - void appendDownstreamData(const QByteArray &data); + void appendDownstreamData(QByteDataBuffer &data); void appendDownstreamData(QIODevice *data); void finished(); void error(QNetworkReply::NetworkError code, const QString &errorString); @@ -152,6 +154,8 @@ public: void redirectionRequested(const QUrl &target); void sslErrors(const QList<QSslError> &errors); + bool isFinished() const; + QNetworkAccessBackend *backend; QIODevice *outgoingData; QRingBuffer *outgoingDataBuffer; @@ -170,7 +174,7 @@ public: QList<QNetworkProxy> proxyList; #endif - QRingBuffer readBuffer; + QByteDataBuffer readBuffer; qint64 bytesDownloaded; qint64 lastBytesDownloaded; qint64 bytesUploaded; diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp index 557143f..caa8669 100644 --- a/src/network/access/qnetworkrequest.cpp +++ b/src/network/access/qnetworkrequest.cpp @@ -153,9 +153,7 @@ QT_BEGIN_NAMESPACE future uses. If the value is false, the data obtained will not be automatically cached. If true, data may be cached, provided it is cacheable (what is cacheable depends on the protocol - being used). Note that the default QNetworkAccessManager - implementation does not support caching, so it will ignore - this attribute. + being used). \value SourceIsFromCacheAttribute Replies only, type: QVariant::Bool (default: false) |