summaryrefslogtreecommitdiffstats
path: root/src/network/access
diff options
context:
space:
mode:
Diffstat (limited to 'src/network/access')
-rw-r--r--src/network/access/qfilenetworkreply.cpp9
-rw-r--r--src/network/access/qfilenetworkreply_p.h2
-rw-r--r--src/network/access/qhttpnetworkconnection.cpp13
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel.cpp188
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel_p.h13
-rw-r--r--src/network/access/qhttpnetworkreply.cpp113
-rw-r--r--src/network/access/qhttpnetworkreply_p.h15
-rw-r--r--src/network/access/qhttpnetworkrequest.cpp11
-rw-r--r--src/network/access/qhttpnetworkrequest_p.h4
-rw-r--r--src/network/access/qnetworkaccessbackend.cpp13
-rw-r--r--src/network/access/qnetworkaccessbackend_p.h4
-rw-r--r--src/network/access/qnetworkaccessfilebackend.cpp11
-rw-r--r--src/network/access/qnetworkaccessftpbackend.cpp2
-rw-r--r--src/network/access/qnetworkaccesshttpbackend.cpp33
-rw-r--r--src/network/access/qnetworkaccesshttpbackend_p.h1
-rw-r--r--src/network/access/qnetworkaccessmanager.cpp16
-rw-r--r--src/network/access/qnetworkreply.cpp18
-rw-r--r--src/network/access/qnetworkreply.h1
-rw-r--r--src/network/access/qnetworkreply_p.h3
-rw-r--r--src/network/access/qnetworkreplyimpl.cpp106
-rw-r--r--src/network/access/qnetworkreplyimpl_p.h12
-rw-r--r--src/network/access/qnetworkrequest.cpp4
22 files changed, 427 insertions, 165 deletions
diff --git a/src/network/access/qfilenetworkreply.cpp b/src/network/access/qfilenetworkreply.cpp
index 00bd29e..73965f4 100644
--- a/src/network/access/qfilenetworkreply.cpp
+++ b/src/network/access/qfilenetworkreply.cpp
@@ -68,10 +68,9 @@ QFileNetworkReply::QFileNetworkReply(QObject *parent, const QNetworkRequest &req
setRequest(req);
setUrl(req.url());
setOperation(op);
+ setFinished(true);
QNetworkReply::open(QIODevice::ReadOnly);
- qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError");
-
QFileNetworkReplyPrivate *d = (QFileNetworkReplyPrivate*) d_func();
QUrl url = req.url();
@@ -144,12 +143,6 @@ QFileNetworkReply::QFileNetworkReply(QObject *parent, const QNetworkRequest &req
QMetaObject::invokeMethod(this, "readyRead", Qt::QueuedConnection);
QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
}
-
-bool QFileNetworkReplyPrivate::isFinished() const
-{
- return true;
-}
-
void QFileNetworkReply::close()
{
Q_D(QFileNetworkReply);
diff --git a/src/network/access/qfilenetworkreply_p.h b/src/network/access/qfilenetworkreply_p.h
index 710ec9f..227c775 100644
--- a/src/network/access/qfilenetworkreply_p.h
+++ b/src/network/access/qfilenetworkreply_p.h
@@ -92,8 +92,6 @@ public:
qint64 fileSize;
qint64 filePos;
- virtual bool isFinished() const;
-
Q_DECLARE_PUBLIC(QFileNetworkReply)
};
diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp
index ccdbb20..2ab28c7 100644
--- a/src/network/access/qhttpnetworkconnection.cpp
+++ b/src/network/access/qhttpnetworkconnection.cpp
@@ -117,6 +117,7 @@ void QHttpNetworkConnectionPrivate::init()
{
for (int i = 0; i < channelCount; i++) {
channels[i].setConnection(this->q_func());
+ channels[i].ssl = encrypt;
channels[i].init();
}
}
@@ -530,33 +531,35 @@ void QHttpNetworkConnectionPrivate::fillPipeline(QAbstractSocket *socket)
|| channels[i].state == QHttpNetworkConnectionChannel::ReadingState))
return;
-
- //qDebug() << "QHttpNetworkConnectionPrivate::fillPipeline processing highPriorityQueue, size=" << highPriorityQueue.size() << " alreadyPipelined=" << channels[i].alreadyPipelinedRequests.length();
int lengthBefore;
while (!highPriorityQueue.isEmpty()) {
lengthBefore = channels[i].alreadyPipelinedRequests.length();
fillPipeline(highPriorityQueue, channels[i]);
- if (channels[i].alreadyPipelinedRequests.length() >= defaultPipelineLength)
+ if (channels[i].alreadyPipelinedRequests.length() >= defaultPipelineLength) {
+ channels[i].pipelineFlush();
return;
+ }
if (lengthBefore == channels[i].alreadyPipelinedRequests.length())
break; // did not process anything, now do the low prio queue
}
- //qDebug() << "QHttpNetworkConnectionPrivate::fillPipeline processing lowPriorityQueue, size=" << lowPriorityQueue.size() << " alreadyPipelined=" << channels[i].alreadyPipelinedRequests.length();
while (!lowPriorityQueue.isEmpty()) {
lengthBefore = channels[i].alreadyPipelinedRequests.length();
fillPipeline(lowPriorityQueue, channels[i]);
- if (channels[i].alreadyPipelinedRequests.length() >= defaultPipelineLength)
+ if (channels[i].alreadyPipelinedRequests.length() >= defaultPipelineLength) {
+ channels[i].pipelineFlush();
return;
+ }
if (lengthBefore == channels[i].alreadyPipelinedRequests.length())
break; // did not process anything
}
+ channels[i].pipelineFlush();
}
// returns true when the processing of a queue has been done
diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp
index 6437f6f..e39f9ed 100644
--- a/src/network/access/qhttpnetworkconnectionchannel.cpp
+++ b/src/network/access/qhttpnetworkconnectionchannel.cpp
@@ -60,6 +60,7 @@ QT_BEGIN_NAMESPACE
QHttpNetworkConnectionChannel::QHttpNetworkConnectionChannel()
: socket(0)
+ , ssl(false)
, state(IdleState)
, reply(0)
, written(0)
@@ -90,11 +91,8 @@ void QHttpNetworkConnectionChannel::init()
#else
socket = new QTcpSocket;
#endif
-
- // limit the socket read buffer size. we will read everything into
- // the QHttpNetworkReply anyway, so let's grow only that and not
- // here and there.
- socket->setReadBufferSize(64*1024);
+ // Set by QNAM anyway, but let's be safe here
+ socket->setProxy(QNetworkProxy::NoProxy);
QObject::connect(socket, SIGNAL(bytesWritten(qint64)),
this, SLOT(_q_bytesWritten(qint64)),
@@ -164,11 +162,12 @@ bool QHttpNetworkConnectionChannel::sendRequest()
written = 0; // excluding the header
bytesTotal = 0;
- reply->d_func()->clear();
- reply->d_func()->connection = connection;
- reply->d_func()->connectionChannel = this;
- reply->d_func()->autoDecompress = request.d->autoDecompress;
- reply->d_func()->pipeliningUsed = false;
+ QHttpNetworkReplyPrivate *replyPrivate = reply->d_func();
+ replyPrivate->clear();
+ replyPrivate->connection = connection;
+ replyPrivate->connectionChannel = this;
+ replyPrivate->autoDecompress = request.d->autoDecompress;
+ replyPrivate->pipeliningUsed = false;
pendingEncrypt = false;
// if the url contains authentication parameters, use the new ones
@@ -328,7 +327,6 @@ void QHttpNetworkConnectionChannel::_q_receiveReply()
return;
}
- qint64 bytes = 0;
QAbstractSocket::SocketState socketState = socket->state();
// connection might be closed to signal the end of data
@@ -349,12 +347,14 @@ void QHttpNetworkConnectionChannel::_q_receiveReply()
}
// read loop for the response
- while (socket->bytesAvailable()) {
+ qint64 bytes = 0;
+ qint64 lastBytes = bytes;
+ do {
+ lastBytes = bytes;
+
QHttpNetworkReplyPrivate::ReplyState state = reply->d_func()->state;
switch (state) {
case QHttpNetworkReplyPrivate::NothingDoneState: {
- // only eat whitespace on the first call
- eatWhitespace();
state = reply->d_func()->state = QHttpNetworkReplyPrivate::ReadingStatusState;
// fallthrough
}
@@ -378,6 +378,7 @@ void QHttpNetworkConnectionChannel::_q_receiveReply()
return;
}
bytes += headerBytes;
+ // If headers were parsed successfully now it is the ReadingDataState
if (replyPrivate->state == QHttpNetworkReplyPrivate::ReadingDataState) {
if (replyPrivate->isGzipped() && replyPrivate->autoDecompress) {
// remove the Content-Length from header
@@ -392,6 +393,10 @@ void QHttpNetworkConnectionChannel::_q_receiveReply()
}
if (replyPrivate->shouldEmitSignals())
emit reply->headerChanged();
+ // After headerChanged had been emitted
+ // we can suddenly have a replyPrivate->userProvidedDownloadBuffer
+ // this is handled in the ReadingDataState however
+
if (!replyPrivate->expectContent()) {
replyPrivate->state = QHttpNetworkReplyPrivate::AllDoneState;
allDone();
@@ -412,22 +417,27 @@ void QHttpNetworkConnectionChannel::_q_receiveReply()
return;
}
- if (!replyPrivate->isChunked() && !replyPrivate->autoDecompress
- && replyPrivate->bodyLength > 0) {
- // bulk files like images should fulfill these properties and
- // we can therefore save on memory copying
- bytes = replyPrivate->readBodyFast(socket, &replyPrivate->responseData);
- replyPrivate->totalProgress += bytes;
+ if (replyPrivate->userProvidedDownloadBuffer) {
+ // the user provided a direct buffer where we should put all our data in.
+ // this only works when we can tell the user the content length and he/she can allocate
+ // the buffer in that size.
+ // note that this call will read only from the still buffered data
+ qint64 haveRead = replyPrivate->readBodyVeryFast(socket, replyPrivate->userProvidedDownloadBuffer + replyPrivate->totalProgress);
+ bytes += haveRead;
+ replyPrivate->totalProgress += haveRead;
+
+ // the user will get notified of it via progress signal
+ emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
+ } else if (!replyPrivate->isChunked() && !replyPrivate->autoDecompress
+ && replyPrivate->bodyLength > 0) {
+ // bulk files like images should fulfill these properties and
+ // we can therefore save on memory copying
+ qint64 haveRead = replyPrivate->readBodyFast(socket, &replyPrivate->responseData);
+ bytes += haveRead;
+ replyPrivate->totalProgress += haveRead;
if (replyPrivate->shouldEmitSignals()) {
- QPointer<QHttpNetworkReply> replyPointer = reply;
emit reply->readyRead();
- // make sure that the reply is valid
- if (replyPointer.isNull())
- return;
emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
- // make sure that the reply is valid
- if (replyPointer.isNull())
- return;
}
}
else
@@ -435,8 +445,9 @@ void QHttpNetworkConnectionChannel::_q_receiveReply()
// use the traditional slower reading (for compressed encoding, chunked encoding,
// no content-length etc)
QByteDataBuffer byteDatas;
- bytes = replyPrivate->readBody(socket, &byteDatas);
- if (bytes) {
+ qint64 haveRead = replyPrivate->readBody(socket, &byteDatas);
+ if (haveRead) {
+ bytes += haveRead;
if (replyPrivate->autoDecompress)
replyPrivate->appendCompressedReplyData(byteDatas);
else
@@ -445,17 +456,10 @@ void QHttpNetworkConnectionChannel::_q_receiveReply()
if (!replyPrivate->autoDecompress) {
replyPrivate->totalProgress += bytes;
if (replyPrivate->shouldEmitSignals()) {
- QPointer<QHttpNetworkReply> replyPointer = 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 (replyPointer.isNull())
- return;
emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
- // make sure that the reply is valid
- if (replyPointer.isNull())
- return;
}
}
#ifndef QT_NO_COMPRESS
@@ -477,7 +481,7 @@ void QHttpNetworkConnectionChannel::_q_receiveReply()
default:
break;
}
- }
+ } while (bytes != lastBytes && reply);
}
// called when unexpectedly reading a -1 or when data is expected but socket is closed
@@ -520,7 +524,7 @@ bool QHttpNetworkConnectionChannel::ensureConnection()
if (socketState != QAbstractSocket::ConnectedState) {
// connect to the host if not already connected.
state = QHttpNetworkConnectionChannel::ConnectingState;
- pendingEncrypt = connection->d_func()->encrypt;
+ pendingEncrypt = ssl;
// reset state
pipeliningSupported = PipeliningSupportUnknown;
@@ -543,23 +547,43 @@ bool QHttpNetworkConnectionChannel::ensureConnection()
#ifndef QT_NO_NETWORKPROXY
// HTTPS always use transparent proxy.
- if (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy && !connection->d_func()->encrypt) {
+ if (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy && !ssl) {
connectHost = connection->d_func()->networkProxy.hostName();
connectPort = connection->d_func()->networkProxy.port();
}
#endif
- if (connection->d_func()->encrypt) {
+ if (ssl) {
#ifndef QT_NO_OPENSSL
QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket);
sslSocket->connectToHostEncrypted(connectHost, connectPort);
if (ignoreAllSslErrors)
sslSocket->ignoreSslErrors();
sslSocket->ignoreSslErrors(ignoreSslErrorsList);
+
+ // limit the socket read buffer size. we will read everything into
+ // the QHttpNetworkReply anyway, so let's grow only that and not
+ // here and there.
+ socket->setReadBufferSize(64*1024);
#else
connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ProtocolUnknownError);
#endif
} else {
- socket->connectToHost(connectHost, connectPort);
+ // In case of no proxy we can use the Unbuffered QTcpSocket
+ if (connection->d_func()->networkProxy.type() == QNetworkProxy::NoProxy
+ && connection->cacheProxy().type() == QNetworkProxy::NoProxy
+ && connection->transparentProxy().type() == QNetworkProxy::NoProxy) {
+ socket->connectToHost(connectHost, connectPort, QIODevice::ReadWrite | QIODevice::Unbuffered);
+ // For an Unbuffered QTcpSocket, the read buffer size has a special meaning.
+ socket->setReadBufferSize(1*1024);
+
+ } else {
+ socket->connectToHost(connectHost, connectPort);
+
+ // limit the socket read buffer size. we will read everything into
+ // the QHttpNetworkReply anyway, so let's grow only that and not
+ // here and there.
+ socket->setReadBufferSize(64*1024);
+ }
}
return false;
}
@@ -589,18 +613,10 @@ bool QHttpNetworkConnectionChannel::expand(bool dataComplete)
reply->d_func()->totalProgress += inflated.size();
reply->d_func()->appendUncompressedReplyData(inflated);
if (reply->d_func()->shouldEmitSignals()) {
- QPointer<QHttpNetworkReply> replyPointer = 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 (replyPointer.isNull())
- return true;
emit reply->dataReadProgress(reply->d_func()->totalProgress, 0);
- // make sure that the reply is valid
- if (replyPointer.isNull())
- return true;
-
}
}
} else {
@@ -678,10 +694,15 @@ void QHttpNetworkConnectionChannel::allDone()
// this was wrong, allDone gets called from that function anyway.
}
} else if (alreadyPipelinedRequests.isEmpty() && socket->bytesAvailable() > 0) {
- eatWhitespace();
// this is weird. we had nothing pipelined but still bytes available. better close it.
- if (socket->bytesAvailable() > 0)
- close();
+ //if (socket->bytesAvailable() > 0)
+ // close();
+ //
+ // FIXME
+ // We do not close it anymore now, but should introduce this again after having fixed
+ // the chunked decoder in QHttpNetworkReply to read the whitespace after the last chunk.
+ // (Currently this is worked around by readStatus in the QHttpNetworkReply ignoring
+ // leading whitespace.
QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
} else if (alreadyPipelinedRequests.isEmpty()) {
if (qobject_cast<QHttpNetworkConnection*>(connection))
@@ -728,30 +749,6 @@ void QHttpNetworkConnectionChannel::requeueCurrentlyPipelinedRequests()
QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
}
-void QHttpNetworkConnectionChannel::eatWhitespace()
-{
- char c;
- do {
- qint64 ret = socket->peek(&c, 1);
-
- // nothing read, fine.
- if (ret == 0)
- return;
-
- // EOF from socket?
- if (ret == -1)
- return; // FIXME, we need to stop processing. however the next stuff done will also do that.
-
- // read all whitespace and line endings
- if (c == 11 || c == '\n' || c == '\r' || c == ' ' || c == 31) {
- socket->read(&c, 1);
- continue;
- } else {
- break;
- }
- } while(true);
-}
-
void QHttpNetworkConnectionChannel::handleStatus()
{
Q_ASSERT(socket);
@@ -813,7 +810,7 @@ bool QHttpNetworkConnectionChannel::resetUploadData()
}
-void QHttpNetworkConnectionChannel::pipelineInto(HttpMessagePair &pair)
+void QHttpNetworkConnectionChannel::pipelineInto(HttpMessagePair &pair)
{
// this is only called for simple GET
@@ -826,16 +823,32 @@ void QHttpNetworkConnectionChannel::pipelineInto(HttpMessagePair &pair)
reply->d_func()->pipeliningUsed = true;
#ifndef QT_NO_NETWORKPROXY
- QByteArray header = QHttpNetworkRequestPrivate::header(request,
- (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy));
+ pipeline.append(QHttpNetworkRequestPrivate::header(request,
+ (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy)));
#else
- QByteArray header = QHttpNetworkRequestPrivate::header(request, false);
+ pipeline.append(QHttpNetworkRequestPrivate::header(request, false));
#endif
- socket->write(header);
alreadyPipelinedRequests.append(pair);
+
+ // pipelineFlush() needs to be called at some point afterwards
}
+void QHttpNetworkConnectionChannel::pipelineFlush()
+{
+ if (pipeline.isEmpty())
+ return;
+
+ // The goal of this is so that we have everything in one TCP packet.
+ // For the Unbuffered QTcpSocket this is manually needed, the buffered
+ // QTcpSocket does it automatically.
+ // Also, sometimes the OS does it for us (Nagle's algorithm) but that
+ // happens only sometimes.
+ socket->write(pipeline);
+ pipeline.clear();
+}
+
+
void QHttpNetworkConnectionChannel::closeAndResendCurrentRequest()
{
requeueCurrentlyPipelinedRequests();
@@ -868,6 +881,19 @@ bool QHttpNetworkConnectionChannel::isSocketReading() const
//private slots
void QHttpNetworkConnectionChannel::_q_readyRead()
{
+ // We got a readyRead but no bytes are available..
+ // This happens for the Unbuffered QTcpSocket
+ // Also check if socket is in ConnectedState since
+ // this function may also be invoked via the event loop.
+ if (socket->state() == QAbstractSocket::ConnectedState && socket->bytesAvailable() == 0) {
+ char c;
+ qint64 ret = socket->peek(&c, 1);
+ if (ret < 0) {
+ socket->disconnectFromHost();
+ return;
+ }
+ }
+
if (isSocketWaiting() || isSocketReading()) {
state = QHttpNetworkConnectionChannel::ReadingState;
if (reply)
diff --git a/src/network/access/qhttpnetworkconnectionchannel_p.h b/src/network/access/qhttpnetworkconnectionchannel_p.h
index 41a896d..4f3a65c 100644
--- a/src/network/access/qhttpnetworkconnectionchannel_p.h
+++ b/src/network/access/qhttpnetworkconnectionchannel_p.h
@@ -99,6 +99,7 @@ public:
BusyState = (ConnectingState|WritingState|WaitingState|ReadingState|Wait4AuthState)
};
QAbstractSocket *socket;
+ bool ssl;
ChannelState state;
QHttpNetworkRequest request; // current request
QHttpNetworkReply *reply; // current reply for this request
@@ -125,7 +126,11 @@ public:
};
PipeliningSupport pipeliningSupported;
QList<HttpMessagePair> alreadyPipelinedRequests;
-
+ QByteArray pipeline; // temporary buffer that gets sent to socket in pipelineFlush
+ void pipelineInto(HttpMessagePair &pair);
+ void pipelineFlush();
+ void requeueCurrentlyPipelinedRequests();
+ void detectPipeliningSupport();
QHttpNetworkConnectionChannel();
@@ -145,15 +150,9 @@ public:
bool resetUploadData(); // return true if resetting worked or there is no upload data
- void pipelineInto(HttpMessagePair &pair);
- void requeueCurrentlyPipelinedRequests();
- void detectPipeliningSupport();
-
void handleUnexpectedEOF();
void closeAndResendCurrentRequest();
- void eatWhitespace();
-
bool isSocketBusy() const;
bool isSocketWriting() const;
bool isSocketWaiting() const;
diff --git a/src/network/access/qhttpnetworkreply.cpp b/src/network/access/qhttpnetworkreply.cpp
index 108ba8a..1c55482 100644
--- a/src/network/access/qhttpnetworkreply.cpp
+++ b/src/network/access/qhttpnetworkreply.cpp
@@ -119,6 +119,7 @@ void QHttpNetworkReply::setRequest(const QHttpNetworkRequest &request)
{
Q_D(QHttpNetworkReply);
d->request = request;
+ d->ssl = request.isSsl();
}
int QHttpNetworkReply::statusCode() const
@@ -195,6 +196,25 @@ void QHttpNetworkReply::setDownstreamLimited(bool dsl)
d->connection->d_func()->readMoreLater(this);
}
+bool QHttpNetworkReply::supportsUserProvidedDownloadBuffer()
+{
+ Q_D(QHttpNetworkReply);
+ return (!d->isChunked() && !d->autoDecompress && d->bodyLength > 0);
+}
+
+void QHttpNetworkReply::setUserProvidedDownloadBuffer(char* b)
+{
+ Q_D(QHttpNetworkReply);
+ if (supportsUserProvidedDownloadBuffer())
+ d->userProvidedDownloadBuffer = b;
+}
+
+char* QHttpNetworkReply::userProvidedDownloadBuffer()
+{
+ Q_D(QHttpNetworkReply);
+ return d->userProvidedDownloadBuffer;
+}
+
bool QHttpNetworkReply::isFinished() const
{
return d_func()->state == QHttpNetworkReplyPrivate::AllDoneState;
@@ -207,7 +227,10 @@ bool QHttpNetworkReply::isPipeliningUsed() const
QHttpNetworkReplyPrivate::QHttpNetworkReplyPrivate(const QUrl &newUrl)
- : QHttpNetworkHeaderPrivate(newUrl), state(NothingDoneState), statusCode(100),
+ : QHttpNetworkHeaderPrivate(newUrl)
+ , state(NothingDoneState)
+ , ssl(false)
+ , statusCode(100),
majorVersion(0), minorVersion(0), bodyLength(0), contentRead(0), totalProgress(0),
chunkedTransferEncoding(false),
connectionCloseEnabled(true),
@@ -215,6 +238,7 @@ QHttpNetworkReplyPrivate::QHttpNetworkReplyPrivate(const QUrl &newUrl)
currentChunkSize(0), currentChunkRead(0), connection(0), initInflate(false),
autoDecompress(false), responseData(), requestIsPrepared(false)
,pipeliningUsed(false), downstreamLimited(false)
+ ,userProvidedDownloadBuffer(0)
{
}
@@ -448,6 +472,8 @@ qint64 QHttpNetworkReplyPrivate::readStatus(QAbstractSocket *socket)
return -1; // unexpected EOF
else if (haveRead == 0)
break; // read more later
+ else if (haveRead == 1 && bytes == 0 && (c == 11 || c == '\n' || c == '\r' || c == ' ' || c == 31))
+ continue; // Ignore all whitespace that was trailing froma previous request on that socket
bytes++;
@@ -623,12 +649,32 @@ bool QHttpNetworkReplyPrivate::isConnectionCloseEnabled()
// note this function can only be used for non-chunked, non-compressed with
// known content length
+qint64 QHttpNetworkReplyPrivate::readBodyVeryFast(QAbstractSocket *socket, char *b)
+{
+ // This first read is to flush the buffer inside the socket
+ qint64 haveRead = 0;
+ haveRead = socket->read(b, bodyLength - contentRead);
+ if (haveRead == -1) {
+ return 0; // ### error checking here;
+ }
+ contentRead += haveRead;
+
+ if (contentRead == bodyLength) {
+ state = AllDoneState;
+ }
+
+ return haveRead;
+}
+
+// note this function can only be used for non-chunked, non-compressed with
+// known content length
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());
+ qint64 haveRead = socket->read(bd.data(), toBeRead);
if (haveRead == -1) {
bd.clear();
return 0; // ### error checking here;
@@ -650,29 +696,34 @@ qint64 QHttpNetworkReplyPrivate::readBody(QAbstractSocket *socket, QByteDataBuff
{
qint64 bytes = 0;
if (isChunked()) {
- bytes += readReplyBodyChunked(socket, out); // chunked transfer encoding (rfc 2616, sec 3.6)
- } else if (bodyLength > 0) { // we have a Content-Length
+ // chunked transfer encoding (rfc 2616, sec 3.6)
+ bytes += readReplyBodyChunked(socket, out);
+ } else if (bodyLength > 0) {
+ // we have a Content-Length
bytes += readReplyBodyRaw(socket, out, bodyLength - contentRead);
if (contentRead + bytes == bodyLength)
state = AllDoneState;
} else {
+ // no content length. just read what's possible
bytes += readReplyBodyRaw(socket, out, socket->bytesAvailable());
}
contentRead += bytes;
return bytes;
}
-qint64 QHttpNetworkReplyPrivate::readReplyBodyRaw(QIODevice *in, QByteDataBuffer *out, qint64 size)
+qint64 QHttpNetworkReplyPrivate::readReplyBodyRaw(QAbstractSocket *socket, QByteDataBuffer *out, qint64 size)
{
+ // FIXME get rid of this function and just use readBodyFast and give it socket->bytesAvailable()
qint64 bytes = 0;
- Q_ASSERT(in);
+ Q_ASSERT(socket);
Q_ASSERT(out);
- int toBeRead = qMin<qint64>(128*1024, qMin<qint64>(size, in->bytesAvailable()));
+ int toBeRead = qMin<qint64>(128*1024, qMin<qint64>(size, socket->bytesAvailable()));
+
while (toBeRead > 0) {
QByteArray byteData;
byteData.resize(toBeRead);
- qint64 haveRead = in->read(byteData.data(), byteData.size());
+ qint64 haveRead = socket->read(byteData.data(), byteData.size());
if (haveRead <= 0) {
// ### error checking here
byteData.clear();
@@ -684,25 +735,35 @@ qint64 QHttpNetworkReplyPrivate::readReplyBodyRaw(QIODevice *in, QByteDataBuffer
bytes += haveRead;
size -= haveRead;
- toBeRead = qMin<qint64>(128*1024, qMin<qint64>(size, in->bytesAvailable()));
+ toBeRead = qMin<qint64>(128*1024, qMin<qint64>(size, socket->bytesAvailable()));
}
return bytes;
}
-qint64 QHttpNetworkReplyPrivate::readReplyBodyChunked(QIODevice *in, QByteDataBuffer *out)
+qint64 QHttpNetworkReplyPrivate::readReplyBodyChunked(QAbstractSocket *socket, QByteDataBuffer *out)
{
qint64 bytes = 0;
- while (in->bytesAvailable()) { // while we can read from input
- // if we are done with the current chunk, get the size of the new chunk
+ while (socket->bytesAvailable()) {
if (currentChunkRead >= currentChunkSize) {
+ // For the first chunk and when we're done with a chunk
currentChunkSize = 0;
currentChunkRead = 0;
if (bytes) {
+ // After a chunk
char crlf[2];
- bytes += in->read(crlf, 2); // read the "\r\n" after the chunk
+ // read the "\r\n" after the chunk
+ qint64 haveRead = socket->read(crlf, 2);
+ // FIXME: This code is slightly broken and not optimal. What if the 2 bytes are not available yet?!
+ // For nice reasons (the toLong in getChunkSize accepting \n at the beginning
+ // it right now still works, but we should definitely fix this.
+
+ if (haveRead != 2)
+ return bytes; // FIXME
+ bytes += haveRead;
}
- bytes += getChunkSize(in, &currentChunkSize);
+ // Note that chunk size gets stored in currentChunkSize, what is returned is the bytes read
+ bytes += getChunkSize(socket, &currentChunkSize);
if (currentChunkSize == -1)
break;
}
@@ -712,8 +773,8 @@ qint64 QHttpNetworkReplyPrivate::readReplyBodyChunked(QIODevice *in, QByteDataBu
break;
}
- // otherwise, try to read what is missing for this chunk
- qint64 haveRead = readReplyBodyRaw (in, out, currentChunkSize - currentChunkRead);
+ // otherwise, try to begin reading this chunk / to read what is missing for this chunk
+ qint64 haveRead = readReplyBodyRaw (socket, out, currentChunkSize - currentChunkRead);
currentChunkRead += haveRead;
bytes += haveRead;
@@ -723,22 +784,25 @@ qint64 QHttpNetworkReplyPrivate::readReplyBodyChunked(QIODevice *in, QByteDataBu
return bytes;
}
-qint64 QHttpNetworkReplyPrivate::getChunkSize(QIODevice *in, qint64 *chunkSize)
+qint64 QHttpNetworkReplyPrivate::getChunkSize(QAbstractSocket *socket, qint64 *chunkSize)
{
qint64 bytes = 0;
char crlf[2];
*chunkSize = -1;
- int bytesAvailable = in->bytesAvailable();
+
+ int bytesAvailable = socket->bytesAvailable();
+ // FIXME rewrite to permanent loop without bytesAvailable
while (bytesAvailable > bytes) {
- qint64 sniffedBytes = in->peek(crlf, 2);
+ qint64 sniffedBytes = socket->peek(crlf, 2);
int fragmentSize = fragment.size();
+
// check the next two bytes for a "\r\n", skip blank lines
if ((fragmentSize && sniffedBytes == 2 && crlf[0] == '\r' && crlf[1] == '\n')
||(fragmentSize > 1 && fragment.endsWith('\r') && crlf[0] == '\n'))
{
- bytes += in->read(crlf, 1); // read the \r or \n
+ bytes += socket->read(crlf, 1); // read the \r or \n
if (crlf[0] == '\r')
- bytes += in->read(crlf, 1); // read the \n
+ bytes += socket->read(crlf, 1); // read the \n
bool ok = false;
// ignore the chunk-extension
fragment = fragment.mid(0, fragment.indexOf(';')).trimmed();
@@ -748,10 +812,15 @@ qint64 QHttpNetworkReplyPrivate::getChunkSize(QIODevice *in, qint64 *chunkSize)
} else {
// read the fragment to the buffer
char c = 0;
- bytes += in->read(&c, 1);
+ qint64 haveRead = socket->read(&c, 1);
+ if (haveRead < 0) {
+ return -1; // FIXME
+ }
+ bytes += haveRead;
fragment.append(c);
}
}
+
return bytes;
}
diff --git a/src/network/access/qhttpnetworkreply_p.h b/src/network/access/qhttpnetworkreply_p.h
index 4011c78..02ce248 100644
--- a/src/network/access/qhttpnetworkreply_p.h
+++ b/src/network/access/qhttpnetworkreply_p.h
@@ -128,6 +128,10 @@ public:
QByteArray readAny();
void setDownstreamLimited(bool t);
+ bool supportsUserProvidedDownloadBuffer();
+ void setUserProvidedDownloadBuffer(char*);
+ char* userProvidedDownloadBuffer();
+
bool isFinished() const;
bool isPipeliningUsed() const;
@@ -147,6 +151,7 @@ Q_SIGNALS:
void finished();
void finishedWithError(QNetworkReply::NetworkError errorCode, const QString &detail = QString());
void headerChanged();
+ // FIXME we need to change this to qint64!
void dataReadProgress(int done, int total);
void dataSendProgress(qint64 done, qint64 total);
@@ -168,15 +173,16 @@ public:
qint64 readHeader(QAbstractSocket *socket);
void parseHeader(const QByteArray &header);
qint64 readBody(QAbstractSocket *socket, QByteDataBuffer *out);
+ qint64 readBodyVeryFast(QAbstractSocket *socket, char *b);
qint64 readBodyFast(QAbstractSocket *socket, QByteDataBuffer *rb);
bool findChallenge(bool forProxy, QByteArray &challenge) const;
QAuthenticatorPrivate::Method authenticationMethod(bool isProxy) const;
void clear();
void clearHttpLayerInformation();
- qint64 readReplyBodyRaw(QIODevice *in, QByteDataBuffer *out, qint64 size);
- qint64 readReplyBodyChunked(QIODevice *in, QByteDataBuffer *out);
- qint64 getChunkSize(QIODevice *in, qint64 *chunkSize);
+ qint64 readReplyBodyRaw(QAbstractSocket *in, QByteDataBuffer *out, qint64 size);
+ qint64 readReplyBodyChunked(QAbstractSocket *in, QByteDataBuffer *out);
+ qint64 getChunkSize(QAbstractSocket *in, qint64 *chunkSize);
void appendUncompressedReplyData(QByteArray &qba);
void appendUncompressedReplyData(QByteDataBuffer &data);
@@ -205,6 +211,7 @@ public:
} state;
QHttpNetworkRequest request;
+ bool ssl;
int statusCode;
int majorVersion;
int minorVersion;
@@ -234,6 +241,8 @@ public:
bool pipeliningUsed;
bool downstreamLimited;
+
+ char* userProvidedDownloadBuffer;
};
diff --git a/src/network/access/qhttpnetworkrequest.cpp b/src/network/access/qhttpnetworkrequest.cpp
index 639025e..d2f3212 100644
--- a/src/network/access/qhttpnetworkrequest.cpp
+++ b/src/network/access/qhttpnetworkrequest.cpp
@@ -63,6 +63,7 @@ QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(const QHttpNetworkRequest
pipeliningAllowed = other.pipeliningAllowed;
customVerb = other.customVerb;
withCredentials = other.withCredentials;
+ ssl = other.ssl;
}
QHttpNetworkRequestPrivate::~QHttpNetworkRequestPrivate()
@@ -73,6 +74,7 @@ bool QHttpNetworkRequestPrivate::operator==(const QHttpNetworkRequestPrivate &ot
{
return QHttpNetworkHeaderPrivate::operator==(other)
&& (operation == other.operation)
+ && (ssl == other.ssl)
&& (uploadByteDevice == other.uploadByteDevice);
}
@@ -199,6 +201,15 @@ void QHttpNetworkRequest::setUrl(const QUrl &url)
d->url = url;
}
+bool QHttpNetworkRequest::isSsl() const
+{
+ return d->ssl;
+}
+void QHttpNetworkRequest::setSsl(bool s)
+{
+ d->ssl = s;
+}
+
qint64 QHttpNetworkRequest::contentLength() const
{
return d->contentLength();
diff --git a/src/network/access/qhttpnetworkrequest_p.h b/src/network/access/qhttpnetworkrequest_p.h
index 15cab73..123babc 100644
--- a/src/network/access/qhttpnetworkrequest_p.h
+++ b/src/network/access/qhttpnetworkrequest_p.h
@@ -116,6 +116,9 @@ public:
bool withCredentials() const;
void setWithCredentials(bool b);
+ bool isSsl() const;
+ void setSsl(bool);
+
void setUploadByteDevice(QNonContiguousByteDevice *bd);
QNonContiguousByteDevice* uploadByteDevice() const;
@@ -146,6 +149,7 @@ public:
bool autoDecompress;
bool pipeliningAllowed;
bool withCredentials;
+ bool ssl;
};
diff --git a/src/network/access/qnetworkaccessbackend.cpp b/src/network/access/qnetworkaccessbackend.cpp
index 60f7dc6..c3d765b 100644
--- a/src/network/access/qnetworkaccessbackend.cpp
+++ b/src/network/access/qnetworkaccessbackend.cpp
@@ -146,7 +146,7 @@ QNonContiguousByteDevice* QNetworkAccessBackend::createUploadByteDevice()
// and the special backends need to access this.
void QNetworkAccessBackend::emitReplyUploadProgress(qint64 bytesSent, qint64 bytesTotal)
{
- if (reply->isFinished())
+ if (reply->isFinished)
return;
reply->emitUploadProgress(bytesSent, bytesTotal);
}
@@ -252,6 +252,17 @@ void QNetworkAccessBackend::writeDownstreamData(QIODevice *data)
reply->appendDownstreamData(data);
}
+// not actually appending data, it was already written to the user buffer
+void QNetworkAccessBackend::writeDownstreamDataDownloadBuffer(qint64 bytesReceived, qint64 bytesTotal)
+{
+ reply->appendDownstreamDataDownloadBuffer(bytesReceived, bytesTotal);
+}
+
+char* QNetworkAccessBackend::getDownloadBuffer(qint64 size)
+{
+ return reply->getDownloadBuffer(size);
+}
+
QVariant QNetworkAccessBackend::header(QNetworkRequest::KnownHeaders header) const
{
return reply->q_func()->header(header);
diff --git a/src/network/access/qnetworkaccessbackend_p.h b/src/network/access/qnetworkaccessbackend_p.h
index 4fe6de6..9f8a01f 100644
--- a/src/network/access/qnetworkaccessbackend_p.h
+++ b/src/network/access/qnetworkaccessbackend_p.h
@@ -177,6 +177,10 @@ protected:
qint64 nextDownstreamBlockSize() const;
void writeDownstreamData(QByteDataBuffer &list);
+ // not actually appending data, it was already written to the user buffer
+ void writeDownstreamDataDownloadBuffer(qint64, qint64);
+ char* getDownloadBuffer(qint64);
+
public slots:
// for task 251801, needs to be a slot to be called asynchronously
void writeDownstreamData(QIODevice *data);
diff --git a/src/network/access/qnetworkaccessfilebackend.cpp b/src/network/access/qnetworkaccessfilebackend.cpp
index 4560153..710c258 100644
--- a/src/network/access/qnetworkaccessfilebackend.cpp
+++ b/src/network/access/qnetworkaccessfilebackend.cpp
@@ -65,10 +65,15 @@ QNetworkAccessFileBackendFactory::create(QNetworkAccessManager::Operation op,
}
QUrl url = request.url();
- if (url.scheme() == QLatin1String("qrc") || !url.toLocalFile().isEmpty())
+ if (url.scheme().compare(QLatin1String("qrc"), Qt::CaseInsensitive) == 0 || url.isLocalFile()) {
return new QNetworkAccessFileBackend;
- else if (!url.isEmpty() && url.authority().isEmpty()) {
- // check if QFile could, in theory, open this URL
+ } else if (!url.scheme().isEmpty() && url.authority().isEmpty()) {
+ // check if QFile could, in theory, open this URL via the file engines
+ // it has to be in the format:
+ // prefix:path/to/file
+ // or prefix:/path/to/file
+ //
+ // this construct here must match the one below in open()
QFileInfo fi(url.toString(QUrl::RemoveAuthority | QUrl::RemoveFragment | QUrl::RemoveQuery));
if (fi.exists() || (op == QNetworkAccessManager::PutOperation && fi.dir().exists()))
return new QNetworkAccessFileBackend;
diff --git a/src/network/access/qnetworkaccessftpbackend.cpp b/src/network/access/qnetworkaccessftpbackend.cpp
index 1a59011..da336d0 100644
--- a/src/network/access/qnetworkaccessftpbackend.cpp
+++ b/src/network/access/qnetworkaccessftpbackend.cpp
@@ -77,7 +77,7 @@ QNetworkAccessFtpBackendFactory::create(QNetworkAccessManager::Operation op,
}
QUrl url = request.url();
- if (url.scheme() == QLatin1String("ftp"))
+ if (url.scheme().compare(QLatin1String("ftp"), Qt::CaseInsensitive) == 0)
return new QNetworkAccessFtpBackend;
return 0;
}
diff --git a/src/network/access/qnetworkaccesshttpbackend.cpp b/src/network/access/qnetworkaccesshttpbackend.cpp
index f617244..8b9a99f 100644
--- a/src/network/access/qnetworkaccesshttpbackend.cpp
+++ b/src/network/access/qnetworkaccesshttpbackend.cpp
@@ -542,6 +542,9 @@ void QNetworkAccessHttpBackend::postRequest()
break; // can't happen
}
+ bool encrypt = (url().scheme().toLower() == QLatin1String("https"));
+ httpRequest.setSsl(encrypt);
+
httpRequest.setUrl(url());
QList<QByteArray> headers = request().rawHeaderList();
@@ -595,7 +598,6 @@ void QNetworkAccessHttpBackend::postRequest()
httpReply->ignoreSslErrors(pendingIgnoreSslErrorsList);
#endif
- connect(httpReply, SIGNAL(readyRead()), SLOT(replyReadyRead()));
connect(httpReply, SIGNAL(finished()), SLOT(replyFinished()));
connect(httpReply, SIGNAL(finishedWithError(QNetworkReply::NetworkError,QString)),
SLOT(httpError(QNetworkReply::NetworkError,QString)));
@@ -859,9 +861,33 @@ void QNetworkAccessHttpBackend::replyHeaderChanged()
if (!isCachingEnabled())
setCachingEnabled(true);
}
+
+ // Check if a download buffer is supported from the HTTP reply
+ char *buf = 0;
+ if (httpReply->supportsUserProvidedDownloadBuffer()) {
+ // Check if a download buffer is supported by the user
+ buf = getDownloadBuffer(httpReply->contentLength());
+ if (buf) {
+ httpReply->setUserProvidedDownloadBuffer(buf);
+ // If there is a download buffer we react on the progress signal
+ connect(httpReply, SIGNAL(dataReadProgress(int,int)), SLOT(replyDownloadProgressSlot(int,int)));
+ }
+ }
+
+ // If there is no buffer, we react on the readyRead signal
+ if (!buf) {
+ connect(httpReply, SIGNAL(readyRead()), SLOT(replyReadyRead()));
+ }
+
metaDataChanged();
}
+void QNetworkAccessHttpBackend::replyDownloadProgressSlot(int received, int total)
+{
+ // we can be sure here that there is a download buffer
+ writeDownstreamDataDownloadBuffer(received, total);
+}
+
void QNetworkAccessHttpBackend::httpAuthenticationRequired(const QHttpNetworkRequest &,
QAuthenticator *auth)
{
@@ -1169,6 +1195,11 @@ bool QNetworkAccessHttpBackend::canResume() const
return false;
}
+ // If we're using a download buffer then we don't support resuming/migration
+ // right now. Too much trouble.
+ if (httpReply->userProvidedDownloadBuffer())
+ return false;
+
return true;
}
diff --git a/src/network/access/qnetworkaccesshttpbackend_p.h b/src/network/access/qnetworkaccesshttpbackend_p.h
index c4c88ae..fb12781 100644
--- a/src/network/access/qnetworkaccesshttpbackend_p.h
+++ b/src/network/access/qnetworkaccesshttpbackend_p.h
@@ -104,6 +104,7 @@ private slots:
void replyReadyRead();
void replyFinished();
void replyHeaderChanged();
+ void replyDownloadProgressSlot(int,int);
void httpAuthenticationRequired(const QHttpNetworkRequest &request, QAuthenticator *auth);
void httpCacheCredentials(const QHttpNetworkRequest &request, QAuthenticator *auth);
void httpError(QNetworkReply::NetworkError error, const QString &errorString);
diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp
index a637474..86b1c66 100644
--- a/src/network/access/qnetworkaccessmanager.cpp
+++ b/src/network/access/qnetworkaccessmanager.cpp
@@ -450,6 +450,8 @@ QNetworkAccessManager::QNetworkAccessManager(QObject *parent)
: QObject(*new QNetworkAccessManagerPrivate, parent)
{
ensureInitialized();
+
+ qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError");
}
/*!
@@ -944,21 +946,19 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
{
Q_D(QNetworkAccessManager);
+ bool isLocalFile = req.url().isLocalFile();
+
// fast path for GET on file:// URLs
- // Also if the scheme is empty we consider it a file.
// The QNetworkAccessFileBackend will right now only be used for PUT
if ((op == QNetworkAccessManager::GetOperation || op == QNetworkAccessManager::HeadOperation)
- && (req.url().scheme() == QLatin1String("file")
- || req.url().scheme() == QLatin1String("qrc")
- || req.url().scheme().isEmpty())) {
+ && (isLocalFile || req.url().scheme() == QLatin1String("qrc"))) {
return new QFileNetworkReply(this, req, op);
}
#ifndef QT_NO_BEARERMANAGEMENT
// Return a disabled network reply if network access is disabled.
// Except if the scheme is empty or file://.
- if (!d->networkAccessible && !(req.url().scheme() == QLatin1String("file") ||
- req.url().scheme().isEmpty())) {
+ if (!d->networkAccessible && !isLocalFile) {
return new QDisabledNetworkReply(this, req, op);
}
@@ -992,7 +992,7 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
if (d->cookieJar) {
QList<QNetworkCookie> cookies = d->cookieJar->cookiesForUrl(request.url());
if (!cookies.isEmpty())
- request.setHeader(QNetworkRequest::CookieHeader, qVariantFromValue(cookies));
+ request.setHeader(QNetworkRequest::CookieHeader, QVariant::fromValue(cookies));
}
}
@@ -1000,7 +1000,7 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
QUrl url = request.url();
QNetworkReplyImpl *reply = new QNetworkReplyImpl(this);
#ifndef QT_NO_BEARERMANAGEMENT
- if (req.url().scheme() != QLatin1String("file") && !req.url().scheme().isEmpty()) {
+ if (!isLocalFile) {
connect(this, SIGNAL(networkSessionConnected()),
reply, SLOT(_q_networkSessionConnected()));
}
diff --git a/src/network/access/qnetworkreply.cpp b/src/network/access/qnetworkreply.cpp
index 261ed20..f55f67d 100644
--- a/src/network/access/qnetworkreply.cpp
+++ b/src/network/access/qnetworkreply.cpp
@@ -49,6 +49,7 @@ QNetworkReplyPrivate::QNetworkReplyPrivate()
: readBufferMaxSize(0),
operation(QNetworkAccessManager::UnknownOperation),
errorCode(QNetworkReply::NoError)
+ , isFinished(false)
{
// set the default attribute values
attributes.insert(QNetworkRequest::ConnectionEncryptedAttribute, false);
@@ -462,7 +463,7 @@ QNetworkReply::NetworkError QNetworkReply::error() const
*/
bool QNetworkReply::isFinished() const
{
- return d_func()->isFinished();
+ return d_func()->isFinished;
}
/*!
@@ -718,6 +719,21 @@ void QNetworkReply::setError(NetworkError errorCode, const QString &errorString)
}
/*!
+ \since 4.8
+ Sets the reply as \a finished.
+
+ After having this set the replies data must not change.
+
+ \sa isFinished()
+*/
+void QNetworkReply::setFinished(bool finished)
+{
+ Q_D(QNetworkReply);
+ d->isFinished = finished;
+}
+
+
+/*!
Sets the URL being processed to be \a url. Normally, the URL
matches that of the request that was posted, but for a variety of
reasons it can be different (for example, a file path being made
diff --git a/src/network/access/qnetworkreply.h b/src/network/access/qnetworkreply.h
index acb7379..b39fd32 100644
--- a/src/network/access/qnetworkreply.h
+++ b/src/network/access/qnetworkreply.h
@@ -163,6 +163,7 @@ protected:
void setOperation(QNetworkAccessManager::Operation operation);
void setRequest(const QNetworkRequest &request);
void setError(NetworkError errorCode, const QString &errorString);
+ void setFinished(bool);
void setUrl(const QUrl &url);
void setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value);
void setRawHeader(const QByteArray &headerName, const QByteArray &value);
diff --git a/src/network/access/qnetworkreply_p.h b/src/network/access/qnetworkreply_p.h
index d7b8ab0..2e2e0bc 100644
--- a/src/network/access/qnetworkreply_p.h
+++ b/src/network/access/qnetworkreply_p.h
@@ -71,12 +71,11 @@ public:
qint64 readBufferMaxSize;
QNetworkAccessManager::Operation operation;
QNetworkReply::NetworkError errorCode;
+ bool isFinished;
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 5850494..3a629cf 100644
--- a/src/network/access/qnetworkreplyimpl.cpp
+++ b/src/network/access/qnetworkreplyimpl.cpp
@@ -49,10 +49,16 @@
#include "QtNetwork/qnetworksession.h"
#include "qnetworkaccesshttpbackend_p.h"
#include "qnetworkaccessmanager_p.h"
+#include <QVarLengthArray>
#include <QtCore/QCoreApplication>
QT_BEGIN_NAMESPACE
+typedef QSharedPointer<QVarLengthArray<char, 0> > QVarLengthArraySharedPointer;
+QT_END_NAMESPACE
+Q_DECLARE_METATYPE(QVarLengthArraySharedPointer)
+
+QT_BEGIN_NAMESPACE
inline QNetworkReplyImplPrivate::QNetworkReplyImplPrivate()
: backend(0), outgoingData(0), outgoingDataBuffer(0),
@@ -62,6 +68,8 @@ inline QNetworkReplyImplPrivate::QNetworkReplyImplPrivate()
bytesDownloaded(0), lastBytesDownloaded(-1), bytesUploaded(-1), preMigrationDownloaded(-1),
httpStatusCode(0),
state(Idle)
+ , downloadBuffer(0)
+ , downloadBufferPosition(0)
{
}
@@ -125,6 +133,10 @@ void QNetworkReplyImplPrivate::_q_copyReadyRead()
if (!copyDevice || !q->isOpen())
return;
+ // FIXME Optimize to use download buffer if it is a QBuffer.
+ // Needs to be done where sendCacheContents() (?) of HTTP is emitting
+ // metaDataChanged ?
+
forever {
qint64 bytesToRead = nextDownstreamBlockSize();
if (bytesToRead == 0)
@@ -543,8 +555,6 @@ void QNetworkReplyImplPrivate::appendDownstreamDataSignalEmissions()
{
Q_Q(QNetworkReplyImpl);
- QPointer<QNetworkReplyImpl> qq = q;
-
QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
if (preMigrationDownloaded != Q_INT64_C(-1))
totalSize = totalSize.toLongLong() + preMigrationDownloaded;
@@ -555,13 +565,10 @@ void QNetworkReplyImplPrivate::appendDownstreamDataSignalEmissions()
// else implicit sharing will trigger memcpy when the user is reading data!
emit q->readyRead();
- // hopefully we haven't been deleted here
- if (!qq.isNull()) {
- resumeNotificationHandling();
- // do we still have room in the buffer?
- if (nextDownstreamBlockSize() > 0)
- backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
- }
+ resumeNotificationHandling();
+ // do we still have room in the buffer?
+ if (nextDownstreamBlockSize() > 0)
+ backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
}
// this is used when it was fetched from the cache, right?
@@ -596,6 +603,55 @@ void QNetworkReplyImplPrivate::appendDownstreamData(const QByteArray &data)
qFatal("QNetworkReplyImplPrivate::appendDownstreamData not implemented");
}
+char* QNetworkReplyImplPrivate::getDownloadBuffer(qint64 size)
+{
+ Q_Q(QNetworkReplyImpl);
+
+ // Check attribute() if allocating a buffer of that size can be allowed
+ if (!downloadBuffer) {
+ QVariant bufferAllocationPolicy = request.attribute(QNetworkRequest::MaximumDownloadBufferSizeAttribute);
+ if (bufferAllocationPolicy.isValid() && bufferAllocationPolicy.toLongLong() >= size) {
+ downloadBufferArray = QSharedPointer<QVarLengthArray<char, 0> >(new QVarLengthArray<char, 0>());
+ downloadBufferArray->reserve(size);
+
+ downloadBuffer = downloadBufferArray->data();
+
+ q->setAttribute(QNetworkRequest::DownloadBufferAttribute, qVariantFromValue<QSharedPointer<QVarLengthArray<char, 0> > > (downloadBufferArray));
+ }
+ }
+
+ return downloadBuffer;
+}
+
+void QNetworkReplyImplPrivate::appendDownstreamDataDownloadBuffer(qint64 bytesReceived, qint64 bytesTotal)
+{
+ Q_Q(QNetworkReplyImpl);
+ if (!q->isOpen())
+ return;
+
+ if (cacheEnabled && !cacheSaveDevice)
+ 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;
+
+ // Update the array so our user (e.g. QtWebKit) knows the real size
+ if (bytesReceived > 0)
+ downloadBufferArray->resize(bytesReceived);
+
+ emit q->downloadProgress(bytesDownloaded, bytesTotal);
+ emit q->readyRead();
+}
+
void QNetworkReplyImplPrivate::finished()
{
Q_Q(QNetworkReplyImpl);
@@ -634,6 +690,8 @@ void QNetworkReplyImplPrivate::finished()
resumeNotificationHandling();
state = Finished;
+ q->setFinished(true);
+
pendingNotifications.clear();
pauseNotificationHandling();
@@ -704,11 +762,6 @@ 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)
{
@@ -743,7 +796,7 @@ void QNetworkReplyImpl::abort()
QNetworkReply::close();
if (d->state != QNetworkReplyImplPrivate::Finished) {
- // emit signals
+ // call finished which will emit signals
d->error(OperationCanceledError, tr("Operation canceled"));
d->finished();
}
@@ -771,7 +824,7 @@ void QNetworkReplyImpl::close()
QNetworkReply::close();
- // emit signals
+ // call finished which will emit signals
d->error(OperationCanceledError, tr("Operation canceled"));
d->finished();
}
@@ -790,6 +843,13 @@ bool QNetworkReplyImpl::canReadLine () const
*/
qint64 QNetworkReplyImpl::bytesAvailable() const
{
+ // Special case for the "zero copy" download buffer
+ Q_D(const QNetworkReplyImpl);
+ if (d->downloadBuffer) {
+ qint64 maxAvail = d->downloadBufferArray->size() - d->downloadBufferPosition;
+ return QNetworkReply::bytesAvailable() + maxAvail;
+ }
+
return QNetworkReply::bytesAvailable() + d_func()->readBuffer.byteAmount();
}
@@ -844,8 +904,22 @@ void QNetworkReplyImpl::ignoreSslErrorsImplementation(const QList<QSslError> &er
qint64 QNetworkReplyImpl::readData(char *data, qint64 maxlen)
{
Q_D(QNetworkReplyImpl);
+
+ // Special case code if we have the "zero copy" download buffer
+ if (d->downloadBuffer) {
+ qint64 maxAvail = qMin<qint64>(d->downloadBufferArray->size() - d->downloadBufferPosition, maxlen);
+ if (maxAvail == 0)
+ return d->state == QNetworkReplyImplPrivate::Finished ? -1 : 0;
+ // FIXME what about "Aborted" state?
+ qMemCopy(data, d->downloadBuffer + d->downloadBufferPosition, maxAvail);
+ d->downloadBufferPosition += maxAvail;
+ return maxAvail;
+ }
+
+
if (d->readBuffer.isEmpty())
return d->state == QNetworkReplyImplPrivate::Finished ? -1 : 0;
+ // FIXME what about "Aborted" state?
d->backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
if (maxlen == 1) {
diff --git a/src/network/access/qnetworkreplyimpl_p.h b/src/network/access/qnetworkreplyimpl_p.h
index 38084bd..2cb3082 100644
--- a/src/network/access/qnetworkreplyimpl_p.h
+++ b/src/network/access/qnetworkreplyimpl_p.h
@@ -62,6 +62,7 @@
#include "QtCore/qbuffer.h"
#include "private/qringbuffer_p.h"
#include "private/qbytedata_p.h"
+#include <QVarLengthArray>
QT_BEGIN_NAMESPACE
@@ -163,14 +164,15 @@ public:
void appendDownstreamData(QIODevice *data);
void appendDownstreamData(const QByteArray &data);
+ char* getDownloadBuffer(qint64 size);
+ void appendDownstreamDataDownloadBuffer(qint64, qint64);
+
void finished();
void error(QNetworkReply::NetworkError code, const QString &errorString);
void metaDataChanged();
void redirectionRequested(const QUrl &target);
void sslErrors(const QList<QSslError> &errors);
- bool isFinished() const;
-
QNetworkAccessBackend *backend;
QIODevice *outgoingData;
QRingBuffer *outgoingDataBuffer;
@@ -191,6 +193,7 @@ public:
QList<QNetworkProxy> proxyList;
#endif
+ // Used for normal downloading. For "zero copy" the downloadBuffer is used
QByteDataBuffer readBuffer;
qint64 bytesDownloaded;
qint64 lastBytesDownloaded;
@@ -202,6 +205,11 @@ public:
State state;
+ // only used when the "zero copy" style is used. Else readBuffer is used.
+ QSharedPointer< QVarLengthArray<char, 0> > downloadBufferArray;
+ char* downloadBuffer;
+ qint64 downloadBufferPosition;
+
Q_DECLARE_PUBLIC(QNetworkReplyImpl)
};
diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp
index d3084cf..09ec4c2 100644
--- a/src/network/access/qnetworkrequest.cpp
+++ b/src/network/access/qnetworkrequest.cpp
@@ -766,7 +766,7 @@ static QVariant parseCookieHeader(const QByteArray &raw)
result += parsed;
}
- return qVariantFromValue(result);
+ return QVariant::fromValue(result);
}
static QVariant parseHeaderValue(QNetworkRequest::KnownHeaders header, const QByteArray &value)
@@ -799,7 +799,7 @@ static QVariant parseHeaderValue(QNetworkRequest::KnownHeaders header, const QBy
return parseCookieHeader(value);
case QNetworkRequest::SetCookieHeader:
- return qVariantFromValue(QNetworkCookie::parseCookies(value));
+ return QVariant::fromValue(QNetworkCookie::parseCookies(value));
default:
Q_ASSERT(0);