Prefer previously used channels in QHttpNetworkConnection

When IPv4 and IPv6 are supported by a server, QHttpNetworkConnection
will start up two connections and pick the network layer of the one
that finish first. In this case the channel with index 1 is used for
IPv6. When IPv6 wins, there is no channel at index 0. This situation
needs to be respected and we should try to use existing channels first
when there is a next request.

This is especially important when TLS session resumption is used.
Creating a new channel will cause to lose the ephemeralServerKey
used in the first connection.

Fixes: QTBUG-93295
Change-Id: Ic9dc6a24ef793a29c2652ad37bc11120e2e6ceef
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
(cherry picked from commit a120d11cb5506ef0a5535e790f23d49595fb8857)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Lars Schmertmann 2017-09-14 12:47:11 +02:00 committed by Qt Cherry-pick Bot
parent e5946f7b18
commit 516d8d22ff

View File

@ -1117,31 +1117,50 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
int normalRequests = queuedRequests - preConnectRequests;
neededOpenChannels = qMax(normalRequests, preConnectRequests);
}
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;
return;
QQueue<int> channelsToConnect;
// use previously used channels first
for (int i = 0; i < activeChannelCount && neededOpenChannels > 0; ++i) {
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();
}
}