diff options
Diffstat (limited to 'src/network')
-rw-r--r-- | src/network/access/qhttpnetworkconnectionchannel.cpp | 47 | ||||
-rw-r--r-- | src/network/access/qhttpthreaddelegate_p.h | 36 | ||||
-rw-r--r-- | src/network/access/qnetworkaccesshttpbackend.cpp | 24 | ||||
-rw-r--r-- | src/network/access/qnetworkaccesshttpbackend_p.h | 5 |
4 files changed, 88 insertions, 24 deletions
diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp index 550e090..db2f712 100644 --- a/src/network/access/qhttpnetworkconnectionchannel.cpp +++ b/src/network/access/qhttpnetworkconnectionchannel.cpp @@ -107,15 +107,19 @@ void QHttpNetworkConnectionChannel::init() socket->setProxy(QNetworkProxy::NoProxy); #endif + // We want all signals (except the interactive ones) be connected as QueuedConnection + // because else we're falling into cases where we recurse back into the socket code + // and mess up the state. Always going to the event loop (and expecting that when reading/writing) + // is safer. QObject::connect(socket, SIGNAL(bytesWritten(qint64)), this, SLOT(_q_bytesWritten(qint64)), - Qt::DirectConnection); + Qt::QueuedConnection); QObject::connect(socket, SIGNAL(connected()), this, SLOT(_q_connected()), - Qt::DirectConnection); + Qt::QueuedConnection); QObject::connect(socket, SIGNAL(readyRead()), this, SLOT(_q_readyRead()), - Qt::DirectConnection); + Qt::QueuedConnection); // The disconnected() and error() signals may already come // while calling connectToHost(). @@ -144,13 +148,13 @@ void QHttpNetworkConnectionChannel::init() // won't be a sslSocket if encrypt is false QObject::connect(sslSocket, SIGNAL(encrypted()), this, SLOT(_q_encrypted()), - Qt::DirectConnection); + Qt::QueuedConnection); QObject::connect(sslSocket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(_q_sslErrors(QList<QSslError>)), Qt::DirectConnection); QObject::connect(sslSocket, SIGNAL(encryptedBytesWritten(qint64)), this, SLOT(_q_encryptedBytesWritten(qint64)), - Qt::DirectConnection); + Qt::QueuedConnection); } #endif } @@ -163,7 +167,8 @@ void QHttpNetworkConnectionChannel::close() else state = QHttpNetworkConnectionChannel::ClosingState; - socket->close(); + if (socket) + socket->close(); } @@ -280,6 +285,14 @@ bool QHttpNetworkConnectionChannel::sendRequest() // nothing to read currently, break the loop break; } else { + if (written != uploadByteDevice->pos()) { + // Sanity check. This was useful in tracking down an upload corruption. + qWarning() << "QHttpProtocolHandler: Internal error in sendRequest. Expected to write at position" << written << "but read device is at" << uploadByteDevice->pos(); + Q_ASSERT(written == uploadByteDevice->pos()); + connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ProtocolFailure); + return false; + } + qint64 currentWriteSize = socket->write(readPointer, currentReadSize); if (currentWriteSize == -1 || currentWriteSize != currentReadSize) { // socket broke down @@ -639,6 +652,14 @@ bool QHttpNetworkConnectionChannel::ensureConnection() } return false; } + + // This code path for ConnectedState + if (pendingEncrypt) { + // Let's only be really connected when we have received the encrypted() signal. Else the state machine seems to mess up + // and corrupt the things sent to the server. + return false; + } + return true; } @@ -980,6 +1001,13 @@ void QHttpNetworkConnectionChannel::_q_readyRead() void QHttpNetworkConnectionChannel::_q_bytesWritten(qint64 bytes) { Q_UNUSED(bytes); + + if (ssl) { + // In the SSL case we want to send data from encryptedBytesWritten signal since that one + // is the one going down to the actual network, not only into some SSL buffer. + return; + } + // bytes have been written to the socket. write even more of them :) if (isSocketWriting()) sendRequest(); @@ -1029,7 +1057,7 @@ void QHttpNetworkConnectionChannel::_q_connected() // ### FIXME: if the server closes the connection unexpectedly, we shouldn't send the same broken request again! //channels[i].reconnectAttempts = 2; - if (!pendingEncrypt) { + if (!pendingEncrypt && !ssl) { // FIXME: Didn't work properly with pendingEncrypt only, we should refactor this into an EncrypingState state = QHttpNetworkConnectionChannel::IdleState; if (!reply) connection->d_func()->dequeueRequest(socket); @@ -1157,7 +1185,10 @@ void QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired(const QNetwor void QHttpNetworkConnectionChannel::_q_uploadDataReadyRead() { - sendRequest(); + if (reply && state == QHttpNetworkConnectionChannel::WritingState) { + // There might be timing issues, make sure to only send upload data if really in that state + sendRequest(); + } } #ifndef QT_NO_OPENSSL diff --git a/src/network/access/qhttpthreaddelegate_p.h b/src/network/access/qhttpthreaddelegate_p.h index 7648325..9dd0deb 100644 --- a/src/network/access/qhttpthreaddelegate_p.h +++ b/src/network/access/qhttpthreaddelegate_p.h @@ -190,6 +190,7 @@ protected: QByteArray m_dataArray; bool m_atEnd; qint64 m_size; + qint64 m_pos; // to match calls of haveDataSlot with the expected position public: QNonContiguousByteDeviceThreadForwardImpl(bool aE, qint64 s) : QNonContiguousByteDevice(), @@ -197,7 +198,8 @@ public: m_amount(0), m_data(0), m_atEnd(aE), - m_size(s) + m_size(s), + m_pos(0) { } @@ -205,6 +207,11 @@ public: { } + qint64 pos() + { + return m_pos; + } + const char* readPointer(qint64 maximumLength, qint64 &len) { if (m_amount > 0) { @@ -232,11 +239,10 @@ public: m_amount -= a; m_data += a; + m_pos += a; - // To main thread to inform about our state - emit processedData(a); - - // FIXME possible optimization, already ask user thread for some data + // To main thread to inform about our state. The m_pos will be sent as a sanity check. + emit processedData(m_pos, a); return true; } @@ -253,10 +259,21 @@ public: { m_amount = 0; m_data = 0; + m_dataArray.clear(); + + if (wantDataPending) { + // had requested the user thread to send some data (only 1 in-flight at any moment) + wantDataPending = false; + } // Communicate as BlockingQueuedConnection bool b = false; emit resetData(&b); + if (b) { + // the reset succeeded, we're at pos 0 again + m_pos = 0; + // the HTTP code will anyway abort the request if !b. + } return b; } @@ -267,8 +284,13 @@ public: public slots: // From user thread: - void haveDataSlot(QByteArray dataArray, bool dataAtEnd, qint64 dataSize) + void haveDataSlot(qint64 pos, QByteArray dataArray, bool dataAtEnd, qint64 dataSize) { + if (pos != m_pos) { + // Sometimes when re-sending a request in the qhttpnetwork* layer there is a pending haveData from the + // user thread on the way to us. We need to ignore it since it is the data for the wrong(later) chunk. + return; + } wantDataPending = false; m_dataArray = dataArray; @@ -288,7 +310,7 @@ signals: // to main thread: void wantData(qint64); - void processedData(qint64); + void processedData(qint64 pos, qint64 amount); void resetData(bool *b); }; diff --git a/src/network/access/qnetworkaccesshttpbackend.cpp b/src/network/access/qnetworkaccesshttpbackend.cpp index cc67258..fe2f627 100644 --- a/src/network/access/qnetworkaccesshttpbackend.cpp +++ b/src/network/access/qnetworkaccesshttpbackend.cpp @@ -193,6 +193,7 @@ QNetworkAccessHttpBackendFactory::create(QNetworkAccessManager::Operation op, QNetworkAccessHttpBackend::QNetworkAccessHttpBackend() : QNetworkAccessBackend() , statusCode(0) + , uploadByteDevicePosition(false) , pendingDownloadDataEmissions(new QAtomicInt()) , pendingDownloadProgressEmissions(new QAtomicInt()) , loadingFromCache(false) @@ -610,9 +611,9 @@ void QNetworkAccessHttpBackend::postRequest() forwardUploadDevice->setParent(delegate); // needed to make sure it is moved on moveToThread() delegate->httpRequest.setUploadByteDevice(forwardUploadDevice); - // From main thread to user thread: - QObject::connect(this, SIGNAL(haveUploadData(QByteArray, bool, qint64)), - forwardUploadDevice, SLOT(haveDataSlot(QByteArray, bool, qint64)), Qt::QueuedConnection); + // From user thread to http thread: + QObject::connect(this, SIGNAL(haveUploadData(qint64,QByteArray,bool,qint64)), + forwardUploadDevice, SLOT(haveDataSlot(qint64,QByteArray,bool,qint64)), Qt::QueuedConnection); QObject::connect(uploadByteDevice.data(), SIGNAL(readyRead()), forwardUploadDevice, SIGNAL(readyRead()), Qt::QueuedConnection); @@ -620,8 +621,8 @@ void QNetworkAccessHttpBackend::postRequest() // From http thread to user thread: QObject::connect(forwardUploadDevice, SIGNAL(wantData(qint64)), this, SLOT(wantUploadDataSlot(qint64))); - QObject::connect(forwardUploadDevice, SIGNAL(processedData(qint64)), - this, SLOT(sentUploadDataSlot(qint64))); + QObject::connect(forwardUploadDevice,SIGNAL(processedData(qint64, qint64)), + this, SLOT(sentUploadDataSlot(qint64,qint64))); connect(forwardUploadDevice, SIGNAL(resetData(bool*)), this, SLOT(resetUploadDataSlot(bool*)), Qt::BlockingQueuedConnection); // this is the only one with BlockingQueued! @@ -915,12 +916,21 @@ void QNetworkAccessHttpBackend::replySslConfigurationChanged(const QSslConfigura void QNetworkAccessHttpBackend::resetUploadDataSlot(bool *r) { *r = uploadByteDevice->reset(); + if (*r) { + // reset our own position which is used for the inter-thread communication + uploadByteDevicePosition = 0; + } } // Coming from QNonContiguousByteDeviceThreadForwardImpl in HTTP thread -void QNetworkAccessHttpBackend::sentUploadDataSlot(qint64 amount) +void QNetworkAccessHttpBackend::sentUploadDataSlot(qint64 pos, qint64 amount) { + if (uploadByteDevicePosition + amount != pos) { + // Sanity check, should not happen. + error(QNetworkReply::UnknownNetworkError, ""); + } uploadByteDevice->advanceReadPointer(amount); + uploadByteDevicePosition += amount; } // Coming from QNonContiguousByteDeviceThreadForwardImpl in HTTP thread @@ -933,7 +943,7 @@ void QNetworkAccessHttpBackend::wantUploadDataSlot(qint64 maxSize) QByteArray dataArray(data, currentUploadDataLength); // Communicate back to HTTP thread - emit haveUploadData(dataArray, uploadByteDevice->atEnd(), uploadByteDevice->size()); + emit haveUploadData(uploadByteDevicePosition, dataArray, uploadByteDevice->atEnd(), uploadByteDevice->size()); } /* diff --git a/src/network/access/qnetworkaccesshttpbackend_p.h b/src/network/access/qnetworkaccesshttpbackend_p.h index 13519c6..b4ed67c 100644 --- a/src/network/access/qnetworkaccesshttpbackend_p.h +++ b/src/network/access/qnetworkaccesshttpbackend_p.h @@ -112,7 +112,7 @@ signals: void startHttpRequestSynchronously(); - void haveUploadData(QByteArray dataArray, bool dataAtEnd, qint64 dataSize); + void haveUploadData(const qint64 pos, QByteArray dataArray, bool dataAtEnd, qint64 dataSize); private slots: // From HTTP thread: void replyDownloadData(QByteArray); @@ -129,13 +129,14 @@ private slots: // From QNonContiguousByteDeviceThreadForwardImpl in HTTP thread: void resetUploadDataSlot(bool *r); void wantUploadDataSlot(qint64); - void sentUploadDataSlot(qint64); + void sentUploadDataSlot(qint64, qint64); bool sendCacheContents(const QNetworkCacheMetaData &metaData); private: QHttpNetworkRequest httpRequest; // There is also a copy in the HTTP thread int statusCode; + qint64 uploadByteDevicePosition; QString reasonPhrase; // Will be increased by HTTP thread: QSharedPointer<QAtomicInt> pendingDownloadDataEmissions; |