Fix Q_ASSERT(!channels[0].isSocketBusy());

Since commit f30641a7 is has been possible to issue more than one host
lookup request per HttpNetworkConnection. If the result was both an
IPv4 and IPv6 address, and we get a second similar DNS reply, we
end up triggering the assert in startNetworkLayerStateLookup().

This patch splits the InProgress state to HostLookupPending and the state
of trying both IPv4 and IPv6. This makes it possible to ignore any new DNS
replies received after the first succesfull one.

Change-Id: I0b8d6b1582fdaed69dde5926019b60bb0cbd580d
Reviewed-by: Peter Hartmann <phartmann@blackberry.com>
This commit is contained in:
Allan Sandfeld Jensen 2013-10-17 19:24:28 +02:00 committed by The Qt Project
parent 13035f7fd6
commit 690cf426f3
3 changed files with 15 additions and 12 deletions

View File

@ -181,7 +181,7 @@ int QHttpNetworkConnectionPrivate::indexOf(QAbstractSocket *socket) const
return 0;
}
// If the connection is in the InProgress state channel errors should not always be
// If the connection is in the HostLookupPendening state channel errors should not always be
// emitted. This function will check the status of the connection channels if we
// have not decided the networkLayerState and will return true if the channel error
// should be emitted by the channel.
@ -200,12 +200,12 @@ bool QHttpNetworkConnectionPrivate::shouldEmitChannelError(QAbstractSocket *sock
}
if (channelCount == 1) {
if (networkLayerState == QHttpNetworkConnectionPrivate::InProgress)
if (networkLayerState == HostLookupPending || networkLayerState == IPv4or6)
networkLayerState = QHttpNetworkConnectionPrivate::Unknown;
channels[0].close();
emitError = true;
} else {
if (networkLayerState == QHttpNetworkConnectionPrivate::InProgress) {
if (networkLayerState == HostLookupPending || networkLayerState == IPv4or6) {
if (channels[otherSocket].isSocketBusy() && (channels[otherSocket].state != QHttpNetworkConnectionChannel::ClosingState)) {
// this was the first socket to fail.
channels[i].close();
@ -560,7 +560,7 @@ QHttpNetworkReply* QHttpNetworkConnectionPrivate::queueRequest(const QHttpNetwor
// untill we have started the first connection attempt. So no
// request will be started untill we know if IPv4 or IPv6
// should be used.
if (networkLayerState == Unknown || networkLayerState == InProgress) {
if (networkLayerState == Unknown || networkLayerState == HostLookupPending) {
startHostInfoLookup();
} else if ( networkLayerState == IPv4 || networkLayerState == IPv6 ) {
// this used to be called via invokeMethod and a QueuedConnection
@ -878,7 +878,7 @@ void QHttpNetworkConnectionPrivate::removeReply(QHttpNetworkReply *reply)
void QHttpNetworkConnectionPrivate::_q_startNextRequest()
{
// If there is no network layer state decided we should not start any new requests.
if (networkLayerState == Unknown || networkLayerState == InProgress)
if (networkLayerState == Unknown || networkLayerState == HostLookupPending || networkLayerState == IPv4or6)
return;
// If the QHttpNetworkConnection is currently paused then bail out immediately
@ -986,7 +986,7 @@ void QHttpNetworkConnectionPrivate::readMoreLater(QHttpNetworkReply *reply)
// lookup as then the hostinfo will already be in the cache.
void QHttpNetworkConnectionPrivate::startHostInfoLookup()
{
networkLayerState = InProgress;
networkLayerState = HostLookupPending;
// check if we already now can decide if this is IPv4 or IPv6
QString lookupHost = hostName;
@ -1028,6 +1028,8 @@ void QHttpNetworkConnectionPrivate::_q_hostLookupFinished(QHostInfo info)
bool bIpv4 = false;
bool bIpv6 = false;
bool foundAddress = false;
if (networkLayerState == IPv4 || networkLayerState == IPv6 || networkLayerState == IPv4or6)
return;
foreach (const QHostAddress &address, info.addresses()) {
if (address.protocol() == QAbstractSocket::IPv4Protocol) {
@ -1077,7 +1079,7 @@ void QHttpNetworkConnectionPrivate::startNetworkLayerStateLookup()
Q_ASSERT(!channels[0].isSocketBusy());
Q_ASSERT(!channels[1].isSocketBusy());
networkLayerState = InProgress;
networkLayerState = IPv4or6;
channels[0].networkLayerPreference = QAbstractSocket::IPv4Protocol;
channels[1].networkLayerPreference = QAbstractSocket::IPv6Protocol;
@ -1101,7 +1103,7 @@ void QHttpNetworkConnectionPrivate::startNetworkLayerStateLookup()
else
channels[0].ensureConnection();
} else {
networkLayerState = InProgress;
networkLayerState = IPv4or6;
channels[0].networkLayerPreference = QAbstractSocket::AnyIPProtocol;
channels[0].ensureConnection();
}

View File

@ -165,9 +165,10 @@ public:
enum NetworkLayerPreferenceState {
Unknown,
InProgress,
HostLookupPending,
IPv4,
IPv6
IPv6,
IPv4or6
};
QHttpNetworkConnectionPrivate(const QString &hostName, quint16 port, bool encrypt);

View File

@ -1045,7 +1045,7 @@ void QHttpNetworkConnectionChannel::_q_disconnected()
void QHttpNetworkConnectionChannel::_q_connected()
{
// For the Happy Eyeballs we need to check if this is the first channel to connect.
if (connection->d_func()->networkLayerState == QHttpNetworkConnectionPrivate::InProgress) {
if (connection->d_func()->networkLayerState == QHttpNetworkConnectionPrivate::HostLookupPending || connection->d_func()->networkLayerState == QHttpNetworkConnectionPrivate::IPv4or6) {
if (connection->d_func()->delayedConnectionTimer.isActive())
connection->d_func()->delayedConnectionTimer.stop();
if (networkLayerPreference == QAbstractSocket::IPv4Protocol)
@ -1212,7 +1212,7 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
QPointer<QHttpNetworkConnection> that = connection;
QString errorString = connection->d_func()->errorDetail(errorCode, socket, socket->errorString());
// In the InProgress state the channel should not emit the error.
// In the HostLookupPending state the channel should not emit the error.
// This will instead be handled by the connection.
if (!connection->d_func()->shouldEmitChannelError(socket))
return;