diff options
Diffstat (limited to 'src/network/access/qhttpnetworkconnection.cpp')
-rw-r--r-- | src/network/access/qhttpnetworkconnection.cpp | 296 |
1 files changed, 157 insertions, 139 deletions
diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp index cec9c87..359cab9 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> @@ -63,7 +64,7 @@ QT_BEGIN_NAMESPACE -const int QHttpNetworkConnectionPrivate::channelCount = 2; +const int QHttpNetworkConnectionPrivate::channelCount = 6; QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(const QString &hostName, quint16 port, bool encrypt) : hostName(hostName), port(port), encrypt(encrypt), @@ -71,7 +72,9 @@ QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(const QString &host #ifndef QT_NO_NETWORKPROXY , networkProxy(QNetworkProxy::NoProxy) #endif + { + channels = new Channel[channelCount]; } QHttpNetworkConnectionPrivate::~QHttpNetworkConnectionPrivate() @@ -82,6 +85,7 @@ QHttpNetworkConnectionPrivate::~QHttpNetworkConnectionPrivate() delete channels[i].socket; } } + delete []channels; } void QHttpNetworkConnectionPrivate::connectSignals(QAbstractSocket *socket) @@ -207,12 +211,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 @@ -363,18 +374,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; @@ -382,30 +387,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 @@ -416,7 +472,7 @@ bool QHttpNetworkConnectionPrivate::sendRequest(QAbstractSocket *socket) return true; } -bool QHttpNetworkConnectionPrivate::emitSignals(QHttpNetworkReply *reply) +bool QHttpNetworkConnectionPrivate::shouldEmitSignals(QHttpNetworkReply *reply) { // for 401 & 407 don't emit the data signals. Content along with these // responses are send only if the authentication fails. @@ -432,7 +488,7 @@ bool QHttpNetworkConnectionPrivate::expectContent(QHttpNetworkReply *reply) || reply->d_func()->statusCode == 204 || reply->d_func()->statusCode == 304) return false; if (reply->d_func()->request.operation() == QHttpNetworkRequest::Head) - return !emitSignals(reply); + return !shouldEmitSignals(reply); if (reply->d_func()->contentLength() == 0) return false; return true; @@ -476,11 +532,14 @@ bool QHttpNetworkConnectionPrivate::expand(QAbstractSocket *socket, QHttpNetwork if (inflated.size()) { reply->d_func()->totalProgress += inflated.size(); appendData(*reply, inflated, false); - if (emitSignals(reply)) { + if (shouldEmitSignals(reply)) { emit reply->readyRead(); // 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) @@ -558,7 +617,7 @@ void QHttpNetworkConnectionPrivate::receiveReply(QAbstractSocket *socket, QHttpN reply->d_func()->state = QHttpNetworkReplyPrivate::ReadingStatusState; break; // ignore } - if (emitSignals(reply)) + if (shouldEmitSignals(reply)) emit reply->headerChanged(); if (!expectContent(reply)) { reply->d_func()->state = QHttpNetworkReplyPrivate::AllDoneState; @@ -576,11 +635,14 @@ void QHttpNetworkConnectionPrivate::receiveReply(QAbstractSocket *socket, QHttpN appendData(*reply, fragment.data(), reply->d_func()->autoDecompress); if (!reply->d_func()->autoDecompress) { reply->d_func()->totalProgress += fragment.size(); - if (emitSignals(reply)) { + if (shouldEmitSignals(reply)) { emit reply->readyRead(); // 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) @@ -615,7 +677,7 @@ void QHttpNetworkConnectionPrivate::allDone(QAbstractSocket *socket, QHttpNetwor expand(socket, reply, true); // ### if expand returns false, its an error #endif // while handling 401 & 407, we might reset the status code, so save this. - bool emitFinished = emitSignals(reply); + bool emitFinished = shouldEmitSignals(reply); handleStatus(socket, reply); // ### at this point there should be no more data on the socket // close if server requested @@ -647,8 +709,24 @@ void QHttpNetworkConnectionPrivate::handleStatus(QAbstractSocket *socket, QHttpN case 407: if (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); } } else { int i = indexOf(socket); @@ -754,7 +832,7 @@ bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket if (priv->phase != QAuthenticatorPrivate::Done) { // send any pending requests copyCredentials(i, auth, isProxy); - QMetaObject::invokeMethod(q, "_q_restartPendingRequest", Qt::QueuedConnection); + QMetaObject::invokeMethod(q, "_q_restartAuthPendingRequests", Qt::QueuedConnection); } } // changing values in QAuthenticator will reset the 'phase' @@ -833,7 +911,7 @@ QHttpNetworkReply* QHttpNetworkConnectionPrivate::queueRequest(const QHttpNetwor return reply; } -void QHttpNetworkConnectionPrivate::unqueueRequest(QAbstractSocket *socket) +void QHttpNetworkConnectionPrivate::unqueueAndSendRequest(QAbstractSocket *socket) { Q_ASSERT(socket); @@ -844,13 +922,12 @@ void QHttpNetworkConnectionPrivate::unqueueRequest(QAbstractSocket *socket) HttpMessagePair &messagePair = highPriorityQueue[j]; if (!messagePair.second->d_func()->requestIsPrepared) prepareRequest(messagePair); - if (!messagePair.second->d_func()->requestIsBuffering) { - channels[i].request = messagePair.first; - channels[i].reply = messagePair.second; - sendRequest(socket); - highPriorityQueue.removeAt(j); - return; - } + + channels[i].request = messagePair.first; + channels[i].reply = messagePair.second; + sendRequest(socket); + highPriorityQueue.removeAt(j); + return; } } @@ -859,13 +936,11 @@ void QHttpNetworkConnectionPrivate::unqueueRequest(QAbstractSocket *socket) HttpMessagePair &messagePair = lowPriorityQueue[j]; if (!messagePair.second->d_func()->requestIsPrepared) prepareRequest(messagePair); - if (!messagePair.second->d_func()->requestIsBuffering) { - channels[i].request = messagePair.first; - channels[i].reply = messagePair.second; - sendRequest(socket); - lowPriorityQueue.removeAt(j); - return; - } + channels[i].request = messagePair.first; + channels[i].reply = messagePair.second; + sendRequest(socket); + lowPriorityQueue.removeAt(j); + return; } } } @@ -992,6 +1067,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 @@ -1018,29 +1094,30 @@ void QHttpNetworkConnectionPrivate::_q_disconnected() void QHttpNetworkConnectionPrivate::_q_startNextRequest() { - // send the current request again - if (channels[0].resendCurrent || channels[1].resendCurrent) { - int i = channels[0].resendCurrent ? 0:1; - QAbstractSocket *socket = channels[i].socket; - channels[i].resendCurrent = false; - channels[i].state = IdleState; - if (channels[i].reply) - sendRequest(socket); - return; + //resend the necessary ones. + for (int i = 0; i < channelCount; ++i) { + if (channels[i].resendCurrent) { + channels[i].resendCurrent = false; + channels[i].state = IdleState; + if (channels[i].reply) + sendRequest(channels[i].socket); + } } - // send the request using the idle socket - QAbstractSocket *socket = channels[0].socket; - if (isSocketBusy(socket)) { - socket = (isSocketBusy(channels[1].socket) ? 0 :channels[1].socket); + QAbstractSocket *socket = 0; + for (int i = 0; i < channelCount; ++i) { + QAbstractSocket *chSocket = channels[i].socket; + // send the request using the idle socket + if (!isSocketBusy(chSocket)) { + socket = chSocket; + break; + } } - - if (!socket) { + if (!socket) return; // this will be called after finishing current request. - } - unqueueRequest(socket); + unqueueAndSendRequest(socket); } -void QHttpNetworkConnectionPrivate::_q_restartPendingRequest() +void QHttpNetworkConnectionPrivate::_q_restartAuthPendingRequests() { // send the request using the idle socket for (int i = 0 ; i < channelCount; ++i) { @@ -1150,80 +1227,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) { |