diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp index e481ab36fb9..501a410ae7c 100644 --- a/src/network/access/qhttpnetworkconnection.cpp +++ b/src/network/access/qhttpnetworkconnection.cpp @@ -52,10 +52,11 @@ static int getPreferredActiveChannelCount(QHttpNetworkConnection::ConnectionType QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate( quint16 connectionCount, const QString &hostName, quint16 port, bool encrypt, - QHttpNetworkConnection::ConnectionType type) + bool isLocalSocket, QHttpNetworkConnection::ConnectionType type) : hostName(hostName), port(port), encrypt(encrypt), + isLocalSocket(isLocalSocket), activeChannelCount(getPreferredActiveChannelCount(type, connectionCount)), channelCount(connectionCount), channels(new QHttpNetworkConnectionChannel[channelCount]), @@ -64,6 +65,8 @@ QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate( #endif connectionType(type) { + if (isLocalSocket) // Don't try to do host lookup for local sockets + networkLayerState = IPv4; // We allocate all 6 channels even if it's an HTTP/2-enabled // connection: in case the protocol negotiation via NPN/ALPN fails, // we will have normally working HTTP/1.1. @@ -533,7 +536,9 @@ QHttpNetworkConnectionPrivate::parseRedirectResponse(QHttpNetworkReply *reply) // Check redirect url protocol const QUrl priorUrl(reply->request().url()); - if (redirectUrl.scheme() == "http"_L1 || redirectUrl.scheme() == "https"_L1) { + const QString targetUrlScheme = redirectUrl.scheme(); + if (targetUrlScheme == "http"_L1 || targetUrlScheme == "https"_L1 + || targetUrlScheme.startsWith("unix"_L1)) { switch (reply->request().redirectPolicy()) { case QNetworkRequest::NoLessSafeRedirectPolicy: // Here we could handle https->http redirects as InsecureProtocolError. @@ -544,7 +549,7 @@ QHttpNetworkConnectionPrivate::parseRedirectResponse(QHttpNetworkReply *reply) break; case QNetworkRequest::SameOriginRedirectPolicy: if (priorUrl.host() != redirectUrl.host() - || priorUrl.scheme() != redirectUrl.scheme() + || priorUrl.scheme() != targetUrlScheme || priorUrl.port() != redirectUrl.port()) { return {{}, QNetworkReply::InsecureRedirectError}; } @@ -1346,9 +1351,9 @@ void QHttpNetworkConnectionPrivate::_q_connectDelayedChannel() } QHttpNetworkConnection::QHttpNetworkConnection(quint16 connectionCount, const QString &hostName, - quint16 port, bool encrypt, QObject *parent, + quint16 port, bool encrypt, bool isLocalSocket, QObject *parent, QHttpNetworkConnection::ConnectionType connectionType) - : QObject(*(new QHttpNetworkConnectionPrivate(connectionCount, hostName, port, encrypt, + : QObject(*(new QHttpNetworkConnectionPrivate(connectionCount, hostName, port, encrypt, isLocalSocket, connectionType)), parent) { Q_D(QHttpNetworkConnection); diff --git a/src/network/access/qhttpnetworkconnection_p.h b/src/network/access/qhttpnetworkconnection_p.h index c2d062fb16d..5e4bce5eb06 100644 --- a/src/network/access/qhttpnetworkconnection_p.h +++ b/src/network/access/qhttpnetworkconnection_p.h @@ -64,7 +64,8 @@ public: }; QHttpNetworkConnection(quint16 channelCount, const QString &hostName, quint16 port = 80, - bool encrypt = false, QObject *parent = nullptr, + bool encrypt = false, bool isLocalSocket = false, + QObject *parent = nullptr, ConnectionType connectionType = ConnectionTypeHTTP); ~QHttpNetworkConnection(); @@ -155,7 +156,8 @@ public: }; QHttpNetworkConnectionPrivate(quint16 connectionCount, const QString &hostName, quint16 port, - bool encrypt, QHttpNetworkConnection::ConnectionType type); + bool encrypt, bool isLocalSocket, + QHttpNetworkConnection::ConnectionType type); ~QHttpNetworkConnectionPrivate(); void init(); @@ -205,6 +207,7 @@ public: QString hostName; quint16 port; bool encrypt; + bool isLocalSocket; bool delayIpv4 = true; // Number of channels we are trying to use at the moment: diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp index c58ffab5ed3..8688e4b8d71 100644 --- a/src/network/access/qhttpnetworkconnectionchannel.cpp +++ b/src/network/access/qhttpnetworkconnectionchannel.cpp @@ -79,6 +79,8 @@ void QHttpNetworkConnectionChannel::init() #ifndef QT_NO_SSL if (connection->d_func()->encrypt) socket = new QSslSocket; + else if (connection->d_func()->isLocalSocket) + socket = new QLocalSocket; else socket = new QTcpSocket; #else diff --git a/src/network/access/qhttpthreaddelegate.cpp b/src/network/access/qhttpthreaddelegate.cpp index b0ae0dcf446..7dddd1369e9 100644 --- a/src/network/access/qhttpthreaddelegate.cpp +++ b/src/network/access/qhttpthreaddelegate.cpp @@ -95,7 +95,9 @@ static QByteArray makeCacheKey(QUrl &url, QNetworkProxy *proxy, const QString &p QUrl copy = url; QString scheme = copy.scheme(); bool isEncrypted = scheme == "https"_L1 || scheme == "preconnect-https"_L1; - copy.setPort(copy.port(isEncrypted ? 443 : 80)); + const bool isLocalSocket = scheme.startsWith("unix"_L1); + if (!isLocalSocket) + copy.setPort(copy.port(isEncrypted ? 443 : 80)); if (scheme == "preconnect-http"_L1) copy.setScheme("http"_L1); else if (scheme == "preconnect-https"_L1) @@ -145,9 +147,9 @@ class QNetworkAccessCachedHttpConnection: public QHttpNetworkConnection, { // Q_OBJECT public: - QNetworkAccessCachedHttpConnection(quint16 connectionCount, const QString &hostName, quint16 port, bool encrypt, + QNetworkAccessCachedHttpConnection(quint16 connectionCount, const QString &hostName, quint16 port, bool encrypt, bool isLocalSocket, QHttpNetworkConnection::ConnectionType connectionType) - : QHttpNetworkConnection(connectionCount, hostName, port, encrypt, /*parent=*/nullptr, connectionType) + : QHttpNetworkConnection(connectionCount, hostName, port, encrypt, isLocalSocket, /*parent=*/nullptr, connectionType) { setExpires(true); setShareable(true); @@ -244,7 +246,9 @@ void QHttpThreadDelegate::startRequest() // check if we have an open connection to this host QUrl urlCopy = httpRequest.url(); - urlCopy.setPort(urlCopy.port(ssl ? 443 : 80)); + const bool isLocalSocket = urlCopy.scheme().startsWith("unix"_L1); + if (!isLocalSocket) + urlCopy.setPort(urlCopy.port(ssl ? 443 : 80)); QHttpNetworkConnection::ConnectionType connectionType = httpRequest.isHTTP2Allowed() ? QHttpNetworkConnection::ConnectionTypeHTTP2 @@ -279,7 +283,10 @@ void QHttpThreadDelegate::startRequest() } else #endif // QT_CONFIG(ssl) { - urlCopy.setScheme(QStringLiteral("h2")); + if (isLocalSocket) + urlCopy.setScheme(QStringLiteral("unix+h2")); + else + urlCopy.setScheme(QStringLiteral("h2")); } } @@ -297,8 +304,9 @@ void QHttpThreadDelegate::startRequest() if (!httpConnection) { // no entry in cache; create an object // the http object is actually a QHttpNetworkConnection - httpConnection = new QNetworkAccessCachedHttpConnection(http1Parameters.numberOfConnectionsPerHost(), urlCopy.host(), urlCopy.port(), ssl, - connectionType); + httpConnection = new QNetworkAccessCachedHttpConnection( + http1Parameters.numberOfConnectionsPerHost(), urlCopy.host(), urlCopy.port(), ssl, + isLocalSocket, connectionType); if (connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2 || connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) { httpConnection->setHttp2Parameters(http2Parameters); diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp index 7ef062a54d7..ae99721758a 100644 --- a/src/network/access/qnetworkaccessmanager.cpp +++ b/src/network/access/qnetworkaccessmanager.cpp @@ -1220,6 +1220,13 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera bool isLocalFile = req.url().isLocalFile(); QString scheme = req.url().scheme(); + // Remap local+http to unix+http to make further processing easier + if (scheme == "local+http"_L1) { + scheme = u"unix+http"_s; + QUrl url = req.url(); + url.setScheme(scheme); + req.setUrl(url); + } // fast path for GET on file:// URLs // The QNetworkAccessFileBackend will right now only be used for PUT @@ -1296,11 +1303,15 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera u"https", u"preconnect-https", #endif + u"unix+http", }; // Since Qt 5 we use the new QNetworkReplyHttpImpl if (std::find(std::begin(httpSchemes), std::end(httpSchemes), scheme) != std::end(httpSchemes)) { + #ifndef QT_NO_SSL - if (isStrictTransportSecurityEnabled() && d->stsCache.isKnownHost(request.url())) { + const bool isLocalSocket = scheme.startsWith("unix"_L1); + if (!isLocalSocket && isStrictTransportSecurityEnabled() + && d->stsCache.isKnownHost(request.url())) { QUrl stsUrl(request.url()); // RFC6797, 8.3: // The UA MUST replace the URI scheme with "https" [RFC2818], @@ -1391,6 +1402,8 @@ QStringList QNetworkAccessManager::supportedSchemesImplementation() const // Those ones don't exist in backends #if QT_CONFIG(http) schemes << QStringLiteral("http"); + schemes << QStringLiteral("unix+http"); + schemes << QStringLiteral("local+http"); #ifndef QT_NO_SSL if (QSslSocket::supportsSsl()) schemes << QStringLiteral("https"); diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp index 3e1fe761ee8..b4aca940a70 100644 --- a/src/network/access/qnetworkreplyhttpimpl.cpp +++ b/src/network/access/qnetworkreplyhttpimpl.cpp @@ -1228,7 +1228,8 @@ void QNetworkReplyHttpImplPrivate::onRedirected(const QUrl &redirectUrl, int htt if (httpRequest.isFollowRedirects()) // update the reply's url as it could've changed url = redirectUrl; - if (managerPrivate->stsEnabled && managerPrivate->stsCache.isKnownHost(url)) { + const bool wasLocalSocket = schemeBefore.startsWith("unix"_L1); + if (!wasLocalSocket && managerPrivate->stsEnabled && managerPrivate->stsCache.isKnownHost(url)) { // RFC6797, 8.3: // The UA MUST replace the URI scheme with "https" [RFC2818], // and if the URI contains an explicit port component of "80", @@ -1242,9 +1243,12 @@ void QNetworkReplyHttpImplPrivate::onRedirected(const QUrl &redirectUrl, int htt url.setPort(443); } - const bool isLessSafe = schemeBefore == "https"_L1 && url.scheme() == "http"_L1; - if (httpRequest.redirectPolicy() == QNetworkRequest::NoLessSafeRedirectPolicy - && isLessSafe) { + // Just to be on the safe side for local sockets, any changes to the scheme + // are considered less safe + const bool changingLocalScheme = wasLocalSocket && url.scheme() != schemeBefore; + const bool isLessSafe = changingLocalScheme + || (schemeBefore == "https"_L1 && url.scheme() == "http"_L1); + if (httpRequest.redirectPolicy() == QNetworkRequest::NoLessSafeRedirectPolicy && isLessSafe) { error(QNetworkReply::InsecureRedirectError, QCoreApplication::translate("QHttp", "Insecure redirect")); return;