Http: Support unix+http: scheme in http backend
[ChangeLog][QtNetwork][QNetworkAccessManager] QNetworkAccessManager now supports local connections using the uri schemes unix+http: or local+http:. Fixes: QTBUG-102855 Change-Id: I1f47b74ab42b51d97b3c555cc3afd6ccd272e1ed Reviewed-by: Mate Barany <mate.barany@qt.io>
This commit is contained in:
parent
956795bda8
commit
cb8e5e0dd9
@ -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);
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user