diff options
Diffstat (limited to 'src/network/access/qhttpnetworkconnection.cpp')
-rw-r--r-- | src/network/access/qhttpnetworkconnection.cpp | 159 |
1 files changed, 105 insertions, 54 deletions
diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp index b0116319b0..20b8d446ba 100644 --- a/src/network/access/qhttpnetworkconnection.cpp +++ b/src/network/access/qhttpnetworkconnection.cpp @@ -462,7 +462,12 @@ bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket else channels[i].authMethod = priv->method; - if (priv->phase == QAuthenticatorPrivate::Done) { + if (priv->phase == QAuthenticatorPrivate::Done || + (priv->phase == QAuthenticatorPrivate::Start + && priv->method == QAuthenticatorPrivate::Ntlm)) { + if (priv->phase == QAuthenticatorPrivate::Start) + priv->phase = QAuthenticatorPrivate::Phase1; + pauseConnection(); if (!isProxy) { if (channels[i].authenticationCredentialsSent) { @@ -510,8 +515,8 @@ bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket channels[i].authenticator = QAuthenticator(); // authentication is cancelled, send the current contents to the user. - emit channels[i].reply->headerChanged(); - emit channels[i].reply->readyRead(); + emit reply->headerChanged(); + emit reply->readyRead(); QNetworkReply::NetworkError errorCode = isProxy ? QNetworkReply::ProxyAuthenticationRequiredError @@ -528,10 +533,23 @@ bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket return false; } -QUrl QHttpNetworkConnectionPrivate::parseRedirectResponse(QAbstractSocket *socket, QHttpNetworkReply *reply) +// Used by the HTTP1 code-path +QUrl QHttpNetworkConnectionPrivate::parseRedirectResponse(QAbstractSocket *socket, + QHttpNetworkReply *reply) +{ + ParseRedirectResult result = parseRedirectResponse(reply); + if (result.errorCode != QNetworkReply::NoError) { + emitReplyError(socket, reply, result.errorCode); + return {}; + } + return std::move(result.redirectUrl); +} + +QHttpNetworkConnectionPrivate::ParseRedirectResult +QHttpNetworkConnectionPrivate::parseRedirectResponse(QHttpNetworkReply *reply) { if (!reply->request().isFollowRedirects()) - return QUrl(); + return {{}, QNetworkReply::NoError}; QUrl redirectUrl; const QList<QPair<QByteArray, QByteArray> > fields = reply->header(); @@ -542,17 +560,13 @@ QUrl QHttpNetworkConnectionPrivate::parseRedirectResponse(QAbstractSocket *socke } } - // If the location url is invalid/empty, we emit ProtocolUnknownError - if (!redirectUrl.isValid()) { - emitReplyError(socket, reply, QNetworkReply::ProtocolUnknownError); - return QUrl(); - } + // If the location url is invalid/empty, we return ProtocolUnknownError + if (!redirectUrl.isValid()) + return {{}, QNetworkReply::ProtocolUnknownError}; // Check if we have exceeded max redirects allowed - if (reply->request().redirectCount() <= 0) { - emitReplyError(socket, reply, QNetworkReply::TooManyRedirectsError); - return QUrl(); - } + if (reply->request().redirectCount() <= 0) + return {{}, QNetworkReply::TooManyRedirectsError}; // Resolve the URL if it's relative if (redirectUrl.isRelative()) @@ -573,8 +587,7 @@ QUrl QHttpNetworkConnectionPrivate::parseRedirectResponse(QAbstractSocket *socke if (priorUrl.host() != redirectUrl.host() || priorUrl.scheme() != redirectUrl.scheme() || priorUrl.port() != redirectUrl.port()) { - emitReplyError(socket, reply, QNetworkReply::InsecureRedirectError); - return QUrl(); + return {{}, QNetworkReply::InsecureRedirectError}; } break; case QNetworkRequest::UserVerifiedRedirectPolicy: @@ -583,10 +596,9 @@ QUrl QHttpNetworkConnectionPrivate::parseRedirectResponse(QAbstractSocket *socke Q_ASSERT(!"Unexpected redirect policy"); } } else { - emitReplyError(socket, reply, QNetworkReply::ProtocolUnknownError); - return QUrl(); + return {{}, QNetworkReply::ProtocolUnknownError}; } - return redirectUrl; + return {std::move(redirectUrl), QNetworkReply::NoError}; } void QHttpNetworkConnectionPrivate::createAuthorization(QAbstractSocket *socket, QHttpNetworkRequest &request) @@ -597,10 +609,20 @@ void QHttpNetworkConnectionPrivate::createAuthorization(QAbstractSocket *socket, // Send "Authorization" header, but not if it's NTLM and the socket is already authenticated. if (channels[i].authMethod != QAuthenticatorPrivate::None) { - if ((channels[i].authMethod != QAuthenticatorPrivate::Ntlm && request.headerField("Authorization").isEmpty()) || channels[i].lastStatus == 401) { - QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(channels[i].authenticator); - if (priv && priv->method != QAuthenticatorPrivate::None) { - QByteArray response = priv->calculateResponse(request.methodName(), request.uri(false), request.url().host()); + QAuthenticatorPrivate *priv = + QAuthenticatorPrivate::getPrivate(channels[i].authenticator); + if (priv && priv->method != QAuthenticatorPrivate::None) { + const bool ntlmNego = priv->method == QAuthenticatorPrivate::Ntlm + || priv->method == QAuthenticatorPrivate::Negotiate; + const bool authNeeded = channels[i].lastStatus == 401; + const bool ntlmNegoOk = ntlmNego && authNeeded + && (priv->phase != QAuthenticatorPrivate::Done + || !channels[i].authenticationCredentialsSent); + const bool otherOk = + !ntlmNego && (authNeeded || request.headerField("Authorization").isEmpty()); + if (ntlmNegoOk || otherOk) { + QByteArray response = priv->calculateResponse( + request.methodName(), request.uri(false), request.url().host()); request.setHeaderField("Authorization", response); channels[i].authenticationCredentialsSent = true; } @@ -610,10 +632,19 @@ void QHttpNetworkConnectionPrivate::createAuthorization(QAbstractSocket *socket, #if QT_CONFIG(networkproxy) // Send "Proxy-Authorization" header, but not if it's NTLM and the socket is already authenticated. if (channels[i].proxyAuthMethod != QAuthenticatorPrivate::None) { - if (!(channels[i].proxyAuthMethod == QAuthenticatorPrivate::Ntlm && channels[i].lastStatus != 407)) { - QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(channels[i].proxyAuthenticator); - if (priv && priv->method != QAuthenticatorPrivate::None) { - QByteArray response = priv->calculateResponse(request.methodName(), request.uri(false), networkProxy.hostName()); + QAuthenticatorPrivate *priv = + QAuthenticatorPrivate::getPrivate(channels[i].proxyAuthenticator); + if (priv && priv->method != QAuthenticatorPrivate::None) { + const bool ntlmNego = channels[i].proxyAuthMethod == QAuthenticatorPrivate::Ntlm + || channels[i].proxyAuthMethod == QAuthenticatorPrivate::Negotiate; + const bool proxyAuthNeeded = channels[i].lastStatus == 407; + const bool ntlmNegoOk = ntlmNego && proxyAuthNeeded + && (priv->phase != QAuthenticatorPrivate::Done + || !channels[i].proxyCredentialsSent); + const bool otherOk = !ntlmNego; + if (ntlmNegoOk || otherOk) { + QByteArray response = priv->calculateResponse( + request.methodName(), request.uri(false), networkProxy.hostName()); request.setHeaderField("Proxy-Authorization", response); channels[i].proxyCredentialsSent = true; } @@ -1077,8 +1108,10 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest() case QHttpNetworkConnection::ConnectionTypeHTTP2Direct: case QHttpNetworkConnection::ConnectionTypeHTTP2: case QHttpNetworkConnection::ConnectionTypeSPDY: { - if (channels[0].spdyRequestsToSend.isEmpty() && channels[0].switchedToHttp2) + if (channels[0].spdyRequestsToSend.isEmpty() && !channels[0].reply + && highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty()) { return; + } if (networkLayerState == IPv4) channels[0].networkLayerPreference = QAbstractSocket::IPv4Protocol; @@ -1118,31 +1151,50 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest() int normalRequests = queuedRequests - preConnectRequests; neededOpenChannels = qMax(normalRequests, preConnectRequests); } + + if (neededOpenChannels <= 0) + return; + + QQueue<int> channelsToConnect; + + // use previously used channels first for (int i = 0; i < activeChannelCount && neededOpenChannels > 0; ++i) { - bool connectChannel = false; - if (channels[i].socket) { - if ((channels[i].socket->state() == QAbstractSocket::ConnectingState) - || (channels[i].socket->state() == QAbstractSocket::HostLookupState) - || channels[i].pendingEncrypt) // pendingEncrypt == "EncryptingState" - neededOpenChannels--; - - if (neededOpenChannels <= 0) - break; - if (!channels[i].reply && !channels[i].isSocketBusy() && (channels[i].socket->state() == QAbstractSocket::UnconnectedState)) - connectChannel = true; - } else { // not previously used channel - connectChannel = true; + if (!channels[i].socket) + continue; + + if ((channels[i].socket->state() == QAbstractSocket::ConnectingState) + || (channels[i].socket->state() == QAbstractSocket::HostLookupState) + || channels[i].pendingEncrypt) { // pendingEncrypt == "EncryptingState" + neededOpenChannels--; + continue; } - if (connectChannel) { - if (networkLayerState == IPv4) - channels[i].networkLayerPreference = QAbstractSocket::IPv4Protocol; - else if (networkLayerState == IPv6) - channels[i].networkLayerPreference = QAbstractSocket::IPv6Protocol; - channels[i].ensureConnection(); + if (!channels[i].reply && !channels[i].isSocketBusy() + && (channels[i].socket->state() == QAbstractSocket::UnconnectedState)) { + channelsToConnect.enqueue(i); neededOpenChannels--; } } + + // use other channels + for (int i = 0; i < activeChannelCount && neededOpenChannels > 0; ++i) { + if (channels[i].socket) + continue; + + channelsToConnect.enqueue(i); + neededOpenChannels--; + } + + while (!channelsToConnect.isEmpty()) { + const int channel = channelsToConnect.dequeue(); + + if (networkLayerState == IPv4) + channels[channel].networkLayerPreference = QAbstractSocket::IPv4Protocol; + else if (networkLayerState == IPv6) + channels[channel].networkLayerPreference = QAbstractSocket::IPv6Protocol; + + channels[channel].ensureConnection(); + } } @@ -1251,11 +1303,10 @@ void QHttpNetworkConnectionPrivate::_q_hostLookupFinished(const QHostInfo &info) emitReplyError(channels[0].socket, currentReply, QNetworkReply::HostNotFoundError); } } else { - // Should not happen: we start a host lookup before sending a request, - // so it's natural to have requests either in SPDY/HTTP/2 queue, - // or in low/high priority queues. - qWarning("QHttpNetworkConnectionPrivate::_q_hostLookupFinished" - " could not de-queue request, failed to report HostNotFoundError"); + // We can end up here if a request has been aborted or otherwise failed (e.g. timeout) + // before the host lookup was finished. + qDebug("QHttpNetworkConnectionPrivate::_q_hostLookupFinished" + " could not de-queue request, failed to report HostNotFoundError"); networkLayerState = QHttpNetworkConnectionPrivate::Unknown; } } @@ -1425,7 +1476,7 @@ void QHttpNetworkConnection::setCacheProxy(const QNetworkProxy &networkProxy) d->networkProxy = networkProxy; // update the authenticator if (!d->networkProxy.user().isEmpty()) { - for (int i = 0; i < d->activeChannelCount; ++i) { + for (int i = 0; i < d->channelCount; ++i) { d->channels[i].proxyAuthenticator.setUser(d->networkProxy.user()); d->channels[i].proxyAuthenticator.setPassword(d->networkProxy.password()); } @@ -1441,7 +1492,7 @@ QNetworkProxy QHttpNetworkConnection::cacheProxy() const void QHttpNetworkConnection::setTransparentProxy(const QNetworkProxy &networkProxy) { Q_D(QHttpNetworkConnection); - for (int i = 0; i < d->activeChannelCount; ++i) + for (int i = 0; i < d->channelCount; ++i) d->channels[i].setProxy(networkProxy); } |