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 <timur.pocheptsov@qt.io>
This commit is contained in:
parent
72b8c7d59c
commit
48aad482a8
@ -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)) {
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -70,6 +70,7 @@ public:
|
||||
ConnectionCacheExpiryTimeoutSecondsAttribute,
|
||||
Http2CleartextAllowedAttribute,
|
||||
UseCredentialsAttribute,
|
||||
FullLocalServerNameAttribute,
|
||||
|
||||
User = 1000,
|
||||
UserMax = 32767
|
||||
|
@ -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"
|
||||
|
Loading…
x
Reference in New Issue
Block a user