QNetworkReply: Propagate proxy errors properly

Map QNetworkError::HostNotFoundError and QNetworkError::ConnectionRefusedError
 to ProxyNotFoundError resp. ProxyConnectionRefusedError when it originated
from the communication with the proxy server.

Fixes: QTBUG-68821
Change-Id: I21b91f2667ba0cd329d4ece1fe543472cdab2d22
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
(cherry picked from commit 69de333e1fbb3b866ab77a9d6a4ad26d0796633b)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Michael Klein 2023-06-22 17:14:01 +02:00 committed by Qt Cherry-pick Bot
parent 972984521e
commit 91132d6b27
3 changed files with 62 additions and 3 deletions

View File

@ -1265,8 +1265,16 @@ void QHttpNetworkConnectionPrivate::_q_hostLookupFinished(const QHostInfo &info)
networkLayerState = QHttpNetworkConnectionPrivate::IPv6;
QMetaObject::invokeMethod(this->q_func(), "_q_startNextRequest", Qt::QueuedConnection);
} else {
auto lookupError = QNetworkReply::HostNotFoundError;
#ifndef QT_NO_NETWORKPROXY
// if the proxy can lookup hostnames, all hostname lookups except for the lookup of the
// proxy hostname are delegated to the proxy.
auto proxyCapabilities = networkProxy.capabilities() | channels[0].proxy.capabilities();
if (proxyCapabilities & QNetworkProxy::HostNameLookupCapability)
lookupError = QNetworkReply::ProxyNotFoundError;
#endif
if (dequeueRequest(channels[0].socket)) {
emitReplyError(channels[0].socket, channels[0].reply, QNetworkReply::HostNotFoundError);
emitReplyError(channels[0].socket, channels[0].reply, lookupError);
networkLayerState = QHttpNetworkConnectionPrivate::Unknown;
} else if (connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2
|| connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
@ -1274,7 +1282,7 @@ void QHttpNetworkConnectionPrivate::_q_hostLookupFinished(const QHostInfo &info)
// emit error for all replies
QHttpNetworkReply *currentReply = h2Pair.second;
Q_ASSERT(currentReply);
emitReplyError(channels[0].socket, currentReply, QNetworkReply::HostNotFoundError);
emitReplyError(channels[0].socket, currentReply, lookupError);
}
} else {
// We can end up here if a request has been aborted or otherwise failed (e.g. timeout)

View File

@ -949,6 +949,10 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
break;
case QAbstractSocket::ConnectionRefusedError:
errorCode = QNetworkReply::ConnectionRefusedError;
#ifndef QT_NO_NETWORKPROXY
if (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy && !ssl)
errorCode = QNetworkReply::ProxyConnectionRefusedError;
#endif
break;
case QAbstractSocket::RemoteHostClosedError:
// This error for SSL comes twice in a row, first from SSL layer ("The TLS/SSL connection has been closed") then from TCP layer.
@ -1030,6 +1034,9 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
}
errorCode = QNetworkReply::TimeoutError;
break;
case QAbstractSocket::ProxyConnectionRefusedError:
errorCode = QNetworkReply::ProxyConnectionRefusedError;
break;
case QAbstractSocket::ProxyAuthenticationRequiredError:
errorCode = QNetworkReply::ProxyAuthenticationRequiredError;
break;

View File

@ -526,6 +526,9 @@ private Q_SLOTS:
void notFoundWithCompression_data();
void notFoundWithCompression();
void qtbug68821proxyError_data();
void qtbug68821proxyError();
// NOTE: This test must be last!
void parentingRepliesToTheApp();
private:
@ -3852,7 +3855,6 @@ void tst_QNetworkReply::ioGetFromHttpWithSocksProxy()
QVERIFY(reader.data.isEmpty());
QVERIFY(int(reply->error()) > 0);
QEXPECT_FAIL("", "QTcpSocket doesn't return enough information yet", Continue);
QCOMPARE(int(reply->error()), int(QNetworkReply::ProxyConnectionRefusedError));
QCOMPARE(authspy.size(), 0);
@ -10094,6 +10096,48 @@ void tst_QNetworkReply::notFoundWithCompression()
QCOMPARE(reply->readAll(), expected);
}
void tst_QNetworkReply::qtbug68821proxyError_data()
{
QTest::addColumn<QString>("proxyHost");
QTest::addColumn<QString>("scheme");
QTest::addColumn<QNetworkReply::NetworkError>("error");
QTest::newRow("invalidhost+http") << "this-host-will-never-exist.qt-project.org"
<< "http" << QNetworkReply::ProxyNotFoundError;
QTest::newRow("localhost+http") << "localhost"
<< "http" << QNetworkReply::ProxyConnectionRefusedError;
#ifndef QT_NO_SSL
QTest::newRow("invalidhost+https") << "this-host-will-never-exist.qt-project.org"
<< "https" << QNetworkReply::ProxyNotFoundError;
QTest::newRow("localhost+https") << "localhost"
<< "https" << QNetworkReply::ProxyConnectionRefusedError;
#endif
}
void tst_QNetworkReply::qtbug68821proxyError()
{
QTcpServer proxyServer;
QVERIFY(proxyServer.listen());
quint16 proxyPort = proxyServer.serverPort();
proxyServer.close();
QFETCH(QString, proxyHost);
QNetworkProxy proxy(QNetworkProxy::HttpProxy, proxyHost, proxyPort);
manager.setProxy(proxy);
QFETCH(QString, scheme);
QNetworkReply *reply = manager.get(QNetworkRequest(QUrl(scheme + "://example.com")));
QSignalSpy spy(reply, &QNetworkReply::errorOccurred);
QVERIFY(spy.isValid());
QVERIFY(spy.wait(15000));
QFETCH(QNetworkReply::NetworkError, error);
QCOMPARE(spy.count(), 1);
QCOMPARE(spy.at(0).at(0), error);
}
// NOTE: This test must be last testcase in tst_qnetworkreply!
void tst_QNetworkReply::parentingRepliesToTheApp()
{