QNetworkAccessManager: delay IPv4 or IPv6 based on getaddrinfo order
authorMartin Petersson <martin.petersson@nokia.com>
Mon, 19 Dec 2011 10:02:46 +0000 (11:02 +0100)
committerQt by Nokia <qt-info@nokia.com>
Wed, 21 Dec 2011 23:47:03 +0000 (00:47 +0100)
Instead of always delaying IPv4 when we have both Ipv4 and IPv6 we
should use the order we get from getaddrinfo to descide which one
that should be delayed.

Task-number: QTBUG-23066
Change-Id: Ibe8c4d7000abd6e57fe8c6afac8a4a843e17ff27
Reviewed-by: Peter Hartmann <peter.hartmann@nokia.com>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Reviewed-by: Shane Kearns <shane.kearns@accenture.com>
src/network/access/qhttpnetworkconnection.cpp
src/network/access/qhttpnetworkconnection_p.h
src/network/access/qhttpnetworkconnectionchannel.cpp

index b43f2b9..b4004ea 100644 (file)
@@ -81,7 +81,7 @@ const int QHttpNetworkConnectionPrivate::defaultRePipelineLength = 2;
 QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(const QString &hostName, quint16 port, bool encrypt)
 : state(RunningState),
   networkLayerState(Unknown),
-  hostName(hostName), port(port), encrypt(encrypt),
+  hostName(hostName), port(port), encrypt(encrypt), delayIpv4(true),
   channelCount(defaultChannelCount)
 #ifndef QT_NO_NETWORKPROXY
   , networkProxy(QNetworkProxy::NoProxy)
@@ -92,7 +92,7 @@ QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(const QString &host
 
 QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(quint16 channelCount, const QString &hostName, quint16 port, bool encrypt)
 : state(RunningState), networkLayerState(Unknown),
-  hostName(hostName), port(port), encrypt(encrypt),
+  hostName(hostName), port(port), encrypt(encrypt), delayIpv4(true),
   channelCount(channelCount)
 #ifndef QT_NO_NETWORKPROXY
   , networkProxy(QNetworkProxy::NoProxy)
@@ -126,8 +126,8 @@ void QHttpNetworkConnectionPrivate::init()
 #endif
         channels[i].init();
     }
-    ipv4ConnectTimer.setSingleShot(true);
-    QObject::connect(&ipv4ConnectTimer, SIGNAL(timeout()), q, SLOT(_q_connectIPv4Channel()));
+    delayedConnectionTimer.setSingleShot(true);
+    QObject::connect(&delayedConnectionTimer, SIGNAL(timeout()), q, SLOT(_q_connectDelayedChannel()));
 }
 
 void QHttpNetworkConnectionPrivate::pauseConnection()
@@ -189,9 +189,9 @@ bool QHttpNetworkConnectionPrivate::shouldEmitChannelError(QAbstractSocket *sock
     int otherSocket = (i == 0 ? 1 : 0);
 
     // If the IPv4 connection still isn't started we need to start it now.
-    if (ipv4ConnectTimer.isActive()) {
-        ipv4ConnectTimer.stop();
-        channels[0].ensureConnection();
+    if (delayedConnectionTimer.isActive()) {
+        delayedConnectionTimer.stop();
+        channels[otherSocket].ensureConnection();
     }
 
     if (channelCount == 1) {
@@ -974,12 +974,22 @@ void QHttpNetworkConnectionPrivate::_q_hostLookupFinished(QHostInfo info)
 {
     bool bIpv4 = false;
     bool bIpv6 = false;
+    bool foundAddress = false;
 
     foreach (QHostAddress address, info.addresses()) {
-        if (address.protocol() == QAbstractSocket::IPv4Protocol)
+        if (address.protocol() == QAbstractSocket::IPv4Protocol) {
+            if (!foundAddress) {
+                foundAddress = true;
+                delayIpv4 = false;
+            }
             bIpv4 = true;
-        else if (address.protocol() == QAbstractSocket::IPv6Protocol)
+        } else if (address.protocol() == QAbstractSocket::IPv6Protocol) {
+            if (!foundAddress) {
+                foundAddress = true;
+                delayIpv4 = true;
+            }
             bIpv6 = true;
+        }
     }
 
     if (bIpv4 && bIpv6)
@@ -1030,8 +1040,11 @@ void QHttpNetworkConnectionPrivate::startNetworkLayerStateLookup()
         else if (networkSession->configuration().bearerType() == QNetworkConfiguration::BearerHSPA)
             timeout = 400;
 #endif
-        ipv4ConnectTimer.start(timeout);
-        channels[1].ensureConnection();
+        delayedConnectionTimer.start(timeout);
+        if (delayIpv4)
+            channels[1].ensureConnection();
+        else
+            channels[0].ensureConnection();
     } else {
         networkLayerState = InProgress;
         channels[0].networkLayerPreference = QAbstractSocket::AnyIPProtocol;
@@ -1039,9 +1052,12 @@ void QHttpNetworkConnectionPrivate::startNetworkLayerStateLookup()
     }
 }
 
-void QHttpNetworkConnectionPrivate::_q_connectIPv4Channel()
+void QHttpNetworkConnectionPrivate::_q_connectDelayedChannel()
 {
-    channels[0].ensureConnection();
+    if (delayIpv4)
+        channels[0].ensureConnection();
+    else
+        channels[1].ensureConnection();
 }
 
 #ifndef QT_NO_BEARERMANAGEMENT
index 18ec92a..75da382 100644 (file)
@@ -135,7 +135,7 @@ private:
 
     Q_PRIVATE_SLOT(d_func(), void _q_startNextRequest())
     Q_PRIVATE_SLOT(d_func(), void _q_hostLookupFinished(QHostInfo))
-    Q_PRIVATE_SLOT(d_func(), void _q_connectIPv4Channel())
+    Q_PRIVATE_SLOT(d_func(), void _q_connectDelayedChannel())
 };
 
 
@@ -198,7 +198,7 @@ public:
     void _q_startNextRequest(); // send the next request from the queue
 
     void _q_hostLookupFinished(QHostInfo info);
-    void _q_connectIPv4Channel();
+    void _q_connectDelayedChannel();
 
     void createAuthorization(QAbstractSocket *socket, QHttpNetworkRequest &request);
 
@@ -210,9 +210,10 @@ public:
     QString hostName;
     quint16 port;
     bool encrypt;
+    bool delayIpv4;
 
     const int channelCount;
-    QTimer ipv4ConnectTimer;
+    QTimer delayedConnectionTimer;
     QHttpNetworkConnectionChannel *channels; // parallel connections to the server
     bool shouldEmitChannelError(QAbstractSocket *socket);
 
index edf66ff..ab178be 100644 (file)
@@ -954,8 +954,8 @@ void QHttpNetworkConnectionChannel::_q_connected()
     // For the Happy Eyeballs we need to check if this is the first channel to connect.
     if (!pendingEncrypt) {
         if (connection->d_func()->networkLayerState == QHttpNetworkConnectionPrivate::InProgress) {
-            if (connection->d_func()->ipv4ConnectTimer.isActive())
-                connection->d_func()->ipv4ConnectTimer.stop();
+            if (connection->d_func()->delayedConnectionTimer.isActive())
+                connection->d_func()->delayedConnectionTimer.stop();
             if (networkLayerPreference == QAbstractSocket::IPv4Protocol)
                 connection->d_func()->networkLayerState = QHttpNetworkConnectionPrivate::IPv4;
             else if (networkLayerPreference == QAbstractSocket::IPv6Protocol)