Coverage Report

Created: 2026-02-26 07:48

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