QHttpSocketEngine: ensure pending EOF triggers a notification

When the remote peer closed the connection, a read notification needs
to always be emitted, otherwise the higher layer does not get the
disconnected signal. From the other side, underlying QAbstractSocket
object could temporarily disable notifications from the engine at
any time. To avoid possible blocking of the socket, take a pending EOF
into account when the read notifications are re-enabled.

Change-Id: Iac9d4e2f790530be3500baf5a2000f1f63df5cc2
Reviewed-by: Ulf Hermann <ulf.hermann@theqtcompany.com>
This commit is contained in:
Alex Trotsenko 2015-11-28 15:27:43 +02:00
parent 5243696528
commit 294111e25a
2 changed files with 51 additions and 1 deletions

View File

@ -439,8 +439,11 @@ void QHttpSocketEngine::setReadNotificationEnabled(bool enable)
d->readNotificationEnabled = enable;
if (enable) {
// Enabling read notification can trigger a notification.
if (bytesAvailable())
if (bytesAvailable()) {
slotSocketReadNotification();
} else if (d->socket && d->socket->state() == QAbstractSocket::UnconnectedState) {
emitReadNotification();
}
}
}

View File

@ -72,6 +72,7 @@ private slots:
void downloadBigFile();
// void tcpLoopbackPerformance();
void passwordAuth();
void ensureEofTriggersNotification();
protected slots:
void tcpSocketNonBlocking_hostFound();
@ -739,5 +740,51 @@ void tst_QHttpSocketEngine::passwordAuth()
//----------------------------------------------------------------------------------
void tst_QHttpSocketEngine::ensureEofTriggersNotification()
{
QList<QByteArray> serverData;
// Set the handshake and server response data
serverData << "HTTP/1.0 200 Connection established\r\n\r\n" << "0";
MiniHttpServer server(serverData);
QTcpSocket socket;
connect(&socket, SIGNAL(connected()), SLOT(exitLoopSlot()));
socket.setProxy(QNetworkProxy(QNetworkProxy::HttpProxy, server.serverAddress().toString(),
server.serverPort()));
socket.connectToHost("0.1.2.3", 12345);
QTestEventLoop::instance().enterLoop(5);
if (QTestEventLoop::instance().timeout())
QFAIL("Connect timed out");
QCOMPARE(socket.state(), QTcpSocket::ConnectedState);
// Disable read notification on server response
socket.setReadBufferSize(1);
socket.putChar(0);
// Wait for the response
connect(&socket, SIGNAL(readyRead()), SLOT(exitLoopSlot()));
QTestEventLoop::instance().enterLoop(5);
if (QTestEventLoop::instance().timeout())
QFAIL("Read timed out");
QCOMPARE(socket.state(), QTcpSocket::ConnectedState);
QCOMPARE(socket.bytesAvailable(), 1);
// Trigger a read notification
socket.readAll();
// Check for pending EOF at input
QCOMPARE(socket.bytesAvailable(), 0);
QCOMPARE(socket.state(), QTcpSocket::ConnectedState);
// Try to read EOF
connect(&socket, SIGNAL(disconnected()), SLOT(exitLoopSlot()));
QTestEventLoop::instance().enterLoop(5);
if (QTestEventLoop::instance().timeout())
QFAIL("Disconnect timed out");
// Check that it's closed
QCOMPARE(socket.state(), QTcpSocket::UnconnectedState);
}
QTEST_MAIN(tst_QHttpSocketEngine)
#include "tst_qhttpsocketengine.moc"