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(
|
QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(
|
||||||
quint16 connectionCount, const QString &hostName, quint16 port, bool encrypt,
|
quint16 connectionCount, const QString &hostName, quint16 port, bool encrypt,
|
||||||
QHttpNetworkConnection::ConnectionType type)
|
bool isLocalSocket, QHttpNetworkConnection::ConnectionType type)
|
||||||
: hostName(hostName),
|
: hostName(hostName),
|
||||||
port(port),
|
port(port),
|
||||||
encrypt(encrypt),
|
encrypt(encrypt),
|
||||||
|
isLocalSocket(isLocalSocket),
|
||||||
activeChannelCount(getPreferredActiveChannelCount(type, connectionCount)),
|
activeChannelCount(getPreferredActiveChannelCount(type, connectionCount)),
|
||||||
channelCount(connectionCount),
|
channelCount(connectionCount),
|
||||||
channels(new QHttpNetworkConnectionChannel[channelCount]),
|
channels(new QHttpNetworkConnectionChannel[channelCount]),
|
||||||
@ -64,6 +65,8 @@ QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(
|
|||||||
#endif
|
#endif
|
||||||
connectionType(type)
|
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
|
// We allocate all 6 channels even if it's an HTTP/2-enabled
|
||||||
// connection: in case the protocol negotiation via NPN/ALPN fails,
|
// connection: in case the protocol negotiation via NPN/ALPN fails,
|
||||||
// we will have normally working HTTP/1.1.
|
// we will have normally working HTTP/1.1.
|
||||||
@ -533,7 +536,9 @@ QHttpNetworkConnectionPrivate::parseRedirectResponse(QHttpNetworkReply *reply)
|
|||||||
|
|
||||||
// Check redirect url protocol
|
// Check redirect url protocol
|
||||||
const QUrl priorUrl(reply->request().url());
|
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()) {
|
switch (reply->request().redirectPolicy()) {
|
||||||
case QNetworkRequest::NoLessSafeRedirectPolicy:
|
case QNetworkRequest::NoLessSafeRedirectPolicy:
|
||||||
// Here we could handle https->http redirects as InsecureProtocolError.
|
// Here we could handle https->http redirects as InsecureProtocolError.
|
||||||
@ -544,7 +549,7 @@ QHttpNetworkConnectionPrivate::parseRedirectResponse(QHttpNetworkReply *reply)
|
|||||||
break;
|
break;
|
||||||
case QNetworkRequest::SameOriginRedirectPolicy:
|
case QNetworkRequest::SameOriginRedirectPolicy:
|
||||||
if (priorUrl.host() != redirectUrl.host()
|
if (priorUrl.host() != redirectUrl.host()
|
||||||
|| priorUrl.scheme() != redirectUrl.scheme()
|
|| priorUrl.scheme() != targetUrlScheme
|
||||||
|| priorUrl.port() != redirectUrl.port()) {
|
|| priorUrl.port() != redirectUrl.port()) {
|
||||||
return {{}, QNetworkReply::InsecureRedirectError};
|
return {{}, QNetworkReply::InsecureRedirectError};
|
||||||
}
|
}
|
||||||
@ -1346,9 +1351,9 @@ void QHttpNetworkConnectionPrivate::_q_connectDelayedChannel()
|
|||||||
}
|
}
|
||||||
|
|
||||||
QHttpNetworkConnection::QHttpNetworkConnection(quint16 connectionCount, const QString &hostName,
|
QHttpNetworkConnection::QHttpNetworkConnection(quint16 connectionCount, const QString &hostName,
|
||||||
quint16 port, bool encrypt, QObject *parent,
|
quint16 port, bool encrypt, bool isLocalSocket, QObject *parent,
|
||||||
QHttpNetworkConnection::ConnectionType connectionType)
|
QHttpNetworkConnection::ConnectionType connectionType)
|
||||||
: QObject(*(new QHttpNetworkConnectionPrivate(connectionCount, hostName, port, encrypt,
|
: QObject(*(new QHttpNetworkConnectionPrivate(connectionCount, hostName, port, encrypt, isLocalSocket,
|
||||||
connectionType)), parent)
|
connectionType)), parent)
|
||||||
{
|
{
|
||||||
Q_D(QHttpNetworkConnection);
|
Q_D(QHttpNetworkConnection);
|
||||||
|
@ -64,7 +64,8 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
QHttpNetworkConnection(quint16 channelCount, const QString &hostName, quint16 port = 80,
|
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);
|
ConnectionType connectionType = ConnectionTypeHTTP);
|
||||||
~QHttpNetworkConnection();
|
~QHttpNetworkConnection();
|
||||||
|
|
||||||
@ -155,7 +156,8 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
QHttpNetworkConnectionPrivate(quint16 connectionCount, const QString &hostName, quint16 port,
|
QHttpNetworkConnectionPrivate(quint16 connectionCount, const QString &hostName, quint16 port,
|
||||||
bool encrypt, QHttpNetworkConnection::ConnectionType type);
|
bool encrypt, bool isLocalSocket,
|
||||||
|
QHttpNetworkConnection::ConnectionType type);
|
||||||
~QHttpNetworkConnectionPrivate();
|
~QHttpNetworkConnectionPrivate();
|
||||||
void init();
|
void init();
|
||||||
|
|
||||||
@ -205,6 +207,7 @@ public:
|
|||||||
QString hostName;
|
QString hostName;
|
||||||
quint16 port;
|
quint16 port;
|
||||||
bool encrypt;
|
bool encrypt;
|
||||||
|
bool isLocalSocket;
|
||||||
bool delayIpv4 = true;
|
bool delayIpv4 = true;
|
||||||
|
|
||||||
// Number of channels we are trying to use at the moment:
|
// Number of channels we are trying to use at the moment:
|
||||||
|
@ -79,6 +79,8 @@ void QHttpNetworkConnectionChannel::init()
|
|||||||
#ifndef QT_NO_SSL
|
#ifndef QT_NO_SSL
|
||||||
if (connection->d_func()->encrypt)
|
if (connection->d_func()->encrypt)
|
||||||
socket = new QSslSocket;
|
socket = new QSslSocket;
|
||||||
|
else if (connection->d_func()->isLocalSocket)
|
||||||
|
socket = new QLocalSocket;
|
||||||
else
|
else
|
||||||
socket = new QTcpSocket;
|
socket = new QTcpSocket;
|
||||||
#else
|
#else
|
||||||
|
@ -95,7 +95,9 @@ static QByteArray makeCacheKey(QUrl &url, QNetworkProxy *proxy, const QString &p
|
|||||||
QUrl copy = url;
|
QUrl copy = url;
|
||||||
QString scheme = copy.scheme();
|
QString scheme = copy.scheme();
|
||||||
bool isEncrypted = scheme == "https"_L1 || scheme == "preconnect-https"_L1;
|
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)
|
if (scheme == "preconnect-http"_L1)
|
||||||
copy.setScheme("http"_L1);
|
copy.setScheme("http"_L1);
|
||||||
else if (scheme == "preconnect-https"_L1)
|
else if (scheme == "preconnect-https"_L1)
|
||||||
@ -145,9 +147,9 @@ class QNetworkAccessCachedHttpConnection: public QHttpNetworkConnection,
|
|||||||
{
|
{
|
||||||
// Q_OBJECT
|
// Q_OBJECT
|
||||||
public:
|
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::ConnectionType connectionType)
|
||||||
: QHttpNetworkConnection(connectionCount, hostName, port, encrypt, /*parent=*/nullptr, connectionType)
|
: QHttpNetworkConnection(connectionCount, hostName, port, encrypt, isLocalSocket, /*parent=*/nullptr, connectionType)
|
||||||
{
|
{
|
||||||
setExpires(true);
|
setExpires(true);
|
||||||
setShareable(true);
|
setShareable(true);
|
||||||
@ -244,7 +246,9 @@ void QHttpThreadDelegate::startRequest()
|
|||||||
|
|
||||||
// check if we have an open connection to this host
|
// check if we have an open connection to this host
|
||||||
QUrl urlCopy = httpRequest.url();
|
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
|
QHttpNetworkConnection::ConnectionType connectionType
|
||||||
= httpRequest.isHTTP2Allowed() ? QHttpNetworkConnection::ConnectionTypeHTTP2
|
= httpRequest.isHTTP2Allowed() ? QHttpNetworkConnection::ConnectionTypeHTTP2
|
||||||
@ -279,7 +283,10 @@ void QHttpThreadDelegate::startRequest()
|
|||||||
} else
|
} else
|
||||||
#endif // QT_CONFIG(ssl)
|
#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) {
|
if (!httpConnection) {
|
||||||
// no entry in cache; create an object
|
// no entry in cache; create an object
|
||||||
// the http object is actually a QHttpNetworkConnection
|
// the http object is actually a QHttpNetworkConnection
|
||||||
httpConnection = new QNetworkAccessCachedHttpConnection(http1Parameters.numberOfConnectionsPerHost(), urlCopy.host(), urlCopy.port(), ssl,
|
httpConnection = new QNetworkAccessCachedHttpConnection(
|
||||||
connectionType);
|
http1Parameters.numberOfConnectionsPerHost(), urlCopy.host(), urlCopy.port(), ssl,
|
||||||
|
isLocalSocket, connectionType);
|
||||||
if (connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2
|
if (connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2
|
||||||
|| connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
|
|| connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
|
||||||
httpConnection->setHttp2Parameters(http2Parameters);
|
httpConnection->setHttp2Parameters(http2Parameters);
|
||||||
|
@ -1220,6 +1220,13 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
|
|||||||
bool isLocalFile = req.url().isLocalFile();
|
bool isLocalFile = req.url().isLocalFile();
|
||||||
QString scheme = req.url().scheme();
|
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
|
// fast path for GET on file:// URLs
|
||||||
// The QNetworkAccessFileBackend will right now only be used for PUT
|
// The QNetworkAccessFileBackend will right now only be used for PUT
|
||||||
@ -1296,11 +1303,15 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
|
|||||||
u"https",
|
u"https",
|
||||||
u"preconnect-https",
|
u"preconnect-https",
|
||||||
#endif
|
#endif
|
||||||
|
u"unix+http",
|
||||||
};
|
};
|
||||||
// Since Qt 5 we use the new QNetworkReplyHttpImpl
|
// Since Qt 5 we use the new QNetworkReplyHttpImpl
|
||||||
if (std::find(std::begin(httpSchemes), std::end(httpSchemes), scheme) != std::end(httpSchemes)) {
|
if (std::find(std::begin(httpSchemes), std::end(httpSchemes), scheme) != std::end(httpSchemes)) {
|
||||||
|
|
||||||
#ifndef QT_NO_SSL
|
#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());
|
QUrl stsUrl(request.url());
|
||||||
// RFC6797, 8.3:
|
// RFC6797, 8.3:
|
||||||
// The UA MUST replace the URI scheme with "https" [RFC2818],
|
// 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
|
// Those ones don't exist in backends
|
||||||
#if QT_CONFIG(http)
|
#if QT_CONFIG(http)
|
||||||
schemes << QStringLiteral("http");
|
schemes << QStringLiteral("http");
|
||||||
|
schemes << QStringLiteral("unix+http");
|
||||||
|
schemes << QStringLiteral("local+http");
|
||||||
#ifndef QT_NO_SSL
|
#ifndef QT_NO_SSL
|
||||||
if (QSslSocket::supportsSsl())
|
if (QSslSocket::supportsSsl())
|
||||||
schemes << QStringLiteral("https");
|
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
|
if (httpRequest.isFollowRedirects()) // update the reply's url as it could've changed
|
||||||
url = redirectUrl;
|
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:
|
// RFC6797, 8.3:
|
||||||
// The UA MUST replace the URI scheme with "https" [RFC2818],
|
// The UA MUST replace the URI scheme with "https" [RFC2818],
|
||||||
// and if the URI contains an explicit port component of "80",
|
// 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);
|
url.setPort(443);
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool isLessSafe = schemeBefore == "https"_L1 && url.scheme() == "http"_L1;
|
// Just to be on the safe side for local sockets, any changes to the scheme
|
||||||
if (httpRequest.redirectPolicy() == QNetworkRequest::NoLessSafeRedirectPolicy
|
// are considered less safe
|
||||||
&& isLessSafe) {
|
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,
|
error(QNetworkReply::InsecureRedirectError,
|
||||||
QCoreApplication::translate("QHttp", "Insecure redirect"));
|
QCoreApplication::translate("QHttp", "Insecure redirect"));
|
||||||
return;
|
return;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user