summaryrefslogtreecommitdiffstats
path: root/src/network/access/qhttpnetworkconnection.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/network/access/qhttpnetworkconnection.cpp')
-rw-r--r--src/network/access/qhttpnetworkconnection.cpp229
1 files changed, 129 insertions, 100 deletions
diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp
index 5940fba..d4bf008 100644
--- a/src/network/access/qhttpnetworkconnection.cpp
+++ b/src/network/access/qhttpnetworkconnection.cpp
@@ -40,6 +40,7 @@
****************************************************************************/
#include "qhttpnetworkconnection_p.h"
+#include "private/qnoncontiguousbytedevice_p.h"
#include <private/qnetworkrequest_p.h>
#include <private/qobject_p.h>
#include <private/qauthenticator_p.h>
@@ -71,6 +72,7 @@ QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(const QString &host
#ifndef QT_NO_NETWORKPROXY
, networkProxy(QNetworkProxy::NoProxy)
#endif
+
{
}
@@ -205,12 +207,19 @@ void QHttpNetworkConnectionPrivate::prepareRequest(HttpMessagePair &messagePair)
// add missing fields for the request
QByteArray value;
// check if Content-Length is provided
- QIODevice *data = request.data();
- if (data && request.contentLength() == -1) {
- if (!data->isSequential())
- request.setContentLength(data->size());
- else
- bufferData(messagePair); // ### or do chunked upload
+ QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
+ if (uploadByteDevice) {
+ if (request.contentLength() != -1 && uploadByteDevice->size() != -1) {
+ // both values known, take the smaller one.
+ request.setContentLength(qMin(uploadByteDevice->size(), request.contentLength()));
+ } else if (request.contentLength() == -1 && uploadByteDevice->size() != -1) {
+ // content length not supplied by user, but the upload device knows it
+ request.setContentLength(uploadByteDevice->size());
+ } else if (request.contentLength() != -1 && uploadByteDevice->size() == -1) {
+ // everything OK, the user supplied us the contentLength
+ } else if (request.contentLength() == -1 && uploadByteDevice->size() == -1) {
+ qFatal("QHttpNetworkConnectionPrivate: Neither content-length nor upload device size were given");
+ }
}
// set the Connection/Proxy-Connection: Keep-Alive headers
#ifndef QT_NO_NETWORKPROXY
@@ -361,18 +370,12 @@ bool QHttpNetworkConnectionPrivate::sendRequest(QAbstractSocket *socket)
false);
#endif
socket->write(header);
- QIODevice *data = channels[i].request.d->data;
- QHttpNetworkReply *reply = channels[i].reply;
- if (reply && reply->d_func()->requestDataBuffer.size())
- data = &channels[i].reply->d_func()->requestDataBuffer;
- if (data && (data->isOpen() || data->open(QIODevice::ReadOnly))) {
- if (data->isSequential()) {
- channels[i].bytesTotal = -1;
- QObject::connect(data, SIGNAL(readyRead()), q, SLOT(_q_dataReadyReadNoBuffer()));
- QObject::connect(data, SIGNAL(readChannelFinished()), q, SLOT(_q_dataReadyReadNoBuffer()));
- } else {
- channels[i].bytesTotal = data->size();
- }
+ QNonContiguousByteDevice* uploadByteDevice = channels[i].request.uploadByteDevice();
+ if (uploadByteDevice) {
+ // connect the signals so this function gets called again
+ QObject::connect(uploadByteDevice, SIGNAL(readyRead()), q, SLOT(_q_uploadDataReadyRead()));
+
+ channels[i].bytesTotal = channels[i].request.contentLength();
} else {
channels[i].state = WaitingState;
break;
@@ -380,30 +383,81 @@ bool QHttpNetworkConnectionPrivate::sendRequest(QAbstractSocket *socket)
// write the initial chunk together with the headers
// fall through
}
- case WritingState: { // write the data
- QIODevice *data = channels[i].request.d->data;
- if (channels[i].reply->d_func()->requestDataBuffer.size())
- data = &channels[i].reply->d_func()->requestDataBuffer;
- if (!data || channels[i].bytesTotal == channels[i].written) {
+ case WritingState:
+ {
+ // write the data
+ QNonContiguousByteDevice* uploadByteDevice = channels[i].request.uploadByteDevice();
+ if (!uploadByteDevice || channels[i].bytesTotal == channels[i].written) {
+ if (uploadByteDevice)
+ emit channels[i].reply->dataSendProgress(channels[i].written, channels[i].bytesTotal);
channels[i].state = WaitingState; // now wait for response
+ sendRequest(socket);
break;
}
- QByteArray chunk;
- chunk.resize(ChunkSize);
- qint64 readSize = data->read(chunk.data(), ChunkSize);
- if (readSize == -1) {
- // source has reached EOF
- channels[i].state = WaitingState; // now wait for response
- } else if (readSize > 0) {
- // source gave us something useful
- channels[i].written += socket->write(chunk.data(), readSize);
- if (channels[i].reply)
- emit channels[i].reply->dataSendProgress(channels[i].written, channels[i].bytesTotal);
+ // only feed the QTcpSocket buffer when there is less than 32 kB in it
+ const qint64 socketBufferFill = 32*1024;
+ const qint64 socketWriteMaxSize = 16*1024;
+
+
+#ifndef QT_NO_OPENSSL
+ QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket);
+ while ((sslSocket->encryptedBytesToWrite() + sslSocket->bytesToWrite()) <= socketBufferFill
+ && channels[i].bytesTotal != channels[i].written)
+#else
+ while (socket->bytesToWrite() <= socketBufferFill
+ && channels[i].bytesTotal != channels[i].written)
+#endif
+ {
+ // get pointer to upload data
+ qint64 currentReadSize;
+ qint64 desiredReadSize = qMin(socketWriteMaxSize, channels[i].bytesTotal - channels[i].written);
+ const char *readPointer = uploadByteDevice->readPointer(desiredReadSize, currentReadSize);
+
+ if (currentReadSize == -1) {
+ // premature eof happened
+ emitReplyError(socket, channels[i].reply, QNetworkReply::UnknownNetworkError);
+ return false;
+ break;
+ } else if (readPointer == 0 || currentReadSize == 0) {
+ // nothing to read currently, break the loop
+ break;
+ } else {
+ qint64 currentWriteSize = socket->write(readPointer, currentReadSize);
+ if (currentWriteSize == -1 || currentWriteSize != currentReadSize) {
+ // socket broke down
+ emitReplyError(socket, channels[i].reply, QNetworkReply::UnknownNetworkError);
+ return false;
+ } else {
+ channels[i].written += currentWriteSize;
+ uploadByteDevice->advanceReadPointer(currentWriteSize);
+
+ emit channels[i].reply->dataSendProgress(channels[i].written, channels[i].bytesTotal);
+
+ if (channels[i].written == channels[i].bytesTotal) {
+ // make sure this function is called once again
+ channels[i].state = WaitingState;
+ sendRequest(socket);
+ break;
+ }
+ }
+ }
}
break;
}
+
case WaitingState:
+ {
+ QNonContiguousByteDevice* uploadByteDevice = channels[i].request.uploadByteDevice();
+ if (uploadByteDevice) {
+ QObject::disconnect(uploadByteDevice, SIGNAL(readyRead()), q, SLOT(_q_uploadDataReadyRead()));
+ }
+ // ensure we try to receive a reply in all cases, even if _q_readyRead_ hat not been called
+ // this is needed if the sends an reply before we have finished sending the request. In that
+ // case receiveReply had been called before but ignored the server reply
+ receiveReply(socket, channels[i].reply);
+ break;
+ }
case ReadingState:
case Wait4AuthState:
// ignore _q_bytesWritten in these states
@@ -479,6 +533,9 @@ bool QHttpNetworkConnectionPrivate::expand(QAbstractSocket *socket, QHttpNetwork
// make sure that the reply is valid
if (channels[i].reply != reply)
return true;
+ // emit dataReadProgress signal (signal is currently not connected
+ // to the rest of QNAM) since readProgress of the
+ // QNonContiguousByteDevice is used
emit reply->dataReadProgress(reply->d_func()->totalProgress, 0);
// make sure that the reply is valid
if (channels[i].reply != reply)
@@ -529,10 +586,20 @@ void QHttpNetworkConnectionPrivate::receiveReply(QAbstractSocket *socket, QHttpN
QHttpNetworkReplyPrivate::ReplyState state = reply ? reply->d_func()->state : QHttpNetworkReplyPrivate::AllDoneState;
switch (state) {
case QHttpNetworkReplyPrivate::NothingDoneState:
- case QHttpNetworkReplyPrivate::ReadingStatusState:
- bytes += reply->d_func()->readStatus(socket);
+ case QHttpNetworkReplyPrivate::ReadingStatusState: {
+ qint64 statusBytes = reply->d_func()->readStatus(socket);
+ if (statusBytes == -1) {
+ // error reading the status, close the socket and emit error
+ socket->close();
+ reply->d_func()->errorString = errorDetail(QNetworkReply::ProtocolFailure, socket);
+ emit reply->finishedWithError(QNetworkReply::ProtocolFailure, reply->d_func()->errorString);
+ QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
+ break;
+ }
+ bytes += statusBytes;
channels[i].lastStatus = reply->d_func()->statusCode;
break;
+ }
case QHttpNetworkReplyPrivate::ReadingHeaderState:
bytes += reply->d_func()->readHeader(socket);
if (reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingDataState) {
@@ -569,6 +636,9 @@ void QHttpNetworkConnectionPrivate::receiveReply(QAbstractSocket *socket, QHttpN
// make sure that the reply is valid
if (channels[i].reply != reply)
return;
+ // emit dataReadProgress signal (signal is currently not connected
+ // to the rest of QNAM) since readProgress of the
+ // QNonContiguousByteDevice is used
emit reply->dataReadProgress(reply->d_func()->totalProgress, reply->d_func()->bodyLength);
// make sure that the reply is valid
if (channels[i].reply != reply)
@@ -635,8 +705,25 @@ void QHttpNetworkConnectionPrivate::handleStatus(QAbstractSocket *socket, QHttpN
case 407:
handleAuthenticateChallenge(socket, reply, (statusCode == 407), resend);
if (resend) {
+ int i = indexOf(socket);
+
+ QNonContiguousByteDevice* uploadByteDevice = channels[i].request.uploadByteDevice();
+ if (uploadByteDevice) {
+ if (uploadByteDevice->reset()) {
+ channels[i].written = 0;
+ } else {
+ emitReplyError(socket, reply, QNetworkReply::ContentReSendError);
+ break;
+ }
+ }
+
eraseData(reply);
- sendRequest(socket);
+
+ // also use async _q_startNextRequest so we dont break with closed
+ // proxy or server connections..
+ channels[i].resendCurrent = true;
+ QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
+
}
break;
default:
@@ -970,6 +1057,7 @@ void QHttpNetworkConnectionPrivate::_q_bytesWritten(qint64 bytes)
QAbstractSocket *socket = qobject_cast<QAbstractSocket*>(q->sender());
if (!socket)
return; // ### error
+ // bytes have been written to the socket. write even more of them :)
if (isSocketWriting(socket))
sendRequest(socket);
// otherwise we do nothing
@@ -1128,80 +1216,21 @@ void QHttpNetworkConnectionPrivate::_q_proxyAuthenticationRequired(const QNetwor
}
#endif
-void QHttpNetworkConnectionPrivate::_q_dataReadyReadNoBuffer()
+void QHttpNetworkConnectionPrivate::_q_uploadDataReadyRead()
{
Q_Q(QHttpNetworkConnection);
- // data emitted either readyRead()
+ // upload data emitted readyRead()
// find out which channel it is for
- QIODevice *sender = qobject_cast<QIODevice *>(q->sender());
+ QObject *sender = q->sender();
- // won't match anything if the qobject_cast above failed
for (int i = 0; i < channelCount; ++i) {
- if (sender == channels[i].request.data()) {
+ if (sender == channels[i].request.uploadByteDevice()) {
sendRequest(channels[i].socket);
break;
}
}
}
-void QHttpNetworkConnectionPrivate::_q_dataReadyReadBuffer()
-{
- Q_Q(QHttpNetworkConnection);
- QIODevice *sender = qobject_cast<QIODevice *>(q->sender());
- HttpMessagePair *thePair = 0;
- for (int i = 0; !thePair && i < lowPriorityQueue.size(); ++i)
- if (lowPriorityQueue.at(i).first.data() == sender)
- thePair = &lowPriorityQueue[i];
-
- for (int i = 0; !thePair && i < highPriorityQueue.size(); ++i)
- if (highPriorityQueue.at(i).first.data() == sender)
- thePair = &highPriorityQueue[i];
-
- if (thePair) {
- bufferData(*thePair);
-
- // are we finished buffering?
- if (!thePair->second->d_func()->requestIsBuffering)
- _q_startNextRequest();
- }
-}
-
-void QHttpNetworkConnectionPrivate::bufferData(HttpMessagePair &messagePair)
-{
- Q_Q(QHttpNetworkConnection);
- QHttpNetworkRequest &request = messagePair.first;
- QHttpNetworkReply *reply = messagePair.second;
- Q_ASSERT(request.data());
- if (!reply->d_func()->requestIsBuffering) { // first time
- QObject::connect(request.data(), SIGNAL(readyRead()), q, SLOT(_q_dataReadyReadBuffer()));
- QObject::connect(request.data(), SIGNAL(readChannelFinished()), q, SLOT(_q_dataReadyReadBuffer()));
- reply->d_func()->requestIsBuffering = true;
- reply->d_func()->requestDataBuffer.open(QIODevice::ReadWrite);
- }
-
- // always try to read at least one byte
- // ### FIXME! use a QRingBuffer
- qint64 bytesToRead = qMax<qint64>(1, request.data()->bytesAvailable());
- QByteArray newData;
- newData.resize(bytesToRead);
- qint64 bytesActuallyRead = request.data()->read(newData.data(), bytesToRead);
-
- if (bytesActuallyRead > 0) {
- // we read something
- newData.chop(bytesToRead - bytesActuallyRead);
- reply->d_func()->requestDataBuffer.write(newData);
- } else if (bytesActuallyRead == -1) { // last time
- QObject::disconnect(request.data(), SIGNAL(readyRead()), q, SLOT(_q_dataReadyReadBuffer()));
- QObject::disconnect(request.data(), SIGNAL(readChannelFinished()), q, SLOT(_q_dataReadyReadBuffer()));
-
- request.setContentLength(reply->d_func()->requestDataBuffer.size());
- reply->d_func()->requestDataBuffer.seek(0);
- reply->d_func()->requestIsBuffering = false;
- }
-}
-
-// QHttpNetworkConnection
-
QHttpNetworkConnection::QHttpNetworkConnection(const QString &hostName, quint16 port, bool encrypt, QObject *parent)
: QObject(*(new QHttpNetworkConnectionPrivate(hostName, port, encrypt)), parent)
{