Add Happy-Eyeballs style IPv6 connection establishing.
authorMartin Petersson <martin.petersson@nokia.com>
Fri, 1 Jul 2011 11:26:47 +0000 (13:26 +0200)
committerQt by Nokia <qt-info@nokia.com>
Mon, 4 Jul 2011 11:04:57 +0000 (13:04 +0200)
In the cases where a DNS lookup will give you both an IPv4 and IPv6
address, this will start two connection channels at the same time.
One trying to connect using IPv4 and one on IPv6. This is done so
that we can use the fastest one for the connection. To do this we
have to do the hostlookup in the connection. The result is then
in the cache for the individual socket so it will not need to do
another lookup.

Task-number: QTBUG-16458
Change-Id: I806c20168d9c5edc2831b80f82a2bd570b36d5fa
Reviewed-on: http://codereview.qt.nokia.com/1003
Reviewed-by: Qt Sanity Bot <qt_sanity_bot@ovi.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
src/network/access/qhttpnetworkconnectionchannel_p.h
src/network/socket/qabstractsocket.cpp
src/network/socket/qabstractsocket.h
src/network/socket/qabstractsocket_p.h
src/network/ssl/qsslsocket.cpp
src/network/ssl/qsslsocket.h

index d950af4..a8a4fd9 100644 (file)
@@ -46,6 +46,7 @@
 #include <private/qnetworkrequest_p.h>
 #include <private/qobject_p.h>
 #include <private/qauthenticator_p.h>
+#include "private/qhostinfo_p.h"
 #include <qnetworkproxy.h>
 #include <qauthenticator.h>
 
@@ -83,7 +84,8 @@ const int QHttpNetworkConnectionPrivate::defaultRePipelineLength = 2;
 QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(const QString &hostName, quint16 port, bool encrypt)
 : state(RunningState),
   hostName(hostName), port(port), encrypt(encrypt),
-  channelCount(defaultChannelCount)
+  channelCount(defaultChannelCount),
+  networkLayerState(Unknown)
 #ifndef QT_NO_NETWORKPROXY
   , networkProxy(QNetworkProxy::NoProxy)
 #endif
@@ -94,7 +96,8 @@ QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(const QString &host
 QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(quint16 channelCount, const QString &hostName, quint16 port, bool encrypt)
 : state(RunningState),
   hostName(hostName), port(port), encrypt(encrypt),
-  channelCount(channelCount)
+  channelCount(channelCount),
+  networkLayerState(Unknown)
 #ifndef QT_NO_NETWORKPROXY
   , networkProxy(QNetworkProxy::NoProxy)
 #endif
@@ -174,6 +177,45 @@ int QHttpNetworkConnectionPrivate::indexOf(QAbstractSocket *socket) const
     return 0;
 }
 
+// If the connection is in the InProgress 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.
+bool QHttpNetworkConnectionPrivate::shouldEmitChannelError(QAbstractSocket *socket)
+{
+    Q_Q(QHttpNetworkConnection);
+
+    bool emitError = true;
+    int i = indexOf(socket);
+    int otherSocket = (i == 0 ? 1 : 0);
+
+    if (networkLayerState == QHttpNetworkConnectionPrivate::InProgress) {
+        if (channels[otherSocket].isSocketBusy() && (channels[otherSocket].state != QHttpNetworkConnectionChannel::ClosingState)) {
+            // this was the first socket to fail.
+            channels[i].close();
+            emitError = false;
+        }
+        else {
+            // Both connection attempts has failed.
+            networkLayerState = QHttpNetworkConnectionPrivate::Unknown;
+            channels[i].close();
+            emitError = true;
+        }
+    } else {
+        if ((networkLayerState == QHttpNetworkConnectionPrivate::IPv4) && (channels[i].networkLayerPreference != QAbstractSocket::IPv4Protocol)
+            || (networkLayerState == QHttpNetworkConnectionPrivate::IPv6) && (channels[i].networkLayerPreference != QAbstractSocket::IPv6Protocol)) {
+            // First connection worked so this is the second one to complete and it failed.
+            channels[i].close();
+            QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
+            emitError = false;
+        }
+        if (networkLayerState == QHttpNetworkConnectionPrivate::Unknown)
+            qWarning() << "We got a connection error when networkLayerState is Unknown";
+    }
+    return emitError;
+}
+
+
 qint64 QHttpNetworkConnectionPrivate::uncompressedBytesAvailable(const QHttpNetworkReply &reply) const
 {
     return reply.d_func()->responseData.byteAmount();
@@ -469,17 +511,23 @@ QHttpNetworkReply* QHttpNetworkConnectionPrivate::queueRequest(const QHttpNetwor
         break;
     }
 
-    // this used to be called via invokeMethod and a QueuedConnection
-    // It is the only place _q_startNextRequest is called directly without going
-    // through the event loop using a QueuedConnection.
-    // This is dangerous because of recursion that might occur when emitting
-    // signals as DirectConnection from this code path. Therefore all signal
-    // emissions that can come out from this code path need to
-    // be QueuedConnection.
-    // We are currently trying to fine-tune this.
-    _q_startNextRequest();
-
-
+    // For Happy Eyeballs the networkLayerState is set to Unkown
+    // 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) {
+        startHostInfoLookup();
+    } else if ( networkLayerState == IPv4 || networkLayerState == IPv6 ) {
+        // this used to be called via invokeMethod and a QueuedConnection
+        // It is the only place _q_startNextRequest is called directly without going
+        // through the event loop using a QueuedConnection.
+        // This is dangerous because of recursion that might occur when emitting
+        // signals as DirectConnection from this code path. Therefore all signal
+        // emissions that can come out from this code path need to
+        // be QueuedConnection.
+        // We are currently trying to fine-tune this.
+        _q_startNextRequest();
+    }
     return reply;
 }
 
@@ -781,6 +829,10 @@ void QHttpNetworkConnectionPrivate::removeReply(QHttpNetworkReply *reply)
 // although it is called _q_startNextRequest, it will actually start multiple requests when possible
 void QHttpNetworkConnectionPrivate::_q_startNextRequest()
 {
+    // If there is no network layer state decided we should not start any new requests.
+    if (networkLayerState == Unknown || networkLayerState == InProgress)
+        return;
+
     // If the QHttpNetworkConnection is currently paused then bail out immediately
     if (state == PausedState)
         return;
@@ -830,11 +882,15 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
     // connected or not. This is to reuse connected channels before we connect new once.
     int queuedRequest = highPriorityQueue.count() + lowPriorityQueue.count();
     for (int i = 0; i < channelCount; ++i) {
-        if (channels[i].socket->state() == QAbstractSocket::ConnectingState)
+        if ((channels[i].socket->state() == QAbstractSocket::ConnectingState) || (channels[i].socket->state() == QAbstractSocket::HostLookupState))
             queuedRequest--;
         if ( queuedRequest <=0 )
             break;
         if (!channels[i].reply && !channels[i].isSocketBusy() && (channels[i].socket->state() == QAbstractSocket::UnconnectedState)) {
+            if (networkLayerState == IPv4)
+                channels[i].networkLayerPreference = QAbstractSocket::IPv4Protocol;
+            else if (networkLayerState == IPv6)
+                channels[i].networkLayerPreference = QAbstractSocket::IPv6Protocol;
             channels[i].ensureConnection();
             queuedRequest--;
         }
@@ -853,6 +909,100 @@ void QHttpNetworkConnectionPrivate::readMoreLater(QHttpNetworkReply *reply)
     }
 }
 
+
+
+// The first time we start the connection is used we do not know if we
+// should use IPv4 or IPv6. So we start a hostlookup to figure this out.
+// Later when we do the connection the socket will not need to do another
+// lookup as then the hostinfo will already be in the cache.
+void QHttpNetworkConnectionPrivate::startHostInfoLookup()
+{
+    // At this time all channels should be unconnected.
+    Q_ASSERT(!channels[0].isSocketBusy());
+    Q_ASSERT(!channels[1].isSocketBusy());
+
+    networkLayerState = InProgress;
+
+    // check if we already now can descide if this is IPv4 or IPv6
+    QHostAddress temp;
+    if (temp.setAddress(hostName)) {
+        if (temp.protocol() == QAbstractSocket::IPv4Protocol) {
+            networkLayerState = QHttpNetworkConnectionPrivate::IPv4;
+            QMetaObject::invokeMethod(this->q_func(), "_q_startNextRequest", Qt::QueuedConnection);
+            return;
+        } else if (temp.protocol() == QAbstractSocket::IPv6Protocol) {
+            networkLayerState = QHttpNetworkConnectionPrivate::IPv6;
+            QMetaObject::invokeMethod(this->q_func(), "_q_startNextRequest", Qt::QueuedConnection);
+            return;
+        }
+    } else {
+        int hostLookupId;
+        bool immediateResultValid = false;
+        QHostInfo hostInfo = qt_qhostinfo_lookup(hostName,
+                                                 this->q_func(),
+                                                 SLOT(_q_hostLookupFinished(QHostInfo)),
+                                                 &immediateResultValid,
+                                                 &hostLookupId);
+        if (immediateResultValid) {
+            _q_hostLookupFinished(hostInfo);
+        }
+    }
+}
+
+
+void QHttpNetworkConnectionPrivate::_q_hostLookupFinished(QHostInfo info)
+{
+    bool bIpv4 = false;
+    bool bIpv6 = false;
+
+    foreach (QHostAddress address, info.addresses()) {
+        if (address.protocol() == QAbstractSocket::IPv4Protocol)
+            bIpv4 = true;
+        else if (address.protocol() == QAbstractSocket::IPv6Protocol)
+            bIpv6 = true;
+    }
+
+    if (bIpv4 && bIpv6)
+        startNetworkLayerStateLookup();
+    else if (bIpv4) {
+        networkLayerState = QHttpNetworkConnectionPrivate::IPv4;
+        QMetaObject::invokeMethod(this->q_func(), "_q_startNextRequest", Qt::QueuedConnection);
+    } else if (bIpv6) {
+        networkLayerState = QHttpNetworkConnectionPrivate::IPv6;
+        QMetaObject::invokeMethod(this->q_func(), "_q_startNextRequest", Qt::QueuedConnection);
+    } else {
+        if (dequeueRequest(channels[0].socket)) {
+            emitReplyError(channels[0].socket, channels[0].reply, QNetworkReply::HostNotFoundError);
+            networkLayerState = QHttpNetworkConnectionPrivate::Unknown;
+        } else {
+            // Should not happen
+            qWarning() << "QHttpNetworkConnectionPrivate::_q_hostLookupFinished could not dequeu request";
+            networkLayerState = QHttpNetworkConnectionPrivate::Unknown;
+        }
+    }
+}
+
+
+// This will be used if the host lookup found both and Ipv4 and
+// Ipv6 address. Then we will start up two connections and pick
+// the network layer of the one that finish first. The second
+// connection will then be disconnected.
+void QHttpNetworkConnectionPrivate::startNetworkLayerStateLookup()
+{
+    // At this time all channels should be unconnected.
+    Q_ASSERT(!channels[0].isSocketBusy());
+    Q_ASSERT(!channels[1].isSocketBusy());
+
+    networkLayerState = InProgress;
+
+    channels[0].networkLayerPreference = QAbstractSocket::IPv4Protocol;
+    channels[1].networkLayerPreference = QAbstractSocket::IPv6Protocol;
+
+    channels[0].ensureConnection(); // Possibly delay this one..
+    channels[1].ensureConnection();
+}
+
+
 #ifndef QT_NO_BEARERMANAGEMENT
 QHttpNetworkConnection::QHttpNetworkConnection(const QString &hostName, quint16 port, bool encrypt, QObject *parent, QSharedPointer<QNetworkSession> networkSession)
     : QObject(*(new QHttpNetworkConnectionPrivate(hostName, port, encrypt)), parent)
index 6c953be..0c86fd9 100644 (file)
@@ -82,6 +82,7 @@ QT_BEGIN_NAMESPACE
 class QHttpNetworkRequest;
 class QHttpNetworkReply;
 class QByteArray;
+class QHostInfo;
 
 class QHttpNetworkConnectionPrivate;
 class Q_AUTOTEST_EXPORT QHttpNetworkConnection : public QObject
@@ -132,6 +133,7 @@ private:
     friend class QHttpNetworkConnectionChannel;
 
     Q_PRIVATE_SLOT(d_func(), void _q_startNextRequest())
+    Q_PRIVATE_SLOT(d_func(), void _q_hostLookupFinished(QHostInfo))
 };
 
 
@@ -152,6 +154,13 @@ public:
         PausedState = 1,
     };
 
+    enum NetworkLayerPreferenceState {
+        Unknown,
+        InProgress,
+        IPv4,
+        IPv6
+    };
+
     QHttpNetworkConnectionPrivate(const QString &hostName, quint16 port, bool encrypt);
     QHttpNetworkConnectionPrivate(quint16 channelCount, const QString &hostName, quint16 port, bool encrypt);
     ~QHttpNetworkConnectionPrivate();
@@ -160,6 +169,7 @@ public:
     void pauseConnection();
     void resumeConnection();
     ConnectionState state;
+    NetworkLayerPreferenceState networkLayerState;
 
     enum { ChunkSize = 4096 };
 
@@ -179,9 +189,14 @@ public:
 
     void copyCredentials(int fromChannel, QAuthenticator *auth, bool isProxy);
 
+    void startHostInfoLookup();
+    void startNetworkLayerStateLookup();
+
     // private slots
     void _q_startNextRequest(); // send the next request from the queue
 
+    void _q_hostLookupFinished(QHostInfo info);
+
     void createAuthorization(QAbstractSocket *socket, QHttpNetworkRequest &request);
 
     QString errorDetail(QNetworkReply::NetworkError errorCode, QAbstractSocket *socket,
@@ -198,6 +213,7 @@ public:
 
     const int channelCount;
     QHttpNetworkConnectionChannel *channels; // parallel connections to the server
+    bool shouldEmitChannelError(QAbstractSocket *socket);
 
     qint64 uncompressedBytesAvailable(const QHttpNetworkReply &reply) const;
     qint64 uncompressedBytesAvailableNextBlock(const QHttpNetworkReply &reply) const;
index 7c644ad..b8ed8ee 100644 (file)
@@ -80,6 +80,7 @@ QHttpNetworkConnectionChannel::QHttpNetworkConnectionChannel()
 #endif
     , pipeliningSupported(PipeliningSupportUnknown)
     , connection(0)
+    , networkLayerPreference(QAbstractSocket::AnyIPProtocol)
 {
     // Inlining this function in the header leads to compiler error on
     // release-armv5, on at least timebox 9.2 and 10.1.
@@ -594,7 +595,7 @@ bool QHttpNetworkConnectionChannel::ensureConnection()
         if (ssl) {
 #ifndef QT_NO_OPENSSL
             QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket);
-            sslSocket->connectToHostEncrypted(connectHost, connectPort);
+            sslSocket->connectToHostEncrypted(connectHost, connectPort, QIODevice::ReadWrite, networkLayerPreference);
             if (ignoreAllSslErrors)
                 sslSocket->ignoreSslErrors();
             sslSocket->ignoreSslErrors(ignoreSslErrorsList);
@@ -613,12 +614,12 @@ bool QHttpNetworkConnectionChannel::ensureConnection()
                     && connection->cacheProxy().type() == QNetworkProxy::NoProxy
                     && connection->transparentProxy().type() == QNetworkProxy::NoProxy) {
 #endif
-                socket->connectToHost(connectHost, connectPort, QIODevice::ReadWrite | QIODevice::Unbuffered);
+                socket->connectToHost(connectHost, connectPort, QIODevice::ReadWrite | QIODevice::Unbuffered, networkLayerPreference);
                 // For an Unbuffered QTcpSocket, the read buffer size has a special meaning.
                 socket->setReadBufferSize(1*1024);
 #ifndef QT_NO_NETWORKPROXY
             } else {
-                socket->connectToHost(connectHost, connectPort);
+                socket->connectToHost(connectHost, connectPort, QIODevice::ReadWrite, networkLayerPreference);
 
                 // limit the socket read buffer size. we will read everything into
                 // the QHttpNetworkReply anyway, so let's grow only that and not
@@ -1002,6 +1003,25 @@ 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 (!pendingEncrypt) {
+        if (connection->d_func()->networkLayerState == QHttpNetworkConnectionPrivate::InProgress) {
+            if (networkLayerPreference == QAbstractSocket::IPv4Protocol)
+                connection->d_func()->networkLayerState = QHttpNetworkConnectionPrivate::IPv4;
+            else if (networkLayerPreference == QAbstractSocket::IPv6Protocol)
+                connection->d_func()->networkLayerState = QHttpNetworkConnectionPrivate::IPv6;
+        } else {
+            if (((connection->d_func()->networkLayerState == QHttpNetworkConnectionPrivate::IPv4) && (networkLayerPreference != QAbstractSocket::IPv4Protocol))
+                || ((connection->d_func()->networkLayerState == QHttpNetworkConnectionPrivate::IPv6) && (networkLayerPreference != QAbstractSocket::IPv6Protocol))) {
+                close();
+                // This is the second connection so it has to be closed and we can schedule it for another request.
+                QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
+                return;
+            }
+            //The connections networkLayerState had already been decided.
+        }
+    }
+
     // improve performance since we get the request sent by the kernel ASAP
     //socket->setSocketOption(QAbstractSocket::LowDelayOption, 1);
     // We have this commented out now. It did not have the effect we wanted. If we want to
@@ -1089,6 +1109,11 @@ 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.
+    // This will instead be handled by the connection.
+    if (!connection->d_func()->shouldEmitChannelError(socket))
+        return;
+
     // Need to dequeu the request so that we can emit the error.
     if (!reply)
         connection->d_func()->dequeueRequest(socket);
index f635cc9..8400f62 100644 (file)
@@ -136,6 +136,8 @@ public:
     void detectPipeliningSupport();
 
     QHttpNetworkConnectionChannel();
+
+    QAbstractSocket::NetworkLayerProtocol networkLayerPreference;
     
     void setConnection(QHttpNetworkConnection *c);
     QPointer<QHttpNetworkConnection> connection;
index 5316626..9afb211 100644 (file)
@@ -483,7 +483,8 @@ QAbstractSocketPrivate::QAbstractSocketPrivate()
       hostLookupId(-1),
       socketType(QAbstractSocket::UnknownSocketType),
       state(QAbstractSocket::UnconnectedState),
-      socketError(QAbstractSocket::UnknownSocketError)
+      socketError(QAbstractSocket::UnknownSocketError),
+      preferredNetworkLayerProtocol(QAbstractSocket::UnknownNetworkLayerProtocol)
 {
 }
 
@@ -899,7 +900,16 @@ void QAbstractSocketPrivate::_q_startConnecting(const QHostInfo &hostInfo)
         qWarning("QAbstractSocketPrivate::_q_startConnecting() received hostInfo for wrong lookup ID %d expected %d", hostInfo.lookupId(), hostLookupId);
     }
 
-    addresses = hostInfo.addresses();
+    // Only add the addresses for the prefered network layer.
+    // Or all if prefered network layer is not set.
+    if (preferredNetworkLayerProtocol == QAbstractSocket::UnknownNetworkLayerProtocol || preferredNetworkLayerProtocol == QAbstractSocket::AnyIPProtocol) {
+        addresses = hostInfo.addresses();
+    } else {
+        foreach (QHostAddress address, hostInfo.addresses())
+            if (address.protocol() == preferredNetworkLayerProtocol)
+                addresses += address;
+    }
+
 
 #if defined(QABSTRACTSOCKET_DEBUG)
     QString s = QLatin1String("{");
@@ -1330,8 +1340,12 @@ bool QAbstractSocket::isValid() const
     \sa state(), peerName(), peerAddress(), peerPort(), waitForConnected()
 */
 void QAbstractSocket::connectToHost(const QString &hostName, quint16 port,
-                                    OpenMode openMode)
+                                    OpenMode openMode,
+                                    NetworkLayerProtocol protocol)
 {
+    Q_D(QAbstractSocket);
+    d->preferredNetworkLayerProtocol = protocol;
+
     QMetaObject::invokeMethod(this, "connectToHostImplementation",
                               Qt::DirectConnection,
                               Q_ARG(QString, hostName),
index 2717ceb..2501df6 100644 (file)
@@ -129,7 +129,7 @@ public:
     virtual ~QAbstractSocket();
 
     // ### Qt 5: Make connectToHost() and disconnectFromHost() virtual.
-    void connectToHost(const QString &hostName, quint16 port, OpenMode mode = ReadWrite);
+    void connectToHost(const QString &hostName, quint16 port, OpenMode mode = ReadWrite, NetworkLayerProtocol protocol = AnyIPProtocol);
     void connectToHost(const QHostAddress &address, quint16 port, OpenMode mode = ReadWrite);
     void disconnectFromHost();
 
index cf7d988..978fb14 100644 (file)
@@ -156,6 +156,8 @@ public:
 
     QAbstractSocket::SocketError socketError;
 
+    QAbstractSocket::NetworkLayerProtocol preferredNetworkLayerProtocol;
+
     bool prePauseReadSocketNotifierState;
     bool prePauseWriteSocketNotifierState;
     bool prePauseExceptionSocketNotifierState;
index df61fb6..f191ed9 100644 (file)
@@ -405,7 +405,7 @@ QSslSocket::~QSslSocket()
 
     \sa connectToHost(), startClientEncryption(), waitForConnected(), waitForEncrypted()
 */
-void QSslSocket::connectToHostEncrypted(const QString &hostName, quint16 port, OpenMode mode)
+void QSslSocket::connectToHostEncrypted(const QString &hostName, quint16 port, OpenMode mode, NetworkLayerProtocol protocol)
 {
     Q_D(QSslSocket);
     if (d->state == ConnectedState || d->state == ConnectingState) {
@@ -419,7 +419,7 @@ void QSslSocket::connectToHostEncrypted(const QString &hostName, quint16 port, O
 
     // Note: When connecting to localhost, some platforms (e.g., HP-UX and some BSDs)
     // establish the connection immediately (i.e., first attempt).
-    connectToHost(hostName, port, mode);
+    connectToHost(hostName, port, mode, protocol);
 }
 
 /*!
@@ -434,7 +434,8 @@ void QSslSocket::connectToHostEncrypted(const QString &hostName, quint16 port, O
     \sa connectToHostEncrypted()
 */
 void QSslSocket::connectToHostEncrypted(const QString &hostName, quint16 port,
-                                        const QString &sslPeerName, OpenMode mode)
+                                        const QString &sslPeerName, OpenMode mode,
+                                        NetworkLayerProtocol protocol)
 {
     Q_D(QSslSocket);
     if (d->state == ConnectedState || d->state == ConnectingState) {
@@ -449,7 +450,7 @@ void QSslSocket::connectToHostEncrypted(const QString &hostName, quint16 port,
 
     // Note: When connecting to localhost, some platforms (e.g., HP-UX and some BSDs)
     // establish the connection immediately (i.e., first attempt).
-    connectToHost(hostName, port, mode);
+    connectToHost(hostName, port, mode, protocol);
 }
 
 /*!
@@ -1740,7 +1741,7 @@ void QSslSocket::connectToHostImplementation(const QString &hostName, quint16 po
     d->plainSocket->setProperty("_q_user-agent", property("_q_user-agent"));
 #endif
     QIODevice::open(openMode);
-    d->plainSocket->connectToHost(hostName, port, openMode);
+    d->plainSocket->connectToHost(hostName, port, openMode, d->preferredNetworkLayerProtocol);
     d->cachedSocketDescriptor = d->plainSocket->socketDescriptor();
 }
 
index 1e7c67c..f175ffd 100644 (file)
@@ -85,8 +85,8 @@ public:
     ~QSslSocket();
 
     // Autostarting the SSL client handshake.
-    void connectToHostEncrypted(const QString &hostName, quint16 port, OpenMode mode = ReadWrite);
-    void connectToHostEncrypted(const QString &hostName, quint16 port, const QString &sslPeerName, OpenMode mode = ReadWrite);
+    void connectToHostEncrypted(const QString &hostName, quint16 port, OpenMode mode = ReadWrite, NetworkLayerProtocol protocol = AnyIPProtocol);
+    void connectToHostEncrypted(const QString &hostName, quint16 port, const QString &sslPeerName, OpenMode mode = ReadWrite, NetworkLayerProtocol protocol = AnyIPProtocol);
     bool setSocketDescriptor(int socketDescriptor, SocketState state = ConnectedState,
                              OpenMode openMode = ReadWrite);