summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMårten Nordheim <[email protected]>2024-03-13 18:30:36 +0100
committerMårten Nordheim <[email protected]>2024-05-24 22:36:59 +0200
commit48aad482a87b5000db6139abf991367013b9aa19 (patch)
treebe97fd4b66b6863ce99422dbfc0787985b52ecd5
parent72b8c7d59c1b485383f4311310bc17ea2c8beb84 (diff)
Http: Add support for full localsocket paths
[ChangeLog][QtNetwork][QNetworkAccessManager] QNetworkAccessManager now supports using full local server name, as in, named pipes on Windows or path to socket objects on Unix. Task-number: QTBUG-102855 Change-Id: Ifc743f5025b3d8d0b558ecffff437881897915d9 Reviewed-by: Timur Pocheptsov <[email protected]>
-rw-r--r--src/network/access/qhttpnetworkconnection.cpp7
-rw-r--r--src/network/access/qhttpnetworkrequest.cpp10
-rw-r--r--src/network/access/qhttpnetworkrequest_p.h4
-rw-r--r--src/network/access/qhttpthreaddelegate.cpp16
-rw-r--r--src/network/access/qnetworkreplyhttpimpl.cpp7
-rw-r--r--src/network/access/qnetworkrequest.cpp10
-rw-r--r--src/network/access/qnetworkrequest.h1
-rw-r--r--tests/auto/network/access/qnetworkreply_local/tst_qnetworkreply_local.cpp63
8 files changed, 116 insertions, 2 deletions
diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp
index 501a410ae7c..3ef07c69939 100644
--- a/src/network/access/qhttpnetworkconnection.cpp
+++ b/src/network/access/qhttpnetworkconnection.cpp
@@ -304,7 +304,12 @@ void QHttpNetworkConnectionPrivate::prepareRequest(HttpMessagePair &messagePair)
request.setHeaderField("User-Agent", "Mozilla/5.0");
// set the host
value = request.headerField("host");
- if (value.isEmpty()) {
+ if (isLocalSocket && value.isEmpty()) {
+ // The local socket connections might have a full file path, and that
+ // may not be suitable for the Host header. But we can use whatever the
+ // user has set in the URL.
+ request.prependHeaderField("Host", request.url().host().toLocal8Bit());
+ } else if (value.isEmpty()) {
QHostAddress add;
QByteArray host;
if (add.setAddress(hostName)) {
diff --git a/src/network/access/qhttpnetworkrequest.cpp b/src/network/access/qhttpnetworkrequest.cpp
index 7a4ffb16849..06cc0b44649 100644
--- a/src/network/access/qhttpnetworkrequest.cpp
+++ b/src/network/access/qhttpnetworkrequest.cpp
@@ -381,5 +381,15 @@ void QHttpNetworkRequest::setPeerVerifyName(const QString &peerName)
d->peerVerifyName = peerName;
}
+QString QHttpNetworkRequest::fullLocalServerName() const
+{
+ return d->fullLocalServerName;
+}
+
+void QHttpNetworkRequest::setFullLocalServerName(const QString &fullServerName)
+{
+ d->fullLocalServerName = fullServerName;
+}
+
QT_END_NAMESPACE
diff --git a/src/network/access/qhttpnetworkrequest_p.h b/src/network/access/qhttpnetworkrequest_p.h
index 131885f6d22..44440204023 100644
--- a/src/network/access/qhttpnetworkrequest_p.h
+++ b/src/network/access/qhttpnetworkrequest_p.h
@@ -117,6 +117,9 @@ public:
QString peerVerifyName() const;
void setPeerVerifyName(const QString &peerName);
+ QString fullLocalServerName() const;
+ void setFullLocalServerName(const QString &fullServerName);
+
private:
QSharedDataPointer<QHttpNetworkRequestPrivate> d;
friend class QHttpNetworkRequestPrivate;
@@ -140,6 +143,7 @@ public:
QHttpNetworkRequest::Operation operation;
QByteArray customVerb;
+ QString fullLocalServerName; // for local sockets
QHttpNetworkRequest::Priority priority;
mutable QNonContiguousByteDevice* uploadByteDevice;
bool autoDecompress;
diff --git a/src/network/access/qhttpthreaddelegate.cpp b/src/network/access/qhttpthreaddelegate.cpp
index 7dddd1369e9..4e5cf05aef4 100644
--- a/src/network/access/qhttpthreaddelegate.cpp
+++ b/src/network/access/qhttpthreaddelegate.cpp
@@ -290,6 +290,12 @@ void QHttpThreadDelegate::startRequest()
}
}
+ QString extraData = httpRequest.peerVerifyName();
+ if (isLocalSocket) {
+ if (QString path = httpRequest.fullLocalServerName(); !path.isEmpty())
+ extraData = path;
+ }
+
#ifndef QT_NO_NETWORKPROXY
if (transparentProxy.type() != QNetworkProxy::NoProxy)
cacheKey = makeCacheKey(urlCopy, &transparentProxy, httpRequest.peerVerifyName());
@@ -302,10 +308,18 @@ void QHttpThreadDelegate::startRequest()
// the http object is actually a QHttpNetworkConnection
httpConnection = static_cast<QNetworkAccessCachedHttpConnection *>(connections.localData()->requestEntryNow(cacheKey));
if (!httpConnection) {
+
+ QString host = urlCopy.host();
+ // Update the host if a unix socket path or named pipe is used:
+ if (isLocalSocket) {
+ if (QString path = httpRequest.fullLocalServerName(); !path.isEmpty())
+ host = path;
+ }
+
// no entry in cache; create an object
// the http object is actually a QHttpNetworkConnection
httpConnection = new QNetworkAccessCachedHttpConnection(
- http1Parameters.numberOfConnectionsPerHost(), urlCopy.host(), urlCopy.port(), ssl,
+ http1Parameters.numberOfConnectionsPerHost(), host, urlCopy.port(), ssl,
isLocalSocket, connectionType);
if (connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2
|| connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp
index b4aca940a70..89458825e95 100644
--- a/src/network/access/qnetworkreplyhttpimpl.cpp
+++ b/src/network/access/qnetworkreplyhttpimpl.cpp
@@ -809,6 +809,13 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
httpRequest.setPeerVerifyName(newHttpRequest.peerVerifyName());
+ if (scheme.startsWith(("unix"_L1))) {
+ if (QVariant path = newHttpRequest.attribute(QNetworkRequest::FullLocalServerNameAttribute);
+ path.isValid() && path.canConvert<QString>()) {
+ httpRequest.setFullLocalServerName(path.toString());
+ }
+ }
+
// Create the HTTP thread delegate
QHttpThreadDelegate *delegate = new QHttpThreadDelegate;
// Propagate Http/2 settings:
diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp
index 7a1b5426d26..0e1172b15c8 100644
--- a/src/network/access/qnetworkrequest.cpp
+++ b/src/network/access/qnetworkrequest.cpp
@@ -324,6 +324,16 @@ QT_IMPL_METATYPE_EXTERN_TAGGED(QNetworkRequest::RedirectPolicy, QNetworkRequest_
same-origin requests. This only affects the WebAssembly platform.
(This value was introduced in 6.5.)
+ \value FullLocalServerNameAttribute
+ Requests only, type: QMetaType::String
+ Holds the full local server name to be used for the underlying
+ QLocalSocket. This attribute is used by the QNetworkAccessManager
+ to connect to a specific local server, when QLocalSocket's behavior for
+ a simple name isn't enough. The URL in the QNetworkRequest must still
+ use unix+http: or local+http: scheme. And the hostname in the URL will
+ be used for the Host header in the HTTP request.
+ (This value was introduced in 6.8.)
+
\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 e281c748345..368eb99d955 100644
--- a/src/network/access/qnetworkrequest.h
+++ b/src/network/access/qnetworkrequest.h
@@ -70,6 +70,7 @@ public:
ConnectionCacheExpiryTimeoutSecondsAttribute,
Http2CleartextAllowedAttribute,
UseCredentialsAttribute,
+ FullLocalServerNameAttribute,
User = 1000,
UserMax = 32767
diff --git a/tests/auto/network/access/qnetworkreply_local/tst_qnetworkreply_local.cpp b/tests/auto/network/access/qnetworkreply_local/tst_qnetworkreply_local.cpp
index 729605d9c86..6d78c815930 100644
--- a/tests/auto/network/access/qnetworkreply_local/tst_qnetworkreply_local.cpp
+++ b/tests/auto/network/access/qnetworkreply_local/tst_qnetworkreply_local.cpp
@@ -24,6 +24,11 @@ private slots:
void get();
void post();
+
+#if QT_CONFIG(localserver)
+ void fullServerName_data();
+ void fullServerName();
+#endif
};
void tst_QNetworkReply_local::initTestCase_data()
@@ -108,6 +113,64 @@ void tst_QNetworkReply_local::post()
QCOMPARE(firstRequest.receivedData.last(payload.size() + 4), "\r\n\r\n" + payload);
}
+#if QT_CONFIG(localserver)
+void tst_QNetworkReply_local::fullServerName_data()
+{
+#if defined(Q_OS_ANDROID) || defined(QT_PLATFORM_UIKIT)
+ QSKIP("While partially supported, the test as-is doesn't make sense on this platform.");
+#else
+
+ QTest::addColumn<QString>("hostAndPath");
+
+ QTest::newRow("dummy-host") << u"://irrelevant/test"_s;
+ QTest::newRow("no-host") << u":///test"_s;
+#endif
+}
+
+void tst_QNetworkReply_local::fullServerName()
+{
+ QFETCH_GLOBAL(QString, scheme);
+ if (!scheme.startsWith("unix"_L1) && !scheme.startsWith("local"_L1))
+ return; // only relevant for local sockets
+
+ MiniHttpServerV2 server;
+ QLocalServer localServer;
+
+ QString path;
+#ifdef Q_OS_WIN
+ path = uR"(\\.\pipe\qt_networkreply_test_fullServerName)"_s
+ % QString::number(QCoreApplication::applicationPid());
+#else
+ path = u"/tmp/qt_networkreply_test_fullServerName"_s
+ % QString::number(QCoreApplication::applicationPid()) % u".sock"_s;
+#endif
+
+ QVERIFY(localServer.listen(path));
+ server.bind(&localServer);
+
+ QFETCH(QString, hostAndPath);
+ QUrl url(scheme % hostAndPath);
+ QNetworkRequest req(url);
+ req.setAttribute(QNetworkRequest::FullLocalServerNameAttribute, path);
+
+ QNetworkAccessManager manager;
+ std::unique_ptr<QNetworkReply> reply(manager.get(req));
+
+ const bool res = QTest::qWaitFor([reply = reply.get()] { return reply->isFinished(); });
+ QVERIFY(res);
+
+ QCOMPARE(reply->readAll(), QByteArray("Hello World!"));
+ QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
+
+ const QByteArray receivedData = server.peerStates().at(0).receivedData;
+ const QByteArray expectedGet = "GET " % url.path().toUtf8() % " HTTP/1.1\r\n";
+ QVERIFY(receivedData.startsWith(expectedGet));
+
+ const QByteArray expectedHost = "host: " % url.host().toUtf8() % "\r\n";
+ QVERIFY(receivedData.contains(expectedHost));
+}
+#endif
+
QTEST_MAIN(tst_QNetworkReply_local)
#include "tst_qnetworkreply_local.moc"