Hi people,

I discussed this feature with Shane and went ahead and made a patch.

The idea is to be able to explicitly specify a TCP host/port target for an HTTP request, while leaving everything else alone. The HTTP Host header, TLS certificate validation, and TLS server name indication would all go against the host from the URL.

I personally need this feature so I can enforce IP address restrictions on HTTP requests. My application resolves the host from the URL in advance, and if the IP address is allowed then I use it as the connect host when the request is made. Currently, I'm making this work in my application by rewriting the URL to contain the IP address and then overriding the HTTP Host header with the original host. Unfortunately, that approach doesn't work for https URLs. My patch makes things cleaner and works with https.

The feature could also be useful in testing environments. And in general, manual connect host overrides are handy to have in protocol network libs just in case.

Justin
diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp
index a279990..a8997fc 100644
--- a/src/network/access/qhttpnetworkconnection.cpp
+++ b/src/network/access/qhttpnetworkconnection.cpp
@@ -77,26 +77,34 @@ const int QHttpNetworkConnectionPrivate::defaultPipelineLength = 3;
 const int QHttpNetworkConnectionPrivate::defaultRePipelineLength = 2;
 
 
-QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(const QString &hostName, quint16 port, bool encrypt)
+QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(const QString &hostName, const QString &connectHostName, quint16 connectPort, bool encrypt)
 : state(RunningState),
   networkLayerState(Unknown),
-  hostName(hostName), port(port), encrypt(encrypt), delayIpv4(true),
+  hostName(hostName), connectHostName(connectHostName),
+  connectPort(connectPort), encrypt(encrypt), delayIpv4(true),
   channelCount(defaultChannelCount)
 #ifndef QT_NO_NETWORKPROXY
   , networkProxy(QNetworkProxy::NoProxy)
 #endif
 {
+    if (this->connectHostName.isEmpty())
+        this->connectHostName = this->hostName;
+
     channels = new QHttpNetworkConnectionChannel[channelCount];
 }
 
-QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(quint16 channelCount, const QString &hostName, quint16 port, bool encrypt)
+QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(quint16 channelCount, const QString &hostName, const QString &connectHostName, quint16 connectPort, bool encrypt)
 : state(RunningState), networkLayerState(Unknown),
-  hostName(hostName), port(port), encrypt(encrypt), delayIpv4(true),
+  hostName(hostName), connectHostName(connectHostName),
+  connectPort(connectPort), encrypt(encrypt), delayIpv4(true),
   channelCount(channelCount)
 #ifndef QT_NO_NETWORKPROXY
   , networkProxy(QNetworkProxy::NoProxy)
 #endif
 {
+    if (this->connectHostName.isEmpty())
+        this->connectHostName = this->hostName;
+
     channels = new QHttpNetworkConnectionChannel[channelCount];
 }
 
@@ -1112,31 +1120,31 @@ void QHttpNetworkConnectionPrivate::_q_connectDelayedChannel()
 }
 
 #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)
+QHttpNetworkConnection::QHttpNetworkConnection(const QString &hostName, const QString &connectHostName, quint16 connectPort, bool encrypt, QObject *parent, QSharedPointer<QNetworkSession> networkSession)
+    : QObject(*(new QHttpNetworkConnectionPrivate(hostName, connectHostName, connectPort, encrypt)), parent)
 {
     Q_D(QHttpNetworkConnection);
     d->networkSession = networkSession;
     d->init();
 }
 
-QHttpNetworkConnection::QHttpNetworkConnection(quint16 connectionCount, const QString &hostName, quint16 port, bool encrypt, QObject *parent, QSharedPointer<QNetworkSession> networkSession)
-     : QObject(*(new QHttpNetworkConnectionPrivate(connectionCount, hostName, port, encrypt)), parent)
+QHttpNetworkConnection::QHttpNetworkConnection(quint16 connectionCount, const QString &hostName, const QString &connectHostName, quint16 connectPort, bool encrypt, QObject *parent, QSharedPointer<QNetworkSession> networkSession)
+     : QObject(*(new QHttpNetworkConnectionPrivate(connectionCount, hostName, connectHostName, connectPort, encrypt)), parent)
 {
     Q_D(QHttpNetworkConnection);
     d->networkSession = networkSession;
     d->init();
 }
 #else
-QHttpNetworkConnection::QHttpNetworkConnection(const QString &hostName, quint16 port, bool encrypt, QObject *parent)
-    : QObject(*(new QHttpNetworkConnectionPrivate(hostName, port, encrypt)), parent)
+QHttpNetworkConnection::QHttpNetworkConnection(const QString &hostName, const QString &connectHostName, quint16 connectPort, bool encrypt, QObject *parent)
+    : QObject(*(new QHttpNetworkConnectionPrivate(hostName, connectHostName, connectPort, encrypt)), parent)
 {
     Q_D(QHttpNetworkConnection);
     d->init();
 }
 
-QHttpNetworkConnection::QHttpNetworkConnection(quint16 connectionCount, const QString &hostName, quint16 port, bool encrypt, QObject *parent)
-     : QObject(*(new QHttpNetworkConnectionPrivate(connectionCount, hostName, port, encrypt)), parent)
+QHttpNetworkConnection::QHttpNetworkConnection(quint16 connectionCount, const QString &hostName, const QString &connectHostName, quint16 connectPort, bool encrypt, QObject *parent)
+     : QObject(*(new QHttpNetworkConnectionPrivate(connectionCount, hostName, connectHostName, connectPort, encrypt)), parent)
 {
     Q_D(QHttpNetworkConnection);
     d->init();
@@ -1153,10 +1161,16 @@ QString QHttpNetworkConnection::hostName() const
     return d->hostName;
 }
 
-quint16 QHttpNetworkConnection::port() const
+QString QHttpNetworkConnection::connectHostName() const
+{
+    Q_D(const QHttpNetworkConnection);
+    return d->connectHostName;
+}
+
+quint16 QHttpNetworkConnection::connectPort() const
 {
     Q_D(const QHttpNetworkConnection);
-    return d->port;
+    return d->connectPort;
 }
 
 QHttpNetworkReply* QHttpNetworkConnection::sendRequest(const QHttpNetworkRequest &request)
diff --git a/src/network/access/qhttpnetworkconnection_p.h b/src/network/access/qhttpnetworkconnection_p.h
index 956499d..ebb75f2 100644
--- a/src/network/access/qhttpnetworkconnection_p.h
+++ b/src/network/access/qhttpnetworkconnection_p.h
@@ -95,18 +95,20 @@ class Q_AUTOTEST_EXPORT QHttpNetworkConnection : public QObject
 public:
 
 #ifndef QT_NO_BEARERMANAGEMENT
-    explicit QHttpNetworkConnection(const QString &hostName, quint16 port = 80, bool encrypt = false, QObject *parent = 0, QSharedPointer<QNetworkSession> networkSession = QSharedPointer<QNetworkSession>());
-    QHttpNetworkConnection(quint16 channelCount, const QString &hostName, quint16 port = 80, bool encrypt = false, QObject *parent = 0, QSharedPointer<QNetworkSession> networkSession = QSharedPointer<QNetworkSession>());
+    explicit QHttpNetworkConnection(const QString &hostName, const QString &connectHostName = QString(), quint16 connectPort = 80, bool encrypt = false, QObject *parent = 0, QSharedPointer<QNetworkSession> networkSession = QSharedPointer<QNetworkSession>());
+    QHttpNetworkConnection(quint16 channelCount, const QString &hostName, const QString &connectHostName = QString(), quint16 connectPort = 80, bool encrypt = false, QObject *parent = 0, QSharedPointer<QNetworkSession> networkSession = QSharedPointer<QNetworkSession>());
 #else
-    explicit QHttpNetworkConnection(const QString &hostName, quint16 port = 80, bool encrypt = false, QObject *parent = 0);
-    QHttpNetworkConnection(quint16 channelCount, const QString &hostName, quint16 port = 80, bool encrypt = false, QObject *parent = 0);
+    explicit QHttpNetworkConnection(const QString &hostName, const QString &connectHostName = QString(), quint16 connectPort = 80, bool encrypt = false, QObject *parent = 0);
+    QHttpNetworkConnection(quint16 channelCount, const QString &hostName, const QString &connectHostName = QString(), quint16 connectPort = 80, bool encrypt = false, QObject *parent = 0);
 #endif
     ~QHttpNetworkConnection();
 
-    //The hostname to which this is connected to.
+    //The virtual hostname of this connection.
     QString hostName() const;
+    //The actual hostname to which this is connected to.
+    QString connectHostName() const;
     //The HTTP port in use.
-    quint16 port() const;
+    quint16 connectPort() const;
 
     //add a new HTTP request through this connection
     QHttpNetworkReply* sendRequest(const QHttpNetworkRequest &request);
@@ -168,8 +170,8 @@ public:
         IPv6
     };
 
-    QHttpNetworkConnectionPrivate(const QString &hostName, quint16 port, bool encrypt);
-    QHttpNetworkConnectionPrivate(quint16 channelCount, const QString &hostName, quint16 port, bool encrypt);
+    QHttpNetworkConnectionPrivate(const QString &hostName, const QString &connectHostName, quint16 connectPort, bool encrypt);
+    QHttpNetworkConnectionPrivate(quint16 channelCount, const QString &hostName, const QString &connectHostName, quint16 connectPort, bool encrypt);
     ~QHttpNetworkConnectionPrivate();
     void init();
 
@@ -214,7 +216,8 @@ public:
     void removeReply(QHttpNetworkReply *reply);
 
     QString hostName;
-    quint16 port;
+    QString connectHostName;
+    quint16 connectPort;
     bool encrypt;
     bool delayIpv4;
 
diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp
index 4b8fe8a..01ae081 100644
--- a/src/network/access/qhttpnetworkconnectionchannel.cpp
+++ b/src/network/access/qhttpnetworkconnectionchannel.cpp
@@ -595,8 +595,8 @@ bool QHttpNetworkConnectionChannel::ensureConnection()
         if (priv && priv->phase == QAuthenticatorPrivate::Done)
             priv->phase = QAuthenticatorPrivate::Start;
 
-        QString connectHost = connection->d_func()->hostName;
-        qint16 connectPort = connection->d_func()->port;
+        QString connectHost = connection->d_func()->connectHostName;
+        qint16 connectPort = connection->d_func()->connectPort;
 
 #ifndef QT_NO_NETWORKPROXY
         // HTTPS always use transparent proxy.
@@ -629,7 +629,7 @@ bool QHttpNetworkConnectionChannel::ensureConnection()
             if (!connection->sslContext().isNull())
                 QSslSocketPrivate::checkSettingSslContext(sslSocket, connection->sslContext());
 
-            sslSocket->connectToHostEncrypted(connectHost, connectPort, QIODevice::ReadWrite, networkLayerPreference);
+            sslSocket->connectToHostEncrypted(connectHost, connectPort, connection->d_func()->hostName, QIODevice::ReadWrite, networkLayerPreference);
             if (ignoreAllSslErrors)
                 sslSocket->ignoreSslErrors();
             sslSocket->ignoreSslErrors(ignoreSslErrorsList);
diff --git a/src/network/access/qhttpnetworkrequest.cpp b/src/network/access/qhttpnetworkrequest.cpp
index e5b2ece..b4b04e9 100644
--- a/src/network/access/qhttpnetworkrequest.cpp
+++ b/src/network/access/qhttpnetworkrequest.cpp
@@ -49,7 +49,8 @@ QT_BEGIN_NAMESPACE
 QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(QHttpNetworkRequest::Operation op,
         QHttpNetworkRequest::Priority pri, const QUrl &newUrl)
     : QHttpNetworkHeaderPrivate(newUrl), operation(op), priority(pri), uploadByteDevice(0),
-      autoDecompress(false), pipeliningAllowed(false), withCredentials(true)
+      autoDecompress(false), pipeliningAllowed(false), withCredentials(true),
+      ssl(false), connectPort(-1)
 {
 }
 
@@ -64,6 +65,8 @@ QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(const QHttpNetworkRequest
     customVerb = other.customVerb;
     withCredentials = other.withCredentials;
     ssl = other.ssl;
+    connectHost = other.connectHost;
+    connectPort = other.connectPort;
 }
 
 QHttpNetworkRequestPrivate::~QHttpNetworkRequestPrivate()
@@ -196,6 +199,24 @@ void QHttpNetworkRequest::setUrl(const QUrl &url)
     d->url = url;
 }
 
+QString QHttpNetworkRequest::connectHost() const
+{
+    return d->connectHost;
+}
+void QHttpNetworkRequest::setConnectHost(const QString &host)
+{
+    d->connectHost = host;
+}
+
+int QHttpNetworkRequest::connectPort() const
+{
+    return d->connectPort;
+}
+void QHttpNetworkRequest::setConnectPort(int port)
+{
+    d->connectPort = port;
+}
+
 bool QHttpNetworkRequest::isSsl() const
 {
     return d->ssl;
diff --git a/src/network/access/qhttpnetworkrequest_p.h b/src/network/access/qhttpnetworkrequest_p.h
index fc4a692..47c01f3 100644
--- a/src/network/access/qhttpnetworkrequest_p.h
+++ b/src/network/access/qhttpnetworkrequest_p.h
@@ -92,6 +92,14 @@ public:
     QUrl url() const;
     void setUrl(const QUrl &url);
 
+    // empty means unset
+    QString connectHost() const;
+    void setConnectHost(const QString &host);
+
+    // -1 means unset
+    int connectPort() const;
+    void setConnectPort(int port);
+
     int majorVersion() const;
     int minorVersion() const;
 
@@ -151,6 +159,8 @@ public:
     bool pipeliningAllowed;
     bool withCredentials;
     bool ssl;
+    QString connectHost;
+    int connectPort;
 };
 
 
diff --git a/src/network/access/qhttpthreaddelegate.cpp b/src/network/access/qhttpthreaddelegate.cpp
index a2cee48..176b9f9 100644
--- a/src/network/access/qhttpthreaddelegate.cpp
+++ b/src/network/access/qhttpthreaddelegate.cpp
@@ -151,11 +151,11 @@ class QNetworkAccessCachedHttpConnection: public QHttpNetworkConnection,
     // Q_OBJECT
 public:
 #ifdef QT_NO_BEARERMANAGEMENT
-    QNetworkAccessCachedHttpConnection(const QString &hostName, quint16 port, bool encrypt)
-        : QHttpNetworkConnection(hostName, port, encrypt)
+    QNetworkAccessCachedHttpConnection(const QString &hostName, const QString &connectHostName, quint16 connectPort, bool encrypt)
+        : QHttpNetworkConnection(hostName, connectHostName, connectPort, encrypt)
 #else
-    QNetworkAccessCachedHttpConnection(const QString &hostName, quint16 port, bool encrypt, QSharedPointer<QNetworkSession> networkSession)
-        : QHttpNetworkConnection(hostName, port, encrypt, /*parent=*/0, networkSession)
+    QNetworkAccessCachedHttpConnection(const QString &hostName, const QString &connectHostName, quint16 connectPort, bool encrypt, QSharedPointer<QNetworkSession> networkSession)
+        : QHttpNetworkConnection(hostName, connectHostName, connectPort, encrypt, /*parent=*/0, networkSession)
 #endif
     {
         setExpires(true);
@@ -249,9 +249,19 @@ void QHttpThreadDelegate::startRequest()
     }
 
     // check if we have an open connection to this host
+
     QUrl urlCopy = httpRequest.url();
     urlCopy.setPort(urlCopy.port(ssl ? 443 : 80));
 
+    QString connectHost = httpRequest.connectHost();
+    if (connectHost.isEmpty())
+        connectHost = urlCopy.host();
+    int connectPort = httpRequest.connectPort();
+    if (connectPort == -1)
+        connectPort = urlCopy.port();
+
+    // note: even though we might connect somewhere else, the cachekey will be
+    // based on the virtual target
 #ifndef QT_NO_NETWORKPROXY
     if (transparentProxy.type() != QNetworkProxy::NoProxy)
         cacheKey = makeCacheKey(urlCopy, &transparentProxy);
@@ -268,9 +278,9 @@ void QHttpThreadDelegate::startRequest()
         // no entry in cache; create an object
         // the http object is actually a QHttpNetworkConnection
 #ifdef QT_NO_BEARERMANAGEMENT
-        httpConnection = new QNetworkAccessCachedHttpConnection(urlCopy.host(), urlCopy.port(), ssl);
+        httpConnection = new QNetworkAccessCachedHttpConnection(urlCopy.host(), connectHost, connectPort, ssl);
 #else
-        httpConnection = new QNetworkAccessCachedHttpConnection(urlCopy.host(), urlCopy.port(), ssl, networkSession);
+        httpConnection = new QNetworkAccessCachedHttpConnection(urlCopy.host(), connectHost, connectPort, ssl, networkSession);
 #endif
 #ifndef QT_NO_SSL
         // Set the QSslConfiguration from this QNetworkRequest.
diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp
index c04421e..bd9e9c7 100644
--- a/src/network/access/qnetworkreplyhttpimpl.cpp
+++ b/src/network/access/qnetworkreplyhttpimpl.cpp
@@ -629,6 +629,13 @@ void QNetworkReplyHttpImplPrivate::postRequest()
     QUrl url = request.url();
     httpRequest.setUrl(url);
 
+    QVariant connectHostAttr = request.attribute(QNetworkRequest::ConnectHostAttribute);
+    if (connectHostAttr.isValid())
+        httpRequest.setConnectHost(connectHostAttr.toString());
+    QVariant connectPortAttr = request.attribute(QNetworkRequest::ConnectPortAttribute);
+    if (connectPortAttr.isValid())
+        httpRequest.setConnectPort(connectPortAttr.toInt());
+
     bool ssl = url.scheme().toLower() == QLatin1String("https");
     q->setAttribute(QNetworkRequest::ConnectionEncryptedAttribute, ssl);
     httpRequest.setSsl(ssl);
diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp
index 3a3e24b..7df9f6e 100644
--- a/src/network/access/qnetworkrequest.cpp
+++ b/src/network/access/qnetworkrequest.cpp
@@ -246,6 +246,17 @@ QT_BEGIN_NAMESPACE
         The QNetworkSession ConnectInBackground property will be set according to
         this attribute.
 
+    \value ConnectHostAttribute
+        Type: QMetaType::String
+        Specify the host to connect to, bypassing the value in the URL. Note
+        that this only affects where the underlying TCP connection will
+        connect. The HTTP Host header and SSL certificate validation will
+        continue to be based on the host value in the URL.
+
+    \value ConnectPortAttribute
+        Type: QMetaType::Int
+        Specify the port to connect to, bypassing the value in the URL.
+
     \value User
         Special type. Additional information can be passed in
         QVariants with types ranging from User to UserMax. The default
diff --git a/src/network/access/qnetworkrequest.h b/src/network/access/qnetworkrequest.h
index 1512c6d..f16a602 100644
--- a/src/network/access/qnetworkrequest.h
+++ b/src/network/access/qnetworkrequest.h
@@ -86,6 +86,8 @@ public:
         DownloadBufferAttribute, // internal
         SynchronousRequestAttribute, // internal
         BackgroundRequestAttribute,
+        ConnectHostAttribute,
+        ConnectPortAttribute,
 
         User = 1000,
         UserMax = 32767
_______________________________________________
Development mailing list
Development@qt-project.org
http://lists.qt-project.org/mailman/listinfo/development

Reply via email to