From 435fa2dbc44868f6ac81956be82631a2040ed1cc Mon Sep 17 00:00:00 2001 From: Markus Goetz Date: Mon, 15 Feb 2010 14:22:59 +0100 Subject: QNAM HTTP: Unify socket EOF handling a bit Reviewed-by: Peter Hartmann --- .../access/qhttpnetworkconnectionchannel.cpp | 61 +++++++++++++--------- .../access/qhttpnetworkconnectionchannel_p.h | 1 + tests/auto/qnetworkreply/tst_qnetworkreply.cpp | 5 ++ 3 files changed, 43 insertions(+), 24 deletions(-) diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp index b0e632a..2a3036b 100644 --- a/src/network/access/qhttpnetworkconnectionchannel.cpp +++ b/src/network/access/qhttpnetworkconnectionchannel.cpp @@ -283,20 +283,17 @@ void QHttpNetworkConnectionChannel::_q_receiveReply() // connection might be closed to signal the end of data if (socketState == QAbstractSocket::UnconnectedState) { - if (!socket->bytesAvailable()) { + if (socket->bytesAvailable() <= 0) { if (reply && reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingDataState) { + // finish this reply. this case happens when the server did not send a content length reply->d_func()->state = QHttpNetworkReplyPrivate::AllDoneState; allDone(); } else { - // try to reconnect/resend before sending an error. - if (reconnectAttempts-- > 0) { - closeAndResendCurrentRequest(); - } else if (reply) { - reply->d_func()->errorString = connection->d_func()->errorDetail(QNetworkReply::RemoteHostClosedError, socket); - emit reply->finishedWithError(QNetworkReply::RemoteHostClosedError, reply->d_func()->errorString); - QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); - } + handleUnexpectedEOF(); + return; } + } else { + // socket not connected but still bytes for reading.. just continue in this function } } @@ -311,18 +308,10 @@ void QHttpNetworkConnectionChannel::_q_receiveReply() } case QHttpNetworkReplyPrivate::ReadingStatusState: { qint64 statusBytes = reply->d_func()->readStatus(socket); - if (statusBytes == -1 && reconnectAttempts <= 0) { - // too many errors reading/receiving/parsing the status, close the socket and emit error - close(); - reply->d_func()->errorString = connection->d_func()->errorDetail(QNetworkReply::ProtocolFailure, socket); - emit reply->finishedWithError(QNetworkReply::ProtocolFailure, reply->d_func()->errorString); - QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); - break; - } else if (statusBytes == -1) { - reconnectAttempts--; - reply->d_func()->clear(); - closeAndResendCurrentRequest(); - break; + if (statusBytes == -1) { + // connection broke while reading status. also handled if later _q_disconnected is called + handleUnexpectedEOF(); + return; } bytes += statusBytes; lastStatus = reply->d_func()->statusCode; @@ -330,7 +319,13 @@ void QHttpNetworkConnectionChannel::_q_receiveReply() } case QHttpNetworkReplyPrivate::ReadingHeaderState: { QHttpNetworkReplyPrivate *replyPrivate = reply->d_func(); - bytes += replyPrivate->readHeader(socket); + qint64 headerBytes = replyPrivate->readHeader(socket); + if (headerBytes == -1) { + // connection broke while reading headers. also handled if later _q_disconnected is called + handleUnexpectedEOF(); + return; + } + bytes += headerBytes; if (replyPrivate->state == QHttpNetworkReplyPrivate::ReadingDataState) { if (replyPrivate->isGzipped() && replyPrivate->autoDecompress) { // remove the Content-Length from header @@ -430,6 +425,23 @@ void QHttpNetworkConnectionChannel::_q_receiveReply() } } +// called when unexpectedly reading a -1 or when data is expected but socket is closed +void QHttpNetworkConnectionChannel::handleUnexpectedEOF() +{ + if (reconnectAttempts <= 0) { + // too many errors reading/receiving/parsing the status, close the socket and emit error + requeueCurrentlyPipelinedRequests(); + close(); + reply->d_func()->errorString = connection->d_func()->errorDetail(QNetworkReply::RemoteHostClosedError, socket); + emit reply->finishedWithError(QNetworkReply::RemoteHostClosedError, reply->d_func()->errorString); + QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); + } else { + reconnectAttempts--; + reply->d_func()->clear(); + closeAndResendCurrentRequest(); + } +} + bool QHttpNetworkConnectionChannel::ensureConnection() { QAbstractSocket::SocketState socketState = socket->state(); @@ -799,9 +811,10 @@ void QHttpNetworkConnectionChannel::_q_disconnected() { // read the available data before closing if (isSocketWaiting() || isSocketReading()) { - state = QHttpNetworkConnectionChannel::ReadingState; - if (reply) + if (reply) { + state = QHttpNetworkConnectionChannel::ReadingState; _q_receiveReply(); + } } else if (state == QHttpNetworkConnectionChannel::IdleState && resendCurrent) { // re-sending request because the socket was in ClosingState QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); diff --git a/src/network/access/qhttpnetworkconnectionchannel_p.h b/src/network/access/qhttpnetworkconnectionchannel_p.h index 75ab50d..5032d2b 100644 --- a/src/network/access/qhttpnetworkconnectionchannel_p.h +++ b/src/network/access/qhttpnetworkconnectionchannel_p.h @@ -157,6 +157,7 @@ public: void requeueCurrentlyPipelinedRequests(); void detectPipeliningSupport(); + void handleUnexpectedEOF(); void closeAndResendCurrentRequest(); void eatWhitespace(); diff --git a/tests/auto/qnetworkreply/tst_qnetworkreply.cpp b/tests/auto/qnetworkreply/tst_qnetworkreply.cpp index eec4797..5fecc86 100644 --- a/tests/auto/qnetworkreply/tst_qnetworkreply.cpp +++ b/tests/auto/qnetworkreply/tst_qnetworkreply.cpp @@ -2034,6 +2034,11 @@ void tst_QNetworkReply::ioGetFromHttpBrokenServer_data() QTest::newRow("invalid-version+disconnect") << QByteArray("HTTP/123 200 ") << true; QTest::newRow("invalid-version2+disconnect") << QByteArray("HTTP/a.\033 200 ") << true; QTest::newRow("invalid-reply-code+disconnect") << QByteArray("HTTP/1.0 fuu ") << true; + + QTest::newRow("immediate disconnect") << QByteArray("") << true; + QTest::newRow("justHalfStatus+disconnect") << QByteArray("HTTP/1.1") << true; + QTest::newRow("justStatus+disconnect") << QByteArray("HTTP/1.1 200 OK\r\n") << true; + QTest::newRow("justStatusAndHalfHeaders+disconnect") << QByteArray("HTTP/1.1 200 OK\r\nContent-L") << true; } void tst_QNetworkReply::ioGetFromHttpBrokenServer() -- cgit v0.12