Coverage Report

Created: 2026-08-17 07:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/qtbase/src/network/access/qhttpnetworkconnectionchannel.cpp
Line
Count
Source
1
// Copyright (C) 2016 The Qt Company Ltd.
2
// Copyright (C) 2014 BlackBerry Limited. All rights reserved.
3
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
// Qt-Security score:critical reason:network-protocol
5
6
#include "qhttpnetworkconnectionchannel_p.h"
7
#include "qhttpnetworkconnection_p.h"
8
#include "private/qnoncontiguousbytedevice_p.h"
9
10
#include <qdebug.h>
11
12
#include <private/qhttp2protocolhandler_p.h>
13
#include <private/qhttpprotocolhandler_p.h>
14
#include <private/http2protocol_p.h>
15
#include <private/qsocketabstraction_p.h>
16
17
#ifndef QT_NO_SSL
18
#    include <private/qsslsocket_p.h>
19
#    include <QtNetwork/qsslkey.h>
20
#    include <QtNetwork/qsslcipher.h>
21
#endif
22
23
#include <QtNetwork/private/qtnetworkglobal_p.h>
24
25
#include <memory>
26
#include <utility>
27
28
QT_BEGIN_NAMESPACE
29
30
// TODO: Put channel specific stuff here so it does not pollute qhttpnetworkconnection.cpp
31
32
// Because in-flight when sending a request, the server might close our connection (because the persistent HTTP
33
// connection times out)
34
// We use 3 because we can get a _q_error 3 times depending on the timing:
35
static const int reconnectAttemptsDefault = 3;
36
static const char keepAliveIdleOption[] = "QT_QNAM_TCP_KEEPIDLE";
37
static const char keepAliveIntervalOption[] = "QT_QNAM_TCP_KEEPINTVL";
38
static const char keepAliveCountOption[] = "QT_QNAM_TCP_KEEPCNT";
39
static const int TCP_KEEPIDLE_DEF = 60;
40
static const int TCP_KEEPINTVL_DEF = 10;
41
static const int TCP_KEEPCNT_DEF = 5;
42
43
QHttpNetworkConnectionChannel::QHttpNetworkConnectionChannel()
44
0
    : socket(nullptr)
45
0
    , ssl(false)
46
0
    , isInitialized(false)
47
0
    , state(IdleState)
48
0
    , reply(nullptr)
49
0
    , written(0)
50
0
    , bytesTotal(0)
51
0
    , resendCurrent(false)
52
0
    , lastStatus(0)
53
0
    , pendingEncrypt(false)
54
0
    , reconnectAttempts(reconnectAttemptsDefault)
55
0
    , authenticationCredentialsSent(false)
56
0
    , proxyCredentialsSent(false)
57
0
    , protocolHandler(nullptr)
58
#ifndef QT_NO_SSL
59
0
    , ignoreAllSslErrors(false)
60
#endif
61
0
    , pipeliningSupported(PipeliningSupportUnknown)
62
0
    , networkLayerPreference(QAbstractSocket::AnyIPProtocol)
63
0
    , connection(nullptr)
64
0
{
65
    // Inlining this function in the header leads to compiler error on
66
    // release-armv5, on at least timebox 9.2 and 10.1.
67
0
}
68
69
void QHttpNetworkConnectionChannel::init()
70
0
{
71
0
#ifndef QT_NO_SSL
72
0
    if (connection->d_func()->encrypt)
73
0
        socket = new QSslSocket;
74
0
#if QT_CONFIG(localserver)
75
0
    else if (connection->d_func()->isLocalSocket)
76
0
        socket = new QLocalSocket;
77
0
#endif
78
0
    else
79
0
        socket = new QTcpSocket;
80
#else
81
    socket = new QTcpSocket;
82
#endif
83
0
#ifndef QT_NO_NETWORKPROXY
84
    // Set by QNAM anyway, but let's be safe here
85
0
    if (auto s = qobject_cast<QAbstractSocket *>(socket))
86
0
        s->setProxy(QNetworkProxy::NoProxy);
87
0
#endif
88
89
    // After some back and forth in all the last years, this is now a DirectConnection because otherwise
90
    // the state inside the *Socket classes gets messed up, also in conjunction with the socket notifiers
91
    // which behave slightly differently on Windows vs Linux
92
0
    QObject::connect(socket, &QIODevice::bytesWritten,
93
0
                     this, &QHttpNetworkConnectionChannel::_q_bytesWritten,
94
0
                     Qt::DirectConnection);
95
0
    QObject::connect(socket, &QIODevice::readyRead,
96
0
                     this, &QHttpNetworkConnectionChannel::_q_readyRead,
97
0
                     Qt::DirectConnection);
98
99
100
0
    QSocketAbstraction::visit([this](auto *socket){
101
0
        using SocketType = std::remove_pointer_t<decltype(socket)>;
102
0
        QObject::connect(socket, &SocketType::connected,
103
0
                        this, &QHttpNetworkConnectionChannel::_q_connected,
104
0
                        Qt::DirectConnection);
105
106
        // The disconnected() and error() signals may already come
107
        // while calling connectToHost().
108
        // In case of a cached hostname or an IP this
109
        // will then emit a signal to the user of QNetworkReply
110
        // but cannot be caught because the user did not have a chance yet
111
        // to connect to QNetworkReply's signals.
112
0
        QObject::connect(socket, &SocketType::disconnected,
113
0
                        this, &QHttpNetworkConnectionChannel::_q_disconnected,
114
0
                        Qt::DirectConnection);
115
0
        if constexpr (std::is_same_v<SocketType, QAbstractSocket>) {
116
0
            QObject::connect(socket, &QAbstractSocket::errorOccurred,
117
0
                            this, &QHttpNetworkConnectionChannel::_q_error,
118
0
                            Qt::DirectConnection);
119
0
#if QT_CONFIG(localserver)
120
0
        } else if constexpr (std::is_same_v<SocketType, QLocalSocket>) {
121
0
            auto convertAndForward = [this](QLocalSocket::LocalSocketError error) {
122
0
                _q_error(static_cast<QAbstractSocket::SocketError>(error));
123
0
            };
124
0
            QObject::connect(socket, &SocketType::errorOccurred,
125
0
                            this, std::move(convertAndForward),
126
0
                            Qt::DirectConnection);
127
0
#endif
128
0
        }
129
0
    }, socket);
Unexecuted instantiation: qhttpnetworkconnectionchannel.cpp:auto QHttpNetworkConnectionChannel::init()::$_0::operator()<QAbstractSocket>(QAbstractSocket*) const
Unexecuted instantiation: qhttpnetworkconnectionchannel.cpp:auto QHttpNetworkConnectionChannel::init()::$_0::operator()<QLocalSocket>(QLocalSocket*) const
130
131
132
133
0
#ifndef QT_NO_NETWORKPROXY
134
0
    if (auto *s = qobject_cast<QAbstractSocket *>(socket)) {
135
0
        QObject::connect(s, &QAbstractSocket::proxyAuthenticationRequired,
136
0
                        this, &QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired,
137
0
                        Qt::DirectConnection);
138
0
    }
139
0
#endif
140
141
0
#ifndef QT_NO_SSL
142
0
    QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket);
143
0
    if (sslSocket) {
144
        // won't be a sslSocket if encrypt is false
145
0
        QObject::connect(sslSocket, &QSslSocket::encrypted,
146
0
                         this, &QHttpNetworkConnectionChannel::_q_encrypted,
147
0
                         Qt::DirectConnection);
148
0
        QObject::connect(sslSocket, &QSslSocket::sslErrors,
149
0
                         this, &QHttpNetworkConnectionChannel::_q_sslErrors,
150
0
                         Qt::DirectConnection);
151
0
        QObject::connect(sslSocket, &QSslSocket::preSharedKeyAuthenticationRequired,
152
0
                         this, &QHttpNetworkConnectionChannel::_q_preSharedKeyAuthenticationRequired,
153
0
                         Qt::DirectConnection);
154
0
        QObject::connect(sslSocket, &QSslSocket::encryptedBytesWritten,
155
0
                         this, &QHttpNetworkConnectionChannel::_q_encryptedBytesWritten,
156
0
                         Qt::DirectConnection);
157
158
        // AXIVION DISABLE Qt-Security-QSslSocketIgnoreSslErrors: intentional behavior
159
0
        if (ignoreAllSslErrors)
160
0
            sslSocket->ignoreSslErrors();
161
162
0
        if (!ignoreSslErrorsList.isEmpty())
163
0
            sslSocket->ignoreSslErrors(ignoreSslErrorsList);
164
        // AXIVION ENABLE Qt-Security-QSslSocketIgnoreSslErrors
165
166
0
        if (sslConfiguration && !sslConfiguration->isNull())
167
0
           sslSocket->setSslConfiguration(*sslConfiguration);
168
0
    } else {
169
0
#endif // !QT_NO_SSL
170
0
        if (connection->connectionType() != QHttpNetworkConnection::ConnectionTypeHTTP2)
171
0
            protocolHandler.reset(new QHttpProtocolHandler(this));
172
0
#ifndef QT_NO_SSL
173
0
    }
174
0
#endif
175
176
0
#ifndef QT_NO_NETWORKPROXY
177
0
    if (auto *s = qobject_cast<QAbstractSocket *>(socket);
178
0
        s && proxy.type() != QNetworkProxy::NoProxy) {
179
0
        s->setProxy(proxy);
180
0
    }
181
0
#endif
182
0
    isInitialized = true;
183
0
}
184
185
186
void QHttpNetworkConnectionChannel::close()
187
0
{
188
0
    if (state == QHttpNetworkConnectionChannel::ClosingState)
189
0
        return;
190
191
0
    if (!socket)
192
0
        state = QHttpNetworkConnectionChannel::IdleState;
193
0
    else if (QSocketAbstraction::socketState(socket) == QAbstractSocket::UnconnectedState)
194
0
        state = QHttpNetworkConnectionChannel::IdleState;
195
0
    else
196
0
        state = QHttpNetworkConnectionChannel::ClosingState;
197
198
    // pendingEncrypt must only be true in between connected and encrypted states
199
0
    pendingEncrypt = false;
200
201
0
    if (socket) {
202
        // socket can be 0 since the host lookup is done from qhttpnetworkconnection.cpp while
203
        // there is no socket yet.
204
0
        socket->close();
205
0
    }
206
0
}
207
208
209
void QHttpNetworkConnectionChannel::abort()
210
0
{
211
0
    if (!socket)
212
0
        state = QHttpNetworkConnectionChannel::IdleState;
213
0
    else if (QSocketAbstraction::socketState(socket) == QAbstractSocket::UnconnectedState)
214
0
        state = QHttpNetworkConnectionChannel::IdleState;
215
0
    else
216
0
        state = QHttpNetworkConnectionChannel::ClosingState;
217
218
    // pendingEncrypt must only be true in between connected and encrypted states
219
0
    pendingEncrypt = false;
220
221
0
    if (socket) {
222
        // socket can be 0 since the host lookup is done from qhttpnetworkconnection.cpp while
223
        // there is no socket yet.
224
0
        auto callAbort = [](auto *s) {
225
0
            s->abort();
226
0
        };
Unexecuted instantiation: qhttpnetworkconnectionchannel.cpp:auto QHttpNetworkConnectionChannel::abort()::$_0::operator()<QAbstractSocket>(QAbstractSocket*) const
Unexecuted instantiation: qhttpnetworkconnectionchannel.cpp:auto QHttpNetworkConnectionChannel::abort()::$_0::operator()<QLocalSocket>(QLocalSocket*) const
227
0
        QSocketAbstraction::visit(callAbort, socket);
228
0
    }
229
0
}
230
231
232
void QHttpNetworkConnectionChannel::sendRequest()
233
0
{
234
0
    Q_ASSERT(protocolHandler);
235
0
    if (waitingForPotentialAbort) {
236
0
        needInvokeSendRequest = true;
237
0
        return;
238
0
    }
239
0
    protocolHandler->sendRequest();
240
0
}
241
242
/*
243
 * Invoke "protocolHandler->sendRequest" using a queued connection.
244
 * It's used to return to the event loop before invoking sendRequest when
245
 * there's a very real chance that the request could have been aborted
246
 * (i.e. after having emitted 'encrypted').
247
 */
248
void QHttpNetworkConnectionChannel::sendRequestDelayed()
249
0
{
250
0
    QMetaObject::invokeMethod(this, [this] {
251
0
        if (reply)
252
0
            sendRequest();
253
0
    }, Qt::ConnectionType::QueuedConnection);
254
0
}
255
256
void QHttpNetworkConnectionChannel::_q_receiveReply()
257
0
{
258
0
    Q_ASSERT(protocolHandler);
259
0
    if (waitingForPotentialAbort) {
260
0
        needInvokeReceiveReply = true;
261
0
        return;
262
0
    }
263
0
    protocolHandler->_q_receiveReply();
264
0
}
265
266
void QHttpNetworkConnectionChannel::_q_readyRead()
267
0
{
268
0
    Q_ASSERT(protocolHandler);
269
0
    if (waitingForPotentialAbort) {
270
0
        needInvokeReadyRead = true;
271
0
        return;
272
0
    }
273
0
    protocolHandler->_q_readyRead();
274
0
}
275
276
// called when unexpectedly reading a -1 or when data is expected but socket is closed
277
void QHttpNetworkConnectionChannel::handleUnexpectedEOF()
278
0
{
279
0
    Q_ASSERT(reply);
280
0
    if (reconnectAttempts <= 0 || !request.methodIsIdempotent()) {
281
        // too many errors reading/receiving/parsing the status, close the socket and emit error
282
0
        requeueCurrentlyPipelinedRequests();
283
0
        close();
284
0
        reply->d_func()->errorString = connection->d_func()->errorDetail(QNetworkReply::RemoteHostClosedError, socket);
285
0
        emit reply->finishedWithError(QNetworkReply::RemoteHostClosedError, reply->d_func()->errorString);
286
0
        reply = nullptr;
287
0
        if (protocolHandler)
288
0
            protocolHandler->setReply(nullptr);
289
0
        request = QHttpNetworkRequest();
290
0
        QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
291
0
    } else {
292
0
        reconnectAttempts--;
293
0
        reply->d_func()->clear();
294
0
        reply->d_func()->connection = connection;
295
0
        reply->d_func()->connectionChannel = this;
296
0
        closeAndResendCurrentRequest();
297
0
    }
298
0
}
299
300
bool QHttpNetworkConnectionChannel::ensureConnection()
301
0
{
302
0
    if (!isInitialized)
303
0
        init();
304
305
0
    QAbstractSocket::SocketState socketState = QSocketAbstraction::socketState(socket);
306
307
    // resend this request after we receive the disconnected signal
308
    // If !socket->isOpen() then we have already called close() on the socket, but there was still a
309
    // pending connectToHost() for which we hadn't seen a connected() signal, yet. The connected()
310
    // has now arrived (as indicated by socketState != ClosingState), but we cannot send anything on
311
    // such a socket anymore.
312
0
    if (socketState == QAbstractSocket::ClosingState ||
313
0
            (socketState != QAbstractSocket::UnconnectedState && !socket->isOpen())) {
314
0
        if (reply)
315
0
            resendCurrent = true;
316
0
        return false;
317
0
    }
318
319
    // already trying to connect?
320
0
    if (socketState == QAbstractSocket::HostLookupState ||
321
0
        socketState == QAbstractSocket::ConnectingState) {
322
0
        return false;
323
0
    }
324
325
    // make sure that this socket is in a connected state, if not initiate
326
    // connection to the host.
327
0
    if (socketState != QAbstractSocket::ConnectedState) {
328
        // connect to the host if not already connected.
329
0
        state = QHttpNetworkConnectionChannel::ConnectingState;
330
0
        pendingEncrypt = ssl;
331
332
        // reset state
333
0
        pipeliningSupported = PipeliningSupportUnknown;
334
0
        authenticationCredentialsSent = false;
335
0
        proxyCredentialsSent = false;
336
0
        authenticator.detach();
337
0
        QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(authenticator);
338
0
        priv->hasFailed = false;
339
0
        proxyAuthenticator.detach();
340
0
        priv = QAuthenticatorPrivate::getPrivate(proxyAuthenticator);
341
0
        priv->hasFailed = false;
342
343
        // This workaround is needed since we use QAuthenticator for NTLM authentication. The "phase == Done"
344
        // is the usual criteria for emitting authentication signals. The "phase" is set to "Done" when the
345
        // last header for Authorization is generated by the QAuthenticator. Basic & Digest logic does not
346
        // check the "phase" for generating the Authorization header. NTLM authentication is a two stage
347
        // process & needs the "phase". To make sure the QAuthenticator uses the current username/password
348
        // the phase is reset to Start.
349
0
        priv = QAuthenticatorPrivate::getPrivate(authenticator);
350
0
        if (priv && priv->phase == QAuthenticatorPrivate::Done)
351
0
            priv->phase = QAuthenticatorPrivate::Start;
352
0
        priv = QAuthenticatorPrivate::getPrivate(proxyAuthenticator);
353
0
        if (priv && priv->phase == QAuthenticatorPrivate::Done)
354
0
            priv->phase = QAuthenticatorPrivate::Start;
355
356
0
        QString connectHost = connection->d_func()->hostName;
357
0
        quint16 connectPort = connection->d_func()->port;
358
359
0
        QHttpNetworkReply *potentialReply = connection->d_func()->predictNextRequestsReply();
360
0
        if (potentialReply) {
361
0
            QMetaObject::invokeMethod(potentialReply, "socketStartedConnecting", Qt::QueuedConnection);
362
0
        } else if (!h2RequestsToSend.isEmpty()) {
363
0
            QMetaObject::invokeMethod(std::as_const(h2RequestsToSend).first().second, "socketStartedConnecting", Qt::QueuedConnection);
364
0
        }
365
366
0
#ifndef QT_NO_NETWORKPROXY
367
        // HTTPS always use transparent proxy.
368
0
        if (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy && !ssl) {
369
0
            connectHost = connection->d_func()->networkProxy.hostName();
370
0
            connectPort = connection->d_func()->networkProxy.port();
371
0
        }
372
0
        if (auto *abSocket = qobject_cast<QAbstractSocket *>(socket);
373
0
            abSocket && abSocket->proxy().type() == QNetworkProxy::HttpProxy) {
374
            // Make user-agent field available to HTTP proxy socket engine (QTBUG-17223)
375
0
            QByteArray value;
376
            // ensureConnection is called before any request has been assigned, but can also be
377
            // called again if reconnecting
378
0
            if (request.url().isEmpty()) {
379
0
                if (connection->connectionType()
380
0
                            == QHttpNetworkConnection::ConnectionTypeHTTP2Direct
381
0
                    || (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2
382
0
                        && !h2RequestsToSend.isEmpty())) {
383
0
                    value = std::as_const(h2RequestsToSend).first().first.headerField("user-agent");
384
0
                } else {
385
0
                    value = connection->d_func()->predictNextRequest().headerField("user-agent");
386
0
                }
387
0
            } else {
388
0
                value = request.headerField("user-agent");
389
0
            }
390
0
            if (!value.isEmpty()) {
391
0
                QNetworkProxy proxy(abSocket->proxy());
392
0
                auto h = proxy.headers();
393
0
                h.replaceOrAppend(QHttpHeaders::WellKnownHeader::UserAgent, value);
394
0
                proxy.setHeaders(std::move(h));
395
0
                abSocket->setProxy(proxy);
396
0
            }
397
0
        }
398
0
#endif
399
0
        if (ssl) {
400
0
#ifndef QT_NO_SSL
401
0
            QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket);
402
403
            // check whether we can re-use an existing SSL session
404
            // (meaning another socket in this connection has already
405
            // performed a full handshake)
406
0
            if (auto ctx = connection->sslContext())
407
0
                QSslSocketPrivate::checkSettingSslContext(sslSocket, std::move(ctx));
408
409
0
            sslSocket->setPeerVerifyName(connection->d_func()->peerVerifyName);
410
0
            sslSocket->connectToHostEncrypted(connectHost, connectPort, QIODevice::ReadWrite, networkLayerPreference);
411
            // AXIVION DISABLE Qt-Security-QSslSocketIgnoreSslErrors: intentional behavior
412
0
            if (ignoreAllSslErrors)
413
0
                sslSocket->ignoreSslErrors();
414
0
            sslSocket->ignoreSslErrors(ignoreSslErrorsList);
415
            // AXIVION ENABLE Qt-Security-QSslSocketIgnoreSslErrors
416
417
            // limit the socket read buffer size. we will read everything into
418
            // the QHttpNetworkReply anyway, so let's grow only that and not
419
            // here and there.
420
0
            sslSocket->setReadBufferSize(64*1024);
421
#else
422
            // Need to dequeue the request so that we can emit the error.
423
            if (!reply)
424
                connection->d_func()->dequeueRequest(socket);
425
            connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ProtocolUnknownError);
426
#endif
427
0
        } else {
428
            // In case of no proxy we can use the Unbuffered QTcpSocket
429
0
#ifndef QT_NO_NETWORKPROXY
430
0
            if (connection->d_func()->networkProxy.type() == QNetworkProxy::NoProxy
431
0
                    && connection->cacheProxy().type() == QNetworkProxy::NoProxy
432
0
                    && connection->transparentProxy().type() == QNetworkProxy::NoProxy) {
433
0
#endif
434
0
                if (auto *s = qobject_cast<QAbstractSocket *>(socket)) {
435
0
                    s->connectToHost(connectHost, connectPort,
436
0
                                     QIODevice::ReadWrite | QIODevice::Unbuffered,
437
0
                                     networkLayerPreference);
438
                    // For an Unbuffered QTcpSocket, the read buffer size has a special meaning.
439
0
                    s->setReadBufferSize(1 * 1024);
440
0
#if QT_CONFIG(localserver)
441
0
                } else if (auto *s = qobject_cast<QLocalSocket *>(socket)) {
442
0
                    s->connectToServer(connectHost);
443
0
#endif
444
0
                }
445
0
#ifndef QT_NO_NETWORKPROXY
446
0
            } else {
447
0
                auto *s = qobject_cast<QAbstractSocket *>(socket);
448
0
                Q_ASSERT(s);
449
                // limit the socket read buffer size. we will read everything into
450
                // the QHttpNetworkReply anyway, so let's grow only that and not
451
                // here and there.
452
0
                s->connectToHost(connectHost, connectPort, QIODevice::ReadWrite, networkLayerPreference);
453
0
                s->setReadBufferSize(64 * 1024);
454
0
            }
455
0
#endif
456
0
        }
457
0
        return false;
458
0
    }
459
460
    // This code path for ConnectedState
461
0
    if (pendingEncrypt) {
462
        // Let's only be really connected when we have received the encrypted() signal. Else the state machine seems to mess up
463
        // and corrupt the things sent to the server.
464
0
        return false;
465
0
    }
466
467
0
    return true;
468
0
}
469
470
void QHttpNetworkConnectionChannel::allDone()
471
0
{
472
0
    Q_ASSERT(reply);
473
474
0
    if (!reply) {
475
0
        qWarning("QHttpNetworkConnectionChannel::allDone() called without reply. Please report at http://bugreports.qt.io/");
476
0
        return;
477
0
    }
478
479
    // For clear text HTTP/2 we tried to upgrade from HTTP/1.1 to HTTP/2; for
480
    // ConnectionTypeHTTP2Direct we can never be here in case of failure
481
    // (after an attempt to read HTTP/1.1 as HTTP/2 frames) or we have a normal
482
    // HTTP/2 response and thus can skip this test:
483
0
    if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2
484
0
        && !ssl && !switchedToHttp2) {
485
0
        if (Http2::is_protocol_upgraded(*reply)) {
486
0
            switchedToHttp2 = true;
487
0
            protocolHandler->setReply(nullptr);
488
489
            // As allDone() gets called from the protocol handler, it's not yet
490
            // safe to delete it. There is no 'deleteLater', since
491
            // QAbstractProtocolHandler is not a QObject. Instead delete it in
492
            // a queued emission.
493
494
0
            QMetaObject::invokeMethod(this, [oldHandler = std::move(protocolHandler)]() mutable {
495
0
                oldHandler.reset();
496
0
            }, Qt::QueuedConnection);
497
498
0
            connection->fillHttp2Queue();
499
0
            protocolHandler.reset(new QHttp2ProtocolHandler(this));
500
0
            QHttp2ProtocolHandler *h2c = static_cast<QHttp2ProtocolHandler *>(protocolHandler.get());
501
0
            QMetaObject::invokeMethod(h2c, "_q_receiveReply", Qt::QueuedConnection);
502
0
            QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
503
0
            return;
504
0
        } else {
505
            // Ok, whatever happened, we do not try HTTP/2 anymore ...
506
0
            connection->setConnectionType(QHttpNetworkConnection::ConnectionTypeHTTP);
507
0
            connection->d_func()->activeChannelCount = connection->d_func()->channelCount;
508
0
        }
509
0
    }
510
511
    // while handling 401 & 407, we might reset the status code, so save this.
512
0
    bool emitFinished = reply->d_func()->shouldEmitSignals();
513
0
    bool connectionCloseEnabled = reply->d_func()->isConnectionCloseEnabled();
514
0
    detectPipeliningSupport();
515
516
0
    handleStatus();
517
    // handleStatus() might have removed the reply because it already called connection->emitReplyError()
518
519
    // queue the finished signal, this is required since we might send new requests from
520
    // slot connected to it. The socket will not fire readyRead signal, if we are already
521
    // in the slot connected to readyRead
522
0
    if (reply && emitFinished)
523
0
        QMetaObject::invokeMethod(reply, "finished", Qt::QueuedConnection);
524
525
526
    // reset the reconnection attempts after we receive a complete reply.
527
    // in case of failures, each channel will attempt two reconnects before emitting error.
528
0
    reconnectAttempts = reconnectAttemptsDefault;
529
530
    // now the channel can be seen as free/idle again, all signal emissions for the reply have been done
531
0
    if (state != QHttpNetworkConnectionChannel::ClosingState)
532
0
        state = QHttpNetworkConnectionChannel::IdleState;
533
534
    // if it does not need to be sent again we can set it to 0
535
    // the previous code did not do that and we had problems with accidental re-sending of a
536
    // finished request.
537
    // Note that this may trigger a segfault at some other point. But then we can fix the underlying
538
    // problem.
539
0
    if (!resendCurrent) {
540
0
        request = QHttpNetworkRequest();
541
0
        reply = nullptr;
542
0
        protocolHandler->setReply(nullptr);
543
0
    }
544
545
    // move next from pipeline to current request
546
0
    if (!alreadyPipelinedRequests.isEmpty()) {
547
0
        if (resendCurrent || connectionCloseEnabled || QSocketAbstraction::socketState(socket) != QAbstractSocket::ConnectedState) {
548
            // move the pipelined ones back to the main queue
549
0
            requeueCurrentlyPipelinedRequests();
550
0
            close();
551
0
        } else {
552
            // there were requests pipelined in and we can continue
553
0
            HttpMessagePair messagePair = alreadyPipelinedRequests.takeFirst();
554
555
0
            request = messagePair.first;
556
0
            reply = messagePair.second;
557
0
            protocolHandler->setReply(messagePair.second);
558
0
            state = QHttpNetworkConnectionChannel::ReadingState;
559
0
            resendCurrent = false;
560
561
0
            written = 0; // message body, excluding the header, irrelevant here
562
0
            bytesTotal = 0; // message body total, excluding the header, irrelevant here
563
564
            // pipeline even more
565
0
            connection->d_func()->fillPipeline(socket);
566
567
            // continue reading
568
            //_q_receiveReply();
569
            // this was wrong, allDone gets called from that function anyway.
570
0
        }
571
0
    } else if (alreadyPipelinedRequests.isEmpty() && socket->bytesAvailable() > 0) {
572
        // this is weird. we had nothing pipelined but still bytes available. better close it.
573
0
        close();
574
575
0
        QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
576
0
    } else if (alreadyPipelinedRequests.isEmpty()) {
577
0
        if (connectionCloseEnabled)
578
0
            if (QSocketAbstraction::socketState(socket) != QAbstractSocket::UnconnectedState)
579
0
                close();
580
0
        if (qobject_cast<QHttpNetworkConnection*>(connection))
581
0
            QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
582
0
    }
583
0
}
584
585
void QHttpNetworkConnectionChannel::detectPipeliningSupport()
586
0
{
587
0
    Q_ASSERT(reply);
588
    // detect HTTP Pipelining support
589
0
    QByteArray serverHeaderField;
590
0
    if (
591
            // check for HTTP/1.1
592
0
            (reply->majorVersion() == 1 && reply->minorVersion() == 1)
593
            // check for not having connection close
594
0
            && (!reply->d_func()->isConnectionCloseEnabled())
595
            // check if it is still connected
596
0
            && (QSocketAbstraction::socketState(socket) == QAbstractSocket::ConnectedState)
597
            // check for broken servers in server reply header
598
            // this is adapted from http://mxr.mozilla.org/firefox/ident?i=SupportsPipelining
599
0
            && (serverHeaderField = reply->headerField("Server"), !serverHeaderField.contains("Microsoft-IIS/4."))
600
0
            && (!serverHeaderField.contains("Microsoft-IIS/5."))
601
0
            && (!serverHeaderField.contains("Netscape-Enterprise/3."))
602
            // this is adpoted from the knowledge of the Nokia 7.x browser team (DEF143319)
603
0
            && (!serverHeaderField.contains("WebLogic"))
604
0
            && (!serverHeaderField.startsWith("Rocket")) // a Python Web Server, see Web2py.com
605
0
            ) {
606
0
        pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningProbablySupported;
607
0
    } else {
608
0
        pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningSupportUnknown;
609
0
    }
610
0
}
611
612
// called when the connection broke and we need to queue some pipelined requests again
613
void QHttpNetworkConnectionChannel::requeueCurrentlyPipelinedRequests()
614
0
{
615
0
    for (int i = 0; i < alreadyPipelinedRequests.size(); i++)
616
0
        connection->d_func()->requeueRequest(alreadyPipelinedRequests.at(i));
617
0
    alreadyPipelinedRequests.clear();
618
619
    // only run when the QHttpNetworkConnection is not currently being destructed, e.g.
620
    // this function is called from _q_disconnected which is called because
621
    // of ~QHttpNetworkConnectionPrivate
622
0
    if (qobject_cast<QHttpNetworkConnection*>(connection))
623
0
        QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
624
0
}
625
626
void QHttpNetworkConnectionChannel::handleStatus()
627
0
{
628
0
    Q_ASSERT(socket);
629
0
    Q_ASSERT(reply);
630
631
0
    int statusCode = reply->statusCode();
632
0
    bool resend = false;
633
634
0
    switch (statusCode) {
635
0
    case 301:
636
0
    case 302:
637
0
    case 303:
638
0
    case 305:
639
0
    case 307:
640
0
    case 308: {
641
        // Parse the response headers and get the "location" url
642
0
        QUrl redirectUrl = connection->d_func()->parseRedirectResponse(socket, reply);
643
0
        if (redirectUrl.isValid())
644
0
            reply->setRedirectUrl(redirectUrl);
645
646
0
        if ((statusCode == 307 || statusCode == 308) && !resetUploadData()) {
647
            // Couldn't reset the upload data, which means it will be unable to POST the data -
648
            // this would lead to a long wait until it eventually failed and then retried.
649
            // Instead of doing that we fail here instead, resetUploadData will already have emitted
650
            // a ContentReSendError, so we're done.
651
0
        } else if (qobject_cast<QHttpNetworkConnection *>(connection)) {
652
0
            QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
653
0
        }
654
0
        break;
655
0
    }
656
0
    case 401: // auth required
657
0
    case 407: // proxy auth required
658
0
        if (connection->d_func()->handleAuthenticateChallenge(socket, reply, (statusCode == 407), resend)) {
659
0
            if (resend) {
660
0
                if (!resetUploadData())
661
0
                    break;
662
663
0
                reply->d_func()->eraseData();
664
665
0
                if (alreadyPipelinedRequests.isEmpty()) {
666
                    // this does a re-send without closing the connection
667
0
                    resendCurrent = true;
668
0
                    QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
669
0
                } else {
670
                    // we had requests pipelined.. better close the connection in closeAndResendCurrentRequest
671
0
                    closeAndResendCurrentRequest();
672
0
                    QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
673
0
                }
674
0
            } else {
675
                //authentication cancelled, close the channel.
676
0
                close();
677
0
            }
678
0
        } else {
679
0
            emit reply->headerChanged();
680
0
            emit reply->readyRead();
681
0
            QNetworkReply::NetworkError errorCode = (statusCode == 407)
682
0
                ? QNetworkReply::ProxyAuthenticationRequiredError
683
0
                : QNetworkReply::AuthenticationRequiredError;
684
0
            reply->d_func()->errorString = connection->d_func()->errorDetail(errorCode, socket);
685
0
            emit reply->finishedWithError(errorCode, reply->d_func()->errorString);
686
0
        }
687
0
        break;
688
0
    default:
689
0
        if (qobject_cast<QHttpNetworkConnection*>(connection))
690
0
            QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
691
0
    }
692
0
}
693
694
bool QHttpNetworkConnectionChannel::resetUploadData()
695
0
{
696
0
    if (!reply) {
697
        //this happens if server closes connection while QHttpNetworkConnectionPrivate::_q_startNextRequest is pending
698
0
        return false;
699
0
    }
700
0
    if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct
701
0
        || switchedToHttp2) {
702
        // The else branch doesn't make any sense for HTTP/2, since 1 channel is multiplexed into
703
        // many streams. And having one stream fail to reset upload data should not completely close
704
        // the channel. Handled in the http2 protocol handler.
705
0
    } else if (QNonContiguousByteDevice *uploadByteDevice = request.uploadByteDevice()) {
706
0
        if (!uploadByteDevice->reset()) {
707
0
            connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ContentReSendError);
708
0
            return false;
709
0
        }
710
0
        written = 0;
711
0
    }
712
0
    return true;
713
0
}
714
715
#ifndef QT_NO_NETWORKPROXY
716
717
void QHttpNetworkConnectionChannel::setProxy(const QNetworkProxy &networkProxy)
718
0
{
719
0
    if (auto *s = qobject_cast<QAbstractSocket *>(socket))
720
0
        s->setProxy(networkProxy);
721
722
0
    proxy = networkProxy;
723
0
}
724
725
#endif
726
727
#ifndef QT_NO_SSL
728
729
void QHttpNetworkConnectionChannel::ignoreSslErrors()
730
0
{
731
    // AXIVION DISABLE Qt-Security-QSslSocketIgnoreSslErrors: intentional behavior
732
0
    if (socket)
733
0
        static_cast<QSslSocket *>(socket)->ignoreSslErrors();
734
    // AXIVION ENABLE Qt-Security-QSslSocketIgnoreSslErrors
735
736
0
    ignoreAllSslErrors = true;
737
0
}
738
739
740
void QHttpNetworkConnectionChannel::ignoreSslErrors(const QList<QSslError> &errors)
741
0
{
742
    // AXIVION DISABLE Qt-Security-QSslSocketIgnoreSslErrors: intentional behavior
743
0
    if (socket)
744
0
        static_cast<QSslSocket *>(socket)->ignoreSslErrors(errors);
745
    // AXIVION ENABLE Qt-Security-QSslSocketIgnoreSslErrors
746
747
0
    ignoreSslErrorsList = errors;
748
0
}
749
750
void QHttpNetworkConnectionChannel::setSslConfiguration(const QSslConfiguration &config)
751
0
{
752
0
    if (socket)
753
0
        static_cast<QSslSocket *>(socket)->setSslConfiguration(config);
754
755
0
    if (sslConfiguration)
756
0
        *sslConfiguration = config;
757
0
    else
758
0
        sslConfiguration = QSslConfiguration(config);
759
0
}
760
761
#endif
762
763
void QHttpNetworkConnectionChannel::pipelineInto(HttpMessagePair &pair)
764
0
{
765
    // this is only called for simple GET
766
767
0
    QHttpNetworkRequest &request = pair.first;
768
0
    QHttpNetworkReply *reply = pair.second;
769
0
    reply->d_func()->clear();
770
0
    reply->d_func()->connection = connection;
771
0
    reply->d_func()->connectionChannel = this;
772
0
    reply->d_func()->autoDecompress = request.d->autoDecompress;
773
0
    reply->d_func()->pipeliningUsed = true;
774
775
0
#ifndef QT_NO_NETWORKPROXY
776
0
    pipeline.append(QHttpNetworkRequestPrivate::header(request,
777
0
                                                           (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy)));
778
#else
779
    pipeline.append(QHttpNetworkRequestPrivate::header(request, false));
780
#endif
781
782
0
    alreadyPipelinedRequests.append(pair);
783
784
    // pipelineFlush() needs to be called at some point afterwards
785
0
}
786
787
void QHttpNetworkConnectionChannel::pipelineFlush()
788
0
{
789
0
    if (pipeline.isEmpty())
790
0
        return;
791
792
    // The goal of this is so that we have everything in one TCP packet.
793
    // For the Unbuffered QTcpSocket this is manually needed, the buffered
794
    // QTcpSocket does it automatically.
795
    // Also, sometimes the OS does it for us (Nagle's algorithm) but that
796
    // happens only sometimes.
797
0
    socket->write(pipeline);
798
0
    pipeline.clear();
799
0
}
800
801
802
void QHttpNetworkConnectionChannel::closeAndResendCurrentRequest()
803
0
{
804
0
    requeueCurrentlyPipelinedRequests();
805
0
    close();
806
0
    if (reply)
807
0
        resendCurrent = true;
808
0
    if (qobject_cast<QHttpNetworkConnection*>(connection))
809
0
        QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
810
0
}
811
812
void QHttpNetworkConnectionChannel::resendCurrentRequest()
813
0
{
814
0
    requeueCurrentlyPipelinedRequests();
815
0
    if (reply)
816
0
        resendCurrent = true;
817
0
    if (qobject_cast<QHttpNetworkConnection*>(connection))
818
0
        QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
819
0
}
820
821
bool QHttpNetworkConnectionChannel::isSocketBusy() const
822
0
{
823
0
    return (state & QHttpNetworkConnectionChannel::BusyState);
824
0
}
825
826
bool QHttpNetworkConnectionChannel::isSocketWriting() const
827
0
{
828
0
    return (state & QHttpNetworkConnectionChannel::WritingState);
829
0
}
830
831
bool QHttpNetworkConnectionChannel::isSocketWaiting() const
832
0
{
833
0
    return (state & QHttpNetworkConnectionChannel::WaitingState);
834
0
}
835
836
bool QHttpNetworkConnectionChannel::isSocketReading() const
837
0
{
838
0
    return (state & QHttpNetworkConnectionChannel::ReadingState);
839
0
}
840
841
QHttp2ProtocolHandler *QHttpNetworkConnectionChannel::h2ProtocolHandler() const noexcept
842
0
{
843
0
    if (!protocolHandler)
844
0
        return nullptr;
845
0
    const auto type = connection->connectionType();
846
0
    if (type == QHttpNetworkConnection::ConnectionTypeHTTP2Direct
847
0
        || (type == QHttpNetworkConnection::ConnectionTypeHTTP2 && switchedToHttp2)) {
848
0
        return static_cast<QHttp2ProtocolHandler *>(protocolHandler.get());
849
0
    }
850
0
    return nullptr;
851
0
}
852
853
void QHttpNetworkConnectionChannel::_q_bytesWritten(qint64 bytes)
854
0
{
855
0
    Q_UNUSED(bytes);
856
0
    if (ssl) {
857
        // In the SSL case we want to send data from encryptedBytesWritten signal since that one
858
        // is the one going down to the actual network, not only into some SSL buffer.
859
0
        return;
860
0
    }
861
862
    // bytes have been written to the socket. write even more of them :)
863
0
    if (isSocketWriting())
864
0
        sendRequest();
865
    // otherwise we do nothing
866
0
}
867
868
void QHttpNetworkConnectionChannel::_q_disconnected()
869
0
{
870
0
    if (state == QHttpNetworkConnectionChannel::ClosingState) {
871
0
        state = QHttpNetworkConnectionChannel::IdleState;
872
0
        QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
873
0
        return;
874
0
    }
875
876
    // read the available data before closing (also done in _q_error for other codepaths)
877
0
    if ((isSocketWaiting() || isSocketReading()) && socket->bytesAvailable()) {
878
0
        if (reply) {
879
0
            state = QHttpNetworkConnectionChannel::ReadingState;
880
0
            _q_receiveReply();
881
0
        }
882
0
    } else if (reply && reply->contentLength() == -1 && !reply->d_func()->isChunked()) {
883
        // There was no content-length header and it's not chunked encoding,
884
        // so this is a valid way to have the connection closed by the server
885
0
        _q_receiveReply();
886
0
    } else if (state == QHttpNetworkConnectionChannel::IdleState && resendCurrent) {
887
        // re-sending request because the socket was in ClosingState
888
0
        QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
889
0
    }
890
0
    state = QHttpNetworkConnectionChannel::IdleState;
891
0
    if (alreadyPipelinedRequests.size()) {
892
        // If nothing was in a pipeline, no need in calling
893
        // _q_startNextRequest (which it does):
894
0
        requeueCurrentlyPipelinedRequests();
895
0
    }
896
897
0
    pendingEncrypt = false;
898
0
}
899
900
901
void QHttpNetworkConnectionChannel::_q_connected_abstract_socket(QAbstractSocket *absSocket)
902
0
{
903
    // For the Happy Eyeballs we need to check if this is the first channel to connect.
904
0
    if (connection->d_func()->networkLayerState == QHttpNetworkConnectionPrivate::HostLookupPending || connection->d_func()->networkLayerState == QHttpNetworkConnectionPrivate::IPv4or6) {
905
0
        if (connection->d_func()->delayedConnectionTimer.isActive())
906
0
            connection->d_func()->delayedConnectionTimer.stop();
907
0
        if (networkLayerPreference == QAbstractSocket::IPv4Protocol)
908
0
            connection->d_func()->networkLayerState = QHttpNetworkConnectionPrivate::IPv4;
909
0
        else if (networkLayerPreference == QAbstractSocket::IPv6Protocol)
910
0
            connection->d_func()->networkLayerState = QHttpNetworkConnectionPrivate::IPv6;
911
0
        else {
912
0
            if (absSocket->peerAddress().protocol() == QAbstractSocket::IPv4Protocol)
913
0
                connection->d_func()->networkLayerState = QHttpNetworkConnectionPrivate::IPv4;
914
0
            else
915
0
                connection->d_func()->networkLayerState = QHttpNetworkConnectionPrivate::IPv6;
916
0
        }
917
0
        connection->d_func()->networkLayerDetected(networkLayerPreference);
918
0
        if (connection->d_func()->activeChannelCount > 1 && !connection->d_func()->encrypt)
919
0
            QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
920
0
    } else {
921
0
        bool anyProtocol = networkLayerPreference == QAbstractSocket::AnyIPProtocol;
922
0
        if (((connection->d_func()->networkLayerState == QHttpNetworkConnectionPrivate::IPv4)
923
0
             && (networkLayerPreference != QAbstractSocket::IPv4Protocol && !anyProtocol))
924
0
            || ((connection->d_func()->networkLayerState == QHttpNetworkConnectionPrivate::IPv6)
925
0
                && (networkLayerPreference != QAbstractSocket::IPv6Protocol && !anyProtocol))) {
926
0
            close();
927
            // This is the second connection so it has to be closed and we can schedule it for another request.
928
0
            QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
929
0
            return;
930
0
        }
931
        //The connections networkLayerState had already been decided.
932
0
    }
933
934
    // improve performance since we get the request sent by the kernel ASAP
935
    //absSocket->setSocketOption(QAbstractSocket::LowDelayOption, 1);
936
    // We have this commented out now. It did not have the effect we wanted. If we want to
937
    // do this properly, Qt has to combine multiple HTTP requests into one buffer
938
    // and send this to the kernel in one syscall and then the kernel immediately sends
939
    // it as one TCP packet because of TCP_NODELAY.
940
    // However, this code is currently not in Qt, so we rely on the kernel combining
941
    // the requests into one TCP packet.
942
943
    // not sure yet if it helps, but it makes sense
944
0
    absSocket->setSocketOption(QAbstractSocket::KeepAliveOption, 1);
945
946
0
    QTcpKeepAliveConfiguration keepAliveConfig = connection->tcpKeepAliveParameters();
947
948
0
    auto getKeepAliveValue = [](int configValue,
949
0
                                const char* envName,
950
0
                                int defaultValue) {
951
0
        if (configValue > 0)
952
0
            return configValue;
953
0
        return static_cast<int>(qEnvironmentVariableIntegerValue(envName).value_or(defaultValue));
954
0
    };
955
956
0
    int kaIdleOption = getKeepAliveValue(keepAliveConfig.idleTimeBeforeProbes.count(), keepAliveIdleOption, TCP_KEEPIDLE_DEF);
957
0
    int kaIntervalOption = getKeepAliveValue(keepAliveConfig.intervalBetweenProbes.count(), keepAliveIntervalOption, TCP_KEEPINTVL_DEF);
958
0
    int kaCountOption = getKeepAliveValue(keepAliveConfig.probeCount, keepAliveCountOption, TCP_KEEPCNT_DEF);
959
0
    absSocket->setSocketOption(QAbstractSocket::KeepAliveIdleOption, kaIdleOption);
960
0
    absSocket->setSocketOption(QAbstractSocket::KeepAliveIntervalOption, kaIntervalOption);
961
0
    absSocket->setSocketOption(QAbstractSocket::KeepAliveCountOption, kaCountOption);
962
963
0
    pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningSupportUnknown;
964
965
    // ### FIXME: if the server closes the connection unexpectedly, we shouldn't send the same broken request again!
966
    //channels[i].reconnectAttempts = 2;
967
0
    if (ssl || pendingEncrypt) { // FIXME: Didn't work properly with pendingEncrypt only, we should refactor this into an EncrypingState
968
0
#ifndef QT_NO_SSL
969
0
        if (!connection->sslContext()) {
970
            // this socket is making the 1st handshake for this connection,
971
            // we need to set the SSL context so new sockets can reuse it
972
0
            if (auto socketSslContext = QSslSocketPrivate::sslContext(static_cast<QSslSocket*>(absSocket)))
973
0
                connection->setSslContext(std::move(socketSslContext));
974
0
        }
975
0
#endif
976
0
    } else if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
977
0
        state = QHttpNetworkConnectionChannel::IdleState;
978
0
        protocolHandler.reset(new QHttp2ProtocolHandler(this));
979
0
        if (h2RequestsToSend.size() > 0) {
980
            // In case our peer has sent us its settings (window size, max concurrent streams etc.)
981
            // let's give _q_receiveReply a chance to read them first ('invokeMethod', QueuedConnection).
982
0
            QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
983
0
        }
984
0
    } else {
985
0
        state = QHttpNetworkConnectionChannel::IdleState;
986
0
        const bool tryProtocolUpgrade = connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2;
987
0
        if (tryProtocolUpgrade) {
988
            // For HTTP/1.1 it's already created and never reset.
989
0
            protocolHandler.reset(new QHttpProtocolHandler(this));
990
0
        }
991
0
        switchedToHttp2 = false;
992
993
0
        if (!reply)
994
0
            connection->d_func()->dequeueRequest(absSocket);
995
996
0
        if (reply) {
997
0
            if (tryProtocolUpgrade) {
998
                // Let's augment our request with some magic headers and try to
999
                // switch to HTTP/2.
1000
0
                Http2::appendProtocolUpgradeHeaders(connection->http2Parameters(), &request);
1001
0
            }
1002
0
            sendRequest();
1003
0
        }
1004
0
    }
1005
0
}
1006
1007
#if QT_CONFIG(localserver)
1008
void QHttpNetworkConnectionChannel::_q_connected_local_socket(QLocalSocket *localSocket)
1009
0
{
1010
0
    state = QHttpNetworkConnectionChannel::IdleState;
1011
0
    if (!reply) // No reply object, try to dequeue a request (which is paired with a reply):
1012
0
        connection->d_func()->dequeueRequest(localSocket);
1013
0
    if (reply)
1014
0
        sendRequest();
1015
0
}
1016
#endif
1017
1018
void QHttpNetworkConnectionChannel::_q_connected()
1019
0
{
1020
0
    if (auto *s = qobject_cast<QAbstractSocket *>(socket))
1021
0
        _q_connected_abstract_socket(s);
1022
0
#if QT_CONFIG(localserver)
1023
0
    else if (auto *s = qobject_cast<QLocalSocket *>(socket))
1024
0
        _q_connected_local_socket(s);
1025
0
#endif
1026
0
}
1027
1028
void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socketError)
1029
0
{
1030
0
    if (!socket)
1031
0
        return;
1032
0
    QNetworkReply::NetworkError errorCode = QNetworkReply::UnknownNetworkError;
1033
1034
0
    switch (socketError) {
1035
0
    case QAbstractSocket::HostNotFoundError:
1036
0
        errorCode = QNetworkReply::HostNotFoundError;
1037
0
        break;
1038
0
    case QAbstractSocket::ConnectionRefusedError:
1039
0
        errorCode = QNetworkReply::ConnectionRefusedError;
1040
0
#ifndef QT_NO_NETWORKPROXY
1041
0
        if (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy && !ssl)
1042
0
            errorCode = QNetworkReply::ProxyConnectionRefusedError;
1043
0
#endif
1044
0
        break;
1045
0
    case QAbstractSocket::RemoteHostClosedError:
1046
        // This error for SSL comes twice in a row, first from SSL layer ("The TLS/SSL connection has been closed") then from TCP layer.
1047
        // Depending on timing it can also come three times in a row (first time when we try to write into a closing QSslSocket).
1048
        // The reconnectAttempts handling catches the cases where we can re-send the request.
1049
0
        if (!reply && state == QHttpNetworkConnectionChannel::IdleState) {
1050
            // Not actually an error, it is normal for Keep-Alive connections to close after some time if no request
1051
            // is sent on them. No need to error the other replies below. Just bail out here.
1052
            // The _q_disconnected will handle the possibly pipelined replies. HTTP/2 is special for now,
1053
            // we do not resend, but must report errors if any request is in progress (note, while
1054
            // not in its sendRequest(), protocol handler switches the channel to IdleState, thus
1055
            // this check is under this condition in 'if'):
1056
0
            if (auto *h2Handler = h2ProtocolHandler())
1057
0
                h2Handler->handleConnectionClosure();
1058
0
            return;
1059
0
        } else if (state != QHttpNetworkConnectionChannel::IdleState && state != QHttpNetworkConnectionChannel::ReadingState) {
1060
            // Try to reconnect/resend before sending an error.
1061
            // While "Reading" the _q_disconnected() will handle this.
1062
            // If we're using ssl then the protocolHandler is not initialized until
1063
            // "encrypted" has been emitted, since retrying requires the protocolHandler (asserted)
1064
            // we will not try if encryption is not done.
1065
0
            if (!pendingEncrypt && reconnectAttempts-- > 0) {
1066
0
                resendCurrentRequest();
1067
0
                return;
1068
0
            } else {
1069
0
                errorCode = QNetworkReply::RemoteHostClosedError;
1070
0
            }
1071
0
        } else if (state == QHttpNetworkConnectionChannel::ReadingState) {
1072
0
            if (!reply)
1073
0
                break;
1074
1075
0
            if (!reply->d_func()->expectContent()) {
1076
                // No content expected, this is a valid way to have the connection closed by the server
1077
                // We need to invoke this asynchronously to make sure the state() of the socket is on QAbstractSocket::UnconnectedState
1078
0
                QMetaObject::invokeMethod(this, "_q_receiveReply", Qt::QueuedConnection);
1079
0
                return;
1080
0
            }
1081
0
            if (reply->contentLength() == -1 && !reply->d_func()->isChunked()) {
1082
                // There was no content-length header and it's not chunked encoding,
1083
                // so this is a valid way to have the connection closed by the server
1084
                // We need to invoke this asynchronously to make sure the state() of the socket is on QAbstractSocket::UnconnectedState
1085
0
                QMetaObject::invokeMethod(this, "_q_receiveReply", Qt::QueuedConnection);
1086
0
                return;
1087
0
            }
1088
            // ok, we got a disconnect even though we did not expect it
1089
            // Try to read everything from the socket before we emit the error.
1090
0
            if (socket->bytesAvailable()) {
1091
                // Read everything from the socket into the reply buffer.
1092
                // we can ignore the readbuffersize as the data is already
1093
                // in memory and we will not receive more data on the socket.
1094
0
                reply->setReadBufferSize(0);
1095
0
                reply->setDownstreamLimited(false);
1096
0
                _q_receiveReply();
1097
0
                if (!reply) {
1098
                    // No more reply assigned after the previous call? Then it had been finished successfully.
1099
0
                    requeueCurrentlyPipelinedRequests();
1100
0
                    state = QHttpNetworkConnectionChannel::IdleState;
1101
0
                    QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
1102
0
                    return;
1103
0
                }
1104
0
            }
1105
1106
0
            errorCode = QNetworkReply::RemoteHostClosedError;
1107
0
        } else {
1108
0
            errorCode = QNetworkReply::RemoteHostClosedError;
1109
0
        }
1110
0
        break;
1111
0
    case QAbstractSocket::SocketTimeoutError:
1112
        // try to reconnect/resend before sending an error.
1113
0
        if (state == QHttpNetworkConnectionChannel::WritingState && (reconnectAttempts-- > 0)) {
1114
0
            resendCurrentRequest();
1115
0
            return;
1116
0
        }
1117
0
        errorCode = QNetworkReply::TimeoutError;
1118
0
        break;
1119
0
    case QAbstractSocket::ProxyConnectionRefusedError:
1120
0
        errorCode = QNetworkReply::ProxyConnectionRefusedError;
1121
0
        break;
1122
0
    case QAbstractSocket::ProxyAuthenticationRequiredError:
1123
0
        errorCode = QNetworkReply::ProxyAuthenticationRequiredError;
1124
0
        break;
1125
0
    case QAbstractSocket::SslHandshakeFailedError:
1126
0
        errorCode = QNetworkReply::SslHandshakeFailedError;
1127
0
        break;
1128
0
    case QAbstractSocket::ProxyConnectionClosedError:
1129
        // try to reconnect/resend before sending an error.
1130
0
        if (reconnectAttempts-- > 0) {
1131
0
            resendCurrentRequest();
1132
0
            return;
1133
0
        }
1134
0
        errorCode = QNetworkReply::ProxyConnectionClosedError;
1135
0
        break;
1136
0
    case QAbstractSocket::ProxyConnectionTimeoutError:
1137
        // try to reconnect/resend before sending an error.
1138
0
        if (reconnectAttempts-- > 0) {
1139
0
            resendCurrentRequest();
1140
0
            return;
1141
0
        }
1142
0
        errorCode = QNetworkReply::ProxyTimeoutError;
1143
0
        break;
1144
0
    default:
1145
        // all other errors are treated as NetworkError
1146
0
        errorCode = QNetworkReply::UnknownNetworkError;
1147
0
        break;
1148
0
    }
1149
0
    QPointer<QHttpNetworkConnection> that = connection;
1150
0
    QString errorString = connection->d_func()->errorDetail(errorCode, socket, socket->errorString());
1151
1152
    // In the HostLookupPending state the channel should not emit the error.
1153
    // This will instead be handled by the connection.
1154
0
    if (!connection->d_func()->shouldEmitChannelError(socket))
1155
0
        return;
1156
1157
    // emit error for all waiting replies
1158
0
    do {
1159
        // First requeue the already pipelined requests for the current failed reply,
1160
        // then dequeue pending requests so we can also mark them as finished with error
1161
0
        if (reply)
1162
0
            requeueCurrentlyPipelinedRequests();
1163
0
        else
1164
0
            connection->d_func()->dequeueRequest(socket);
1165
1166
0
        if (reply) {
1167
0
            reply->d_func()->errorString = errorString;
1168
0
            reply->d_func()->httpErrorCode = errorCode;
1169
0
            emit reply->finishedWithError(errorCode, errorString);
1170
0
            reply = nullptr;
1171
0
            if (protocolHandler)
1172
0
                protocolHandler->setReply(nullptr);
1173
0
        }
1174
0
    } while (!connection->d_func()->highPriorityQueue.isEmpty()
1175
0
             || !connection->d_func()->lowPriorityQueue.isEmpty());
1176
1177
0
    if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2
1178
0
        || connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
1179
0
        const auto h2RequestsToSendCopy = std::exchange(h2RequestsToSend, {});
1180
0
        for (const auto &httpMessagePair : h2RequestsToSendCopy) {
1181
            // emit error for all replies
1182
0
            QHttpNetworkReply *currentReply = httpMessagePair.second;
1183
0
            currentReply->d_func()->errorString = errorString;
1184
0
            currentReply->d_func()->httpErrorCode = errorCode;
1185
0
            Q_ASSERT(currentReply);
1186
0
            emit currentReply->finishedWithError(errorCode, errorString);
1187
0
        }
1188
0
    }
1189
1190
    // send the next request
1191
0
    QMetaObject::invokeMethod(that, "_q_startNextRequest", Qt::QueuedConnection);
1192
1193
0
    if (that) {
1194
        //signal emission triggered event loop
1195
0
        if (!socket)
1196
0
            state = QHttpNetworkConnectionChannel::IdleState;
1197
0
        else if (QSocketAbstraction::socketState(socket) == QAbstractSocket::UnconnectedState)
1198
0
            state = QHttpNetworkConnectionChannel::IdleState;
1199
0
        else
1200
0
            state = QHttpNetworkConnectionChannel::ClosingState;
1201
1202
        // pendingEncrypt must only be true in between connected and encrypted states
1203
0
        pendingEncrypt = false;
1204
0
    }
1205
0
}
1206
1207
#ifndef QT_NO_NETWORKPROXY
1208
void QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator* auth)
1209
0
{
1210
0
    if ((connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2
1211
0
         && (switchedToHttp2 || h2RequestsToSend.size() > 0))
1212
0
        || connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
1213
0
        if (h2RequestsToSend.size() > 0)
1214
0
            connection->d_func()->emitProxyAuthenticationRequired(this, proxy, auth);
1215
0
    } else { // HTTP
1216
        // Need to dequeue the request before we can emit the error.
1217
0
        if (!reply)
1218
0
            connection->d_func()->dequeueRequest(socket);
1219
0
        if (reply)
1220
0
            connection->d_func()->emitProxyAuthenticationRequired(this, proxy, auth);
1221
0
    }
1222
0
}
1223
#endif
1224
1225
void QHttpNetworkConnectionChannel::_q_uploadDataReadyRead()
1226
0
{
1227
0
    if (reply)
1228
0
        sendRequest();
1229
0
}
1230
1231
void QHttpNetworkConnectionChannel::emitFinishedWithError(QNetworkReply::NetworkError error,
1232
                                                          const char *message)
1233
0
{
1234
0
    if (reply)
1235
0
        emit reply->finishedWithError(error, QHttpNetworkConnectionChannel::tr(message));
1236
0
    const auto h2RequestsToSendCopy = h2RequestsToSend;
1237
0
    for (const auto &httpMessagePair : h2RequestsToSendCopy) {
1238
0
        QHttpNetworkReply *currentReply = httpMessagePair.second;
1239
0
        Q_ASSERT(currentReply);
1240
0
        emit currentReply->finishedWithError(error, QHttpNetworkConnectionChannel::tr(message));
1241
0
    }
1242
0
}
1243
1244
#ifndef QT_NO_SSL
1245
void QHttpNetworkConnectionChannel::_q_encrypted()
1246
0
{
1247
0
    QSslSocket *sslSocket = qobject_cast<QSslSocket *>(socket);
1248
0
    Q_ASSERT(sslSocket);
1249
1250
0
    if (!protocolHandler && connection->connectionType() != QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
1251
        // ConnectionTypeHTTP2Direct does not rely on ALPN/NPN to negotiate HTTP/2,
1252
        // after establishing a secure connection we immediately start sending
1253
        // HTTP/2 frames.
1254
0
        switch (sslSocket->sslConfiguration().nextProtocolNegotiationStatus()) {
1255
0
        case QSslConfiguration::NextProtocolNegotiationNegotiated: {
1256
0
            QByteArray nextProtocol = sslSocket->sslConfiguration().nextNegotiatedProtocol();
1257
0
            if (nextProtocol == QSslConfiguration::NextProtocolHttp1_1) {
1258
                // fall through to create a QHttpProtocolHandler
1259
0
            } else if (nextProtocol == QSslConfiguration::ALPNProtocolHTTP2) {
1260
0
                switchedToHttp2 = true;
1261
0
                protocolHandler.reset(new QHttp2ProtocolHandler(this));
1262
0
                connection->setConnectionType(QHttpNetworkConnection::ConnectionTypeHTTP2);
1263
0
                break;
1264
0
            } else {
1265
0
                emitFinishedWithError(QNetworkReply::SslHandshakeFailedError,
1266
0
                                      "detected unknown Next Protocol Negotiation protocol");
1267
0
                break;
1268
0
            }
1269
0
        }
1270
0
            Q_FALLTHROUGH();
1271
0
        case QSslConfiguration::NextProtocolNegotiationUnsupported: // No agreement, try HTTP/1(.1)
1272
0
        case QSslConfiguration::NextProtocolNegotiationNone: {
1273
0
            protocolHandler.reset(new QHttpProtocolHandler(this));
1274
1275
0
            QSslConfiguration newConfiguration = sslSocket->sslConfiguration();
1276
0
            QList<QByteArray> protocols = newConfiguration.allowedNextProtocols();
1277
0
            const int nProtocols = protocols.size();
1278
            // Clear the protocol that we failed to negotiate, so we do not try
1279
            // it again on other channels that our connection can create/open.
1280
0
            if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2)
1281
0
                protocols.removeAll(QSslConfiguration::ALPNProtocolHTTP2);
1282
1283
0
            if (nProtocols > protocols.size()) {
1284
0
                newConfiguration.setAllowedNextProtocols(protocols);
1285
0
                const int channelCount = connection->d_func()->channelCount;
1286
0
                for (int i = 0; i < channelCount; ++i)
1287
0
                    connection->d_func()->channels[i].setSslConfiguration(newConfiguration);
1288
0
            }
1289
1290
0
            connection->setConnectionType(QHttpNetworkConnection::ConnectionTypeHTTP);
1291
            // We use only one channel for HTTP/2, but normally six for
1292
            // HTTP/1.1 - let's restore this number to the reserved number of
1293
            // channels:
1294
0
            if (connection->d_func()->activeChannelCount < connection->d_func()->channelCount) {
1295
0
                connection->d_func()->activeChannelCount = connection->d_func()->channelCount;
1296
                // re-queue requests from HTTP/2 queue to HTTP queue, if any
1297
0
                requeueHttp2Requests();
1298
0
            }
1299
0
            break;
1300
0
        }
1301
0
        default:
1302
0
            emitFinishedWithError(QNetworkReply::SslHandshakeFailedError,
1303
0
                                  "detected unknown Next Protocol Negotiation protocol");
1304
0
        }
1305
0
    } else if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2
1306
0
               || connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
1307
        // We have to reset QHttp2ProtocolHandler's state machine, it's a new
1308
        // connection and the handler's state is unique per connection.
1309
0
        protocolHandler.reset(new QHttp2ProtocolHandler(this));
1310
0
    }
1311
1312
0
    if (!socket)
1313
0
        return; // ### error
1314
0
    state = QHttpNetworkConnectionChannel::IdleState;
1315
0
    pendingEncrypt = false;
1316
1317
0
    if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2 ||
1318
0
        connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
1319
0
        if (!h2RequestsToSend.isEmpty()) {
1320
            // Similar to HTTP/1.1 counterpart below:
1321
0
            const auto &pair = std::as_const(h2RequestsToSend).first();
1322
0
            waitingForPotentialAbort = true;
1323
0
            emit pair.second->encrypted();
1324
1325
            // We don't send or handle any received data until any effects from
1326
            // emitting encrypted() have been processed. This is necessary
1327
            // because the user may have called abort(). We may also abort the
1328
            // whole connection if the request has been aborted and there is
1329
            // no more requests to send.
1330
0
            QMetaObject::invokeMethod(this,
1331
0
                                      &QHttpNetworkConnectionChannel::checkAndResumeCommunication,
1332
0
                                      Qt::QueuedConnection);
1333
1334
            // In case our peer has sent us its settings (window size, max concurrent streams etc.)
1335
            // let's give _q_receiveReply a chance to read them first ('invokeMethod', QueuedConnection).
1336
0
        }
1337
0
    } else { // HTTP
1338
0
        if (!reply)
1339
0
            connection->d_func()->dequeueRequest(socket);
1340
0
        if (reply) {
1341
0
            reply->setHttp2WasUsed(false);
1342
0
            Q_ASSERT(reply->d_func()->connectionChannel == this);
1343
0
            emit reply->encrypted();
1344
0
        }
1345
0
        if (reply)
1346
0
            sendRequestDelayed();
1347
0
    }
1348
0
    QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
1349
0
}
1350
1351
1352
void QHttpNetworkConnectionChannel::checkAndResumeCommunication()
1353
0
{
1354
0
    Q_ASSERT(connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2
1355
0
             || connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct);
1356
1357
    // Because HTTP/2 requires that we send a SETTINGS frame as the first thing we do, and respond
1358
    // to a SETTINGS frame with an ACK, we need to delay any handling until we can ensure that any
1359
    // effects from emitting encrypted() have been processed.
1360
    // This function is called after encrypted() was emitted, so check for changes.
1361
1362
0
    if (!reply && h2RequestsToSend.isEmpty())
1363
0
        abort();
1364
0
    waitingForPotentialAbort = false;
1365
0
    if (needInvokeReadyRead)
1366
0
        _q_readyRead();
1367
0
    if (needInvokeReceiveReply)
1368
0
        _q_receiveReply();
1369
0
    if (needInvokeSendRequest)
1370
0
        sendRequest();
1371
0
}
1372
1373
void QHttpNetworkConnectionChannel::requeueHttp2Requests()
1374
0
{
1375
0
    const auto h2RequestsToSendCopy = std::exchange(h2RequestsToSend, {});
1376
0
    for (const auto &httpMessagePair : h2RequestsToSendCopy)
1377
0
        connection->d_func()->requeueRequest(httpMessagePair);
1378
0
}
1379
1380
void QHttpNetworkConnectionChannel::_q_sslErrors(const QList<QSslError> &errors)
1381
0
{
1382
0
    if (!socket)
1383
0
        return;
1384
    //QNetworkReply::NetworkError errorCode = QNetworkReply::ProtocolFailure;
1385
    // Also pause the connection because socket notifiers may fire while an user
1386
    // dialog is displaying
1387
0
    connection->d_func()->pauseConnection();
1388
0
    if (pendingEncrypt && !reply)
1389
0
        connection->d_func()->dequeueRequest(socket);
1390
0
    if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP) {
1391
0
        if (reply)
1392
0
            emit reply->sslErrors(errors);
1393
0
    }
1394
0
#ifndef QT_NO_SSL
1395
0
    else { // HTTP/2
1396
0
        const auto h2RequestsToSendCopy = h2RequestsToSend;
1397
0
        for (const auto &httpMessagePair : h2RequestsToSendCopy) {
1398
            // emit SSL errors for all replies
1399
0
            QHttpNetworkReply *currentReply = httpMessagePair.second;
1400
0
            Q_ASSERT(currentReply);
1401
0
            emit currentReply->sslErrors(errors);
1402
0
        }
1403
0
    }
1404
0
#endif // QT_NO_SSL
1405
0
    connection->d_func()->resumeConnection();
1406
0
}
1407
1408
void QHttpNetworkConnectionChannel::_q_preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator *authenticator)
1409
0
{
1410
0
    connection->d_func()->pauseConnection();
1411
1412
0
    if (pendingEncrypt && !reply)
1413
0
        connection->d_func()->dequeueRequest(socket);
1414
1415
0
    if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP) {
1416
0
        if (reply)
1417
0
            emit reply->preSharedKeyAuthenticationRequired(authenticator);
1418
0
    } else {
1419
0
        const auto h2RequestsToSendCopy = h2RequestsToSend;
1420
0
        for (const auto &httpMessagePair : h2RequestsToSendCopy) {
1421
            // emit SSL errors for all replies
1422
0
            QHttpNetworkReply *currentReply = httpMessagePair.second;
1423
0
            Q_ASSERT(currentReply);
1424
0
            emit currentReply->preSharedKeyAuthenticationRequired(authenticator);
1425
0
        }
1426
0
    }
1427
1428
0
    connection->d_func()->resumeConnection();
1429
0
}
1430
1431
void QHttpNetworkConnectionChannel::_q_encryptedBytesWritten(qint64 bytes)
1432
0
{
1433
0
    Q_UNUSED(bytes);
1434
    // bytes have been written to the socket. write even more of them :)
1435
0
    if (isSocketWriting())
1436
0
        sendRequest();
1437
    // otherwise we do nothing
1438
0
}
1439
1440
#endif
1441
1442
void QHttpNetworkConnectionChannel::setConnection(QHttpNetworkConnection *c)
1443
0
{
1444
    // Inlining this function in the header leads to compiler error on
1445
    // release-armv5, on at least timebox 9.2 and 10.1.
1446
0
    connection = c;
1447
0
}
1448
1449
QT_END_NAMESPACE
1450
1451
#include "moc_qhttpnetworkconnectionchannel_p.cpp"