SSL NPN negotiation: Do not abort on unmatched protocols
... but choose HTTP/1.1 and continue connecting anyhow. According to the NPN spec, actually we should choose SPDY: "In the event that the client doesn't support any of server's protocols, or the server doesn't advertise any, it SHOULD select the first protocol that it supports." However, some tested servers did not advertise anything and did not support SPDY, so blindly trying the newest protocol would fail. We are conservative in that case and choose HTTP. Task-number: QTBUG-40714 Change-Id: Ia8aaf01fea74e13d9e4416306f85f1890b25559e Reviewed-by: Richard J. Moore <rich@kde.org>
This commit is contained in:
parent
239f67f158
commit
3c24dddaf9
@ -931,7 +931,8 @@ void QHttpNetworkConnectionChannel::_q_encrypted()
|
|||||||
|
|
||||||
if (!protocolHandler) {
|
if (!protocolHandler) {
|
||||||
switch (sslSocket->sslConfiguration().nextProtocolNegotiationStatus()) {
|
switch (sslSocket->sslConfiguration().nextProtocolNegotiationStatus()) {
|
||||||
case QSslConfiguration::NextProtocolNegotiationNegotiated: {
|
case QSslConfiguration::NextProtocolNegotiationNegotiated: /* fall through */
|
||||||
|
case QSslConfiguration::NextProtocolNegotiationUnsupported: {
|
||||||
QByteArray nextProtocol = sslSocket->sslConfiguration().nextNegotiatedProtocol();
|
QByteArray nextProtocol = sslSocket->sslConfiguration().nextNegotiatedProtocol();
|
||||||
if (nextProtocol == QSslConfiguration::NextProtocolHttp1_1) {
|
if (nextProtocol == QSslConfiguration::NextProtocolHttp1_1) {
|
||||||
// fall through to create a QHttpProtocolHandler
|
// fall through to create a QHttpProtocolHandler
|
||||||
@ -953,10 +954,6 @@ void QHttpNetworkConnectionChannel::_q_encrypted()
|
|||||||
// re-queue requests from SPDY queue to HTTP queue, if any
|
// re-queue requests from SPDY queue to HTTP queue, if any
|
||||||
requeueSpdyRequests();
|
requeueSpdyRequests();
|
||||||
break;
|
break;
|
||||||
case QSslConfiguration::NextProtocolNegotiationUnsupported:
|
|
||||||
emitFinishedWithError(QNetworkReply::SslHandshakeFailedError,
|
|
||||||
"chosen Next Protocol Negotiation value unsupported");
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
emitFinishedWithError(QNetworkReply::SslHandshakeFailedError,
|
emitFinishedWithError(QNetworkReply::SslHandshakeFailedError,
|
||||||
"detected unknown Next Protocol Negotiation protocol");
|
"detected unknown Next Protocol Negotiation protocol");
|
||||||
|
@ -1518,14 +1518,20 @@ void QSslSocketBackendPrivate::continueHandshake()
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG)
|
#if OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG)
|
||||||
const unsigned char *proto = 0;
|
|
||||||
unsigned int proto_len = 0;
|
|
||||||
q_SSL_get0_next_proto_negotiated(ssl, &proto, &proto_len);
|
|
||||||
if (proto_len)
|
|
||||||
configuration.nextNegotiatedProtocol = QByteArray(reinterpret_cast<const char *>(proto), proto_len);
|
|
||||||
else
|
|
||||||
configuration.nextNegotiatedProtocol.clear();
|
|
||||||
configuration.nextProtocolNegotiationStatus = sslContextPointer->npnContext().status;
|
configuration.nextProtocolNegotiationStatus = sslContextPointer->npnContext().status;
|
||||||
|
if (sslContextPointer->npnContext().status == QSslConfiguration::NextProtocolNegotiationUnsupported) {
|
||||||
|
// we could not agree -> be conservative and use HTTP/1.1
|
||||||
|
configuration.nextNegotiatedProtocol = QByteArrayLiteral("http/1.1");
|
||||||
|
} else {
|
||||||
|
const unsigned char *proto = 0;
|
||||||
|
unsigned int proto_len = 0;
|
||||||
|
q_SSL_get0_next_proto_negotiated(ssl, &proto, &proto_len);
|
||||||
|
if (proto_len)
|
||||||
|
configuration.nextNegotiatedProtocol = QByteArray(reinterpret_cast<const char *>(proto), proto_len);
|
||||||
|
else
|
||||||
|
configuration.nextNegotiatedProtocol.clear();
|
||||||
|
}
|
||||||
#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ...
|
#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ...
|
||||||
|
|
||||||
connectionEncrypted = true;
|
connectionEncrypted = true;
|
||||||
|
@ -69,6 +69,7 @@ private slots:
|
|||||||
void proxyAuthentication_data();
|
void proxyAuthentication_data();
|
||||||
void proxyAuthentication();
|
void proxyAuthentication();
|
||||||
void authentication();
|
void authentication();
|
||||||
|
void npnWithEmptyList(); // QTBUG-40714
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
void spdyReplyFinished(); // only used by spdyMultipleRequestsPerHost test
|
void spdyReplyFinished(); // only used by spdyMultipleRequestsPerHost test
|
||||||
@ -577,6 +578,34 @@ void tst_qnetworkreply::authentication()
|
|||||||
QVERIFY(statusCode >= 200 && statusCode < 400);
|
QVERIFY(statusCode >= 200 && statusCode < 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tst_qnetworkreply::npnWithEmptyList() // QTBUG-40714
|
||||||
|
{
|
||||||
|
#if defined(QT_BUILD_INTERNAL) && !defined(QT_NO_SSL) && OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG)
|
||||||
|
|
||||||
|
// The server does not send a list of Next Protocols, so we test
|
||||||
|
// that we continue anyhow and it works
|
||||||
|
|
||||||
|
m_manager.clearAccessCache();
|
||||||
|
|
||||||
|
QUrl url(QStringLiteral("https://www.ossifrage.net/"));
|
||||||
|
QNetworkRequest request(url);
|
||||||
|
request.setAttribute(QNetworkRequest::SpdyAllowedAttribute, QVariant(true));
|
||||||
|
QNetworkReply *reply = m_manager.get(request);
|
||||||
|
QObject::connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
|
||||||
|
|
||||||
|
QTestEventLoop::instance().enterLoop(15);
|
||||||
|
QVERIFY(!QTestEventLoop::instance().timeout());
|
||||||
|
|
||||||
|
int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||||
|
QVERIFY(statusCode == 200);
|
||||||
|
|
||||||
|
QCOMPARE(reply->sslConfiguration().nextProtocolNegotiationStatus(),
|
||||||
|
QSslConfiguration::NextProtocolNegotiationUnsupported);
|
||||||
|
#else
|
||||||
|
QSKIP("Qt built withouth OpenSSL, or the OpenSSL version is too old");
|
||||||
|
#endif // defined(QT_BUILD_INTERNAL) && !defined(QT_NO_SSL) ...
|
||||||
|
}
|
||||||
|
|
||||||
QTEST_MAIN(tst_qnetworkreply)
|
QTEST_MAIN(tst_qnetworkreply)
|
||||||
|
|
||||||
#include "main.moc"
|
#include "main.moc"
|
||||||
|
Loading…
Reference in New Issue
Block a user