diff options
Diffstat (limited to 'src/network/access/qhttpnetworkconnectionchannel.cpp')
-rw-r--r-- | src/network/access/qhttpnetworkconnectionchannel.cpp | 192 |
1 files changed, 87 insertions, 105 deletions
diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp index 22b1cd0ec3..71b880f6f8 100644 --- a/src/network/access/qhttpnetworkconnectionchannel.cpp +++ b/src/network/access/qhttpnetworkconnectionchannel.cpp @@ -1,49 +1,12 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Copyright (C) 2014 BlackBerry Limited. All rights reserved. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtNetwork module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// Copyright (C) 2014 BlackBerry Limited. All rights reserved. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qhttpnetworkconnectionchannel_p.h" #include "qhttpnetworkconnection_p.h" #include "qhttp2configuration.h" #include "private/qnoncontiguousbytedevice_p.h" -#include <qpair.h> #include <qdebug.h> #include <private/qhttp2protocolhandler_p.h> @@ -58,6 +21,9 @@ #include "private/qnetconmonitor_p.h" +#include <memory> +#include <utility> + QT_BEGIN_NAMESPACE namespace @@ -74,7 +40,7 @@ private: } -// TODO: Put channel specific stuff here so it does not polute qhttpnetworkconnection.cpp +// TODO: Put channel specific stuff here so it does not pollute qhttpnetworkconnection.cpp // Because in-flight when sending a request, the server might close our connection (because the persistent HTTP // connection times out) @@ -125,14 +91,14 @@ void QHttpNetworkConnectionChannel::init() // After some back and forth in all the last years, this is now a DirectConnection because otherwise // the state inside the *Socket classes gets messed up, also in conjunction with the socket notifiers // which behave slightly differently on Windows vs Linux - QObject::connect(socket, SIGNAL(bytesWritten(qint64)), - this, SLOT(_q_bytesWritten(qint64)), + QObject::connect(socket, &QIODevice::bytesWritten, + this, &QHttpNetworkConnectionChannel::_q_bytesWritten, Qt::DirectConnection); - QObject::connect(socket, SIGNAL(connected()), - this, SLOT(_q_connected()), + QObject::connect(socket, &QAbstractSocket::connected, + this, &QHttpNetworkConnectionChannel::_q_connected, Qt::DirectConnection); - QObject::connect(socket, SIGNAL(readyRead()), - this, SLOT(_q_readyRead()), + QObject::connect(socket, &QIODevice::readyRead, + this, &QHttpNetworkConnectionChannel::_q_readyRead, Qt::DirectConnection); // The disconnected() and error() signals may already come @@ -142,17 +108,17 @@ void QHttpNetworkConnectionChannel::init() // but cannot be caught because the user did not have a chance yet // to connect to QNetworkReply's signals. qRegisterMetaType<QAbstractSocket::SocketError>(); - QObject::connect(socket, SIGNAL(disconnected()), - this, SLOT(_q_disconnected()), + QObject::connect(socket, &QAbstractSocket::disconnected, + this, &QHttpNetworkConnectionChannel::_q_disconnected, Qt::DirectConnection); - QObject::connect(socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), - this, SLOT(_q_error(QAbstractSocket::SocketError)), + QObject::connect(socket, &QAbstractSocket::errorOccurred, + this, &QHttpNetworkConnectionChannel::_q_error, Qt::DirectConnection); #ifndef QT_NO_NETWORKPROXY - QObject::connect(socket, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), - this, SLOT(_q_proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), + QObject::connect(socket, &QAbstractSocket::proxyAuthenticationRequired, + this, &QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired, Qt::DirectConnection); #endif @@ -160,17 +126,17 @@ void QHttpNetworkConnectionChannel::init() QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket); if (sslSocket) { // won't be a sslSocket if encrypt is false - QObject::connect(sslSocket, SIGNAL(encrypted()), - this, SLOT(_q_encrypted()), + QObject::connect(sslSocket, &QSslSocket::encrypted, + this, &QHttpNetworkConnectionChannel::_q_encrypted, Qt::DirectConnection); - QObject::connect(sslSocket, SIGNAL(sslErrors(QList<QSslError>)), - this, SLOT(_q_sslErrors(QList<QSslError>)), + QObject::connect(sslSocket, &QSslSocket::sslErrors, + this, &QHttpNetworkConnectionChannel::_q_sslErrors, Qt::DirectConnection); - QObject::connect(sslSocket, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)), - this, SLOT(_q_preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)), + QObject::connect(sslSocket, &QSslSocket::preSharedKeyAuthenticationRequired, + this, &QHttpNetworkConnectionChannel::_q_preSharedKeyAuthenticationRequired, Qt::DirectConnection); - QObject::connect(sslSocket, SIGNAL(encryptedBytesWritten(qint64)), - this, SLOT(_q_encryptedBytesWritten(qint64)), + QObject::connect(sslSocket, &QSslSocket::encryptedBytesWritten, + this, &QHttpNetworkConnectionChannel::_q_encryptedBytesWritten, Qt::DirectConnection); if (ignoreAllSslErrors) @@ -242,7 +208,7 @@ void QHttpNetworkConnectionChannel::abort() bool QHttpNetworkConnectionChannel::sendRequest() { - Q_ASSERT(!protocolHandler.isNull()); + Q_ASSERT(protocolHandler); return protocolHandler->sendRequest(); } @@ -255,7 +221,7 @@ bool QHttpNetworkConnectionChannel::sendRequest() void QHttpNetworkConnectionChannel::sendRequestDelayed() { QMetaObject::invokeMethod(this, [this] { - Q_ASSERT(!protocolHandler.isNull()); + Q_ASSERT(protocolHandler); if (reply) protocolHandler->sendRequest(); }, Qt::ConnectionType::QueuedConnection); @@ -263,13 +229,13 @@ void QHttpNetworkConnectionChannel::sendRequestDelayed() void QHttpNetworkConnectionChannel::_q_receiveReply() { - Q_ASSERT(!protocolHandler.isNull()); + Q_ASSERT(protocolHandler); protocolHandler->_q_receiveReply(); } void QHttpNetworkConnectionChannel::_q_readyRead() { - Q_ASSERT(!protocolHandler.isNull()); + Q_ASSERT(protocolHandler); protocolHandler->_q_readyRead(); } @@ -356,6 +322,13 @@ bool QHttpNetworkConnectionChannel::ensureConnection() QString connectHost = connection->d_func()->hostName; quint16 connectPort = connection->d_func()->port; + QHttpNetworkReply *potentialReply = connection->d_func()->predictNextRequestsReply(); + if (potentialReply) { + QMetaObject::invokeMethod(potentialReply, "socketStartedConnecting", Qt::QueuedConnection); + } else if (!h2RequestsToSend.isEmpty()) { + QMetaObject::invokeMethod(std::as_const(h2RequestsToSend).first().second, "socketStartedConnecting", Qt::QueuedConnection); + } + #ifndef QT_NO_NETWORKPROXY // HTTPS always use transparent proxy. if (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy && !ssl) { @@ -371,8 +344,8 @@ bool QHttpNetworkConnectionChannel::ensureConnection() if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct || (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2 - && h2RequestsToSend.count() > 0)) { - value = h2RequestsToSend.first().first.headerField("user-agent"); + && !h2RequestsToSend.isEmpty())) { + value = std::as_const(h2RequestsToSend).first().first.headerField("user-agent"); } else { value = connection->d_func()->predictNextRequest().headerField("user-agent"); } @@ -393,8 +366,8 @@ bool QHttpNetworkConnectionChannel::ensureConnection() // check whether we can re-use an existing SSL session // (meaning another socket in this connection has already // performed a full handshake) - if (!connection->sslContext().isNull()) - QSslSocketPrivate::checkSettingSslContext(sslSocket, connection->sslContext()); + if (auto ctx = connection->sslContext()) + QSslSocketPrivate::checkSettingSslContext(sslSocket, std::move(ctx)); sslSocket->setPeerVerifyName(connection->d_func()->peerVerifyName); sslSocket->connectToHostEncrypted(connectHost, connectPort, QIODevice::ReadWrite, networkLayerPreference); @@ -471,18 +444,18 @@ void QHttpNetworkConnectionChannel::allDone() // trick with ProtocolHandlerDeleter, a QObject-derived class. // These dances below just make it somewhat exception-safe. // 1. Create a new owner: - QAbstractProtocolHandler *oldHandler = protocolHandler.data(); - QScopedPointer<ProtocolHandlerDeleter> deleter(new ProtocolHandlerDeleter(oldHandler)); + QAbstractProtocolHandler *oldHandler = protocolHandler.get(); + auto deleter = std::make_unique<ProtocolHandlerDeleter>(oldHandler); // 2. Retire the old one: - protocolHandler.take(); + Q_UNUSED(protocolHandler.release()); // 3. Call 'deleteLater': deleter->deleteLater(); // 3. Give up the ownerthip: - deleter.take(); + Q_UNUSED(deleter.release()); connection->fillHttp2Queue(); protocolHandler.reset(new QHttp2ProtocolHandler(this)); - QHttp2ProtocolHandler *h2c = static_cast<QHttp2ProtocolHandler *>(protocolHandler.data()); + QHttp2ProtocolHandler *h2c = static_cast<QHttp2ProtocolHandler *>(protocolHandler.get()); QMetaObject::invokeMethod(h2c, "_q_receiveReply", Qt::QueuedConnection); QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); // If we only had one request sent with H2 allowed, we may fail to send @@ -577,7 +550,7 @@ void QHttpNetworkConnectionChannel::detectPipeliningSupport() QByteArray serverHeaderField; if ( // check for HTTP/1.1 - (reply->d_func()->majorVersion == 1 && reply->d_func()->minorVersion == 1) + (reply->majorVersion() == 1 && reply->minorVersion() == 1) // check for not having connection close && (!reply->d_func()->isConnectionCloseEnabled()) // check if it is still connected @@ -600,7 +573,7 @@ void QHttpNetworkConnectionChannel::detectPipeliningSupport() // called when the connection broke and we need to queue some pipelined requests again void QHttpNetworkConnectionChannel::requeueCurrentlyPipelinedRequests() { - for (int i = 0; i < alreadyPipelinedRequests.length(); i++) + for (int i = 0; i < alreadyPipelinedRequests.size(); i++) connection->d_func()->requeueRequest(alreadyPipelinedRequests.at(i)); alreadyPipelinedRequests.clear(); @@ -856,7 +829,7 @@ void QHttpNetworkConnectionChannel::_q_disconnected() QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); } state = QHttpNetworkConnectionChannel::IdleState; - if (alreadyPipelinedRequests.length()) { + if (alreadyPipelinedRequests.size()) { // If nothing was in a pipeline, no need in calling // _q_startNextRequest (which it does): requeueCurrentlyPipelinedRequests(); @@ -883,6 +856,8 @@ void QHttpNetworkConnectionChannel::_q_connected() connection->d_func()->networkLayerState = QHttpNetworkConnectionPrivate::IPv6; } connection->d_func()->networkLayerDetected(networkLayerPreference); + if (connection->d_func()->activeChannelCount > 1 && !connection->d_func()->encrypt) + QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); } else { bool anyProtocol = networkLayerPreference == QAbstractSocket::AnyIPProtocol; if (((connection->d_func()->networkLayerState == QHttpNetworkConnectionPrivate::IPv4) @@ -925,18 +900,17 @@ void QHttpNetworkConnectionChannel::_q_connected() //channels[i].reconnectAttempts = 2; if (ssl || pendingEncrypt) { // FIXME: Didn't work properly with pendingEncrypt only, we should refactor this into an EncrypingState #ifndef QT_NO_SSL - if (connection->sslContext().isNull()) { + if (!connection->sslContext()) { // this socket is making the 1st handshake for this connection, // we need to set the SSL context so new sockets can reuse it - QSharedPointer<QSslContext> socketSslContext = QSslSocketPrivate::sslContext(static_cast<QSslSocket*>(socket)); - if (!socketSslContext.isNull()) - connection->setSslContext(socketSslContext); + if (auto socketSslContext = QSslSocketPrivate::sslContext(static_cast<QSslSocket*>(socket))) + connection->setSslContext(std::move(socketSslContext)); } #endif } else if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) { state = QHttpNetworkConnectionChannel::IdleState; protocolHandler.reset(new QHttp2ProtocolHandler(this)); - if (h2RequestsToSend.count() > 0) { + if (h2RequestsToSend.size() > 0) { // In case our peer has sent us its settings (window size, max concurrent streams etc.) // let's give _q_receiveReply a chance to read them first ('invokeMethod', QueuedConnection). QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); @@ -977,6 +951,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. @@ -989,11 +967,11 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket // we do not resend, but must report errors if any request is in progress (note, while // not in its sendRequest(), protocol handler switches the channel to IdleState, thus // this check is under this condition in 'if'): - if (protocolHandler.data()) { + if (protocolHandler) { if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct || (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2 && switchedToHttp2)) { - auto h2Handler = static_cast<QHttp2ProtocolHandler *>(protocolHandler.data()); + auto h2Handler = static_cast<QHttp2ProtocolHandler *>(protocolHandler.get()); h2Handler->handleConnectionClosure(); } } @@ -1058,6 +1036,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; @@ -1115,16 +1096,15 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2 || connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) { - QList<HttpMessagePair> h2Pairs = h2RequestsToSend.values(); - for (int a = 0; a < h2Pairs.count(); ++a) { + const auto h2RequestsToSendCopy = std::exchange(h2RequestsToSend, {}); + for (const auto &httpMessagePair : h2RequestsToSendCopy) { // emit error for all replies - QHttpNetworkReply *currentReply = h2Pairs.at(a).second; + QHttpNetworkReply *currentReply = httpMessagePair.second; currentReply->d_func()->errorString = errorString; currentReply->d_func()->httpErrorCode = errorCode; Q_ASSERT(currentReply); emit currentReply->finishedWithError(errorCode, errorString); } - h2RequestsToSend.clear(); } // send the next request @@ -1148,9 +1128,9 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket void QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator* auth) { if ((connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2 - && (switchedToHttp2 || h2RequestsToSend.count() > 0)) + && (switchedToHttp2 || h2RequestsToSend.size() > 0)) || connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) { - if (h2RequestsToSend.count() > 0) + if (h2RequestsToSend.size() > 0) connection->d_func()->emitProxyAuthenticationRequired(this, proxy, auth); } else { // HTTP // Need to dequeue the request before we can emit the error. @@ -1173,9 +1153,9 @@ void QHttpNetworkConnectionChannel::emitFinishedWithError(QNetworkReply::Network { if (reply) emit reply->finishedWithError(error, QHttpNetworkConnectionChannel::tr(message)); - QList<HttpMessagePair> h2Pairs = h2RequestsToSend.values(); - for (int a = 0; a < h2Pairs.count(); ++a) { - QHttpNetworkReply *currentReply = h2Pairs.at(a).second; + const auto h2RequestsToSendCopy = h2RequestsToSend; + for (const auto &httpMessagePair : h2RequestsToSendCopy) { + QHttpNetworkReply *currentReply = httpMessagePair.second; Q_ASSERT(currentReply); emit currentReply->finishedWithError(error, QHttpNetworkConnectionChannel::tr(message)); } @@ -1256,10 +1236,12 @@ void QHttpNetworkConnectionChannel::_q_encrypted() if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2 || connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) { - if (h2RequestsToSend.count() > 0) { + if (!h2RequestsToSend.isEmpty()) { + // Similar to HTTP/1.1 counterpart below: + const auto &pair = std::as_const(h2RequestsToSend).first(); + emit pair.second->encrypted(); // In case our peer has sent us its settings (window size, max concurrent streams etc.) // let's give _q_receiveReply a chance to read them first ('invokeMethod', QueuedConnection). - QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); } } else { // HTTP if (!reply) @@ -1272,14 +1254,14 @@ void QHttpNetworkConnectionChannel::_q_encrypted() if (reply) sendRequestDelayed(); } + QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); } void QHttpNetworkConnectionChannel::requeueHttp2Requests() { - QList<HttpMessagePair> h2Pairs = h2RequestsToSend.values(); - for (int a = 0; a < h2Pairs.count(); ++a) - connection->d_func()->requeueRequest(h2Pairs.at(a)); - h2RequestsToSend.clear(); + const auto h2RequestsToSendCopy = std::exchange(h2RequestsToSend, {}); + for (const auto &httpMessagePair : h2RequestsToSendCopy) + connection->d_func()->requeueRequest(httpMessagePair); } void QHttpNetworkConnectionChannel::_q_sslErrors(const QList<QSslError> &errors) @@ -1298,10 +1280,10 @@ void QHttpNetworkConnectionChannel::_q_sslErrors(const QList<QSslError> &errors) } #ifndef QT_NO_SSL else { // HTTP/2 - QList<HttpMessagePair> h2Pairs = h2RequestsToSend.values(); - for (int a = 0; a < h2Pairs.count(); ++a) { + const auto h2RequestsToSendCopy = h2RequestsToSend; + for (const auto &httpMessagePair : h2RequestsToSendCopy) { // emit SSL errors for all replies - QHttpNetworkReply *currentReply = h2Pairs.at(a).second; + QHttpNetworkReply *currentReply = httpMessagePair.second; Q_ASSERT(currentReply); emit currentReply->sslErrors(errors); } @@ -1321,10 +1303,10 @@ void QHttpNetworkConnectionChannel::_q_preSharedKeyAuthenticationRequired(QSslPr if (reply) emit reply->preSharedKeyAuthenticationRequired(authenticator); } else { - QList<HttpMessagePair> h2Pairs = h2RequestsToSend.values(); - for (int a = 0; a < h2Pairs.count(); ++a) { + const auto h2RequestsToSendCopy = h2RequestsToSend; + for (const auto &httpMessagePair : h2RequestsToSendCopy) { // emit SSL errors for all replies - QHttpNetworkReply *currentReply = h2Pairs.at(a).second; + QHttpNetworkReply *currentReply = httpMessagePair.second; Q_ASSERT(currentReply); emit currentReply->preSharedKeyAuthenticationRequired(authenticator); } |